drivers: ethernet: phy: add common functions for PHYs

This commit adds common functions for PHYs to the mii driver.

Signed-off-by: Fin Maaß <f.maass@vogl-electronic.com>
This commit is contained in:
Fin Maaß 2025-06-16 12:58:29 +02:00 committed by Henrik Brix Andersen
commit 847be49dbd
7 changed files with 110 additions and 289 deletions

View file

@ -23,6 +23,8 @@
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
#include "phy_mii.h"
#define PHY_MC_KSZ8081_OMSO_REG 0x16
#define PHY_MC_KSZ8081_OMSO_FACTORY_MODE_MASK BIT(15)
#define PHY_MC_KSZ8081_OMSO_NAND_TREE_MASK BIT(5)
@ -339,7 +341,6 @@ static int phy_mc_ksz8081_cfg_link(const struct device *dev,
struct mc_ksz8081_data *data = dev->data;
struct phy_link_state state = {};
int ret;
uint32_t anar;
/* Lock mutex */
ret = k_mutex_lock(&data->mutex, K_FOREVER);
@ -357,39 +358,9 @@ static int phy_mc_ksz8081_cfg_link(const struct device *dev,
goto done;
}
/* Read ANAR register to write back */
ret = phy_mc_ksz8081_read(dev, MII_ANAR, &anar);
if (ret) {
LOG_ERR("Error reading phy (%d) advertising register", config->addr);
goto done;
}
/* Setup advertising register */
if (speeds & LINK_FULL_100BASE) {
anar |= MII_ADVERTISE_100_FULL;
} else {
anar &= ~MII_ADVERTISE_100_FULL;
}
if (speeds & LINK_HALF_100BASE) {
anar |= MII_ADVERTISE_100_HALF;
} else {
anar &= ~MII_ADVERTISE_100_HALF;
}
if (speeds & LINK_FULL_10BASE) {
anar |= MII_ADVERTISE_10_FULL;
} else {
anar &= ~MII_ADVERTISE_10_FULL;
}
if (speeds & LINK_HALF_10BASE) {
anar |= MII_ADVERTISE_10_HALF;
} else {
anar &= ~MII_ADVERTISE_10_HALF;
}
/* Write capabilities to advertising register */
ret = phy_mc_ksz8081_write(dev, MII_ANAR, anar);
if (ret) {
LOG_ERR("Error writing phy (%d) advertising register", config->addr);
ret = phy_mii_set_anar_reg(dev, speeds);
if ((ret < 0) && (ret != -EALREADY)) {
LOG_ERR("Error setting ANAR register for phy (%d)", config->addr);
goto done;
}

View file

@ -18,6 +18,8 @@
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(phy_mii, CONFIG_PHY_LOG_LEVEL);
#include "phy_mii.h"
#define ANY_DYNAMIC_LINK UTIL_NOT(DT_ALL_INST_HAS_PROP_STATUS_OKAY(fixed_link))
#define ANY_FIXED_LINK DT_ANY_INST_HAS_PROP_STATUS_OKAY(fixed_link)
@ -350,11 +352,7 @@ static int phy_mii_cfg_link(const struct device *dev,
{
struct phy_mii_dev_data *const data = dev->data;
const struct phy_mii_dev_config *const cfg = dev->config;
uint16_t anar_reg;
uint16_t anar_reg_old;
uint16_t bmcr_reg;
uint16_t c1kt_reg;
uint16_t c1kt_reg_old;
int ret = 0;
/* if there is no mdio (fixed-link) it is not supported to configure link */
@ -368,79 +366,30 @@ static int phy_mii_cfg_link(const struct device *dev,
k_sem_take(&data->sem, K_FOREVER);
if (phy_mii_reg_read(dev, MII_ANAR, &anar_reg) < 0) {
ret = -EIO;
goto cfg_link_end;
}
anar_reg_old = anar_reg;
if (phy_mii_reg_read(dev, MII_BMCR, &bmcr_reg) < 0) {
ret = -EIO;
goto cfg_link_end;
}
if (data->gigabit_supported) {
if (phy_mii_reg_read(dev, MII_1KTCR, &c1kt_reg) < 0) {
ret = -EIO;
ret = phy_mii_set_c1kt_reg(dev, adv_speeds);
if (ret == -EALREADY) {
/* If the C1KT register is already set, we don't need to do anything */
ret = 0;
} else if (ret < 0) {
goto cfg_link_end;
}
}
if (adv_speeds & LINK_FULL_10BASE) {
anar_reg |= MII_ADVERTISE_10_FULL;
} else {
anar_reg &= ~MII_ADVERTISE_10_FULL;
}
if (adv_speeds & LINK_HALF_10BASE) {
anar_reg |= MII_ADVERTISE_10_HALF;
} else {
anar_reg &= ~MII_ADVERTISE_10_HALF;
}
if (adv_speeds & LINK_FULL_100BASE) {
anar_reg |= MII_ADVERTISE_100_FULL;
} else {
anar_reg &= ~MII_ADVERTISE_100_FULL;
}
if (adv_speeds & LINK_HALF_100BASE) {
anar_reg |= MII_ADVERTISE_100_HALF;
} else {
anar_reg &= ~MII_ADVERTISE_100_HALF;
}
if (data->gigabit_supported) {
c1kt_reg_old = c1kt_reg;
if (adv_speeds & LINK_FULL_1000BASE) {
c1kt_reg |= MII_ADVERTISE_1000_FULL;
} else {
c1kt_reg &= ~MII_ADVERTISE_1000_FULL;
}
if (adv_speeds & LINK_HALF_1000BASE) {
c1kt_reg |= MII_ADVERTISE_1000_HALF;
} else {
c1kt_reg &= ~MII_ADVERTISE_1000_HALF;
}
if (c1kt_reg != c1kt_reg_old) {
if (phy_mii_reg_write(dev, MII_1KTCR, c1kt_reg) < 0) {
ret = -EIO;
goto cfg_link_end;
}
data->restart_autoneg = true;
}
}
if (anar_reg != anar_reg_old) {
if (phy_mii_reg_write(dev, MII_ANAR, anar_reg) < 0) {
ret = -EIO;
goto cfg_link_end;
}
ret = phy_mii_set_anar_reg(dev, adv_speeds);
if (ret == -EALREADY) {
/* If the ANAR register is already set, we don't need to do anything */
ret = 0;
} else if (ret < 0) {
goto cfg_link_end;
} else {
data->restart_autoneg = true;
}

View file

@ -0,0 +1,63 @@
/*
* Copyright The Zephyr Project Contributors
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_PHY_MII_H_
#define ZEPHYR_PHY_MII_H_
#include <zephyr/device.h>
#include <zephyr/net/phy.h>
#include <zephyr/net/mii.h>
static inline int phy_mii_set_anar_reg(const struct device *dev, enum phy_link_speed adv_speeds)
{
uint32_t anar_reg = 0U;
uint32_t anar_reg_old;
if (phy_read(dev, MII_ANAR, &anar_reg) < 0) {
return -EIO;
}
anar_reg_old = anar_reg;
WRITE_BIT(anar_reg, MII_ADVERTISE_10_FULL_BIT, (adv_speeds & LINK_FULL_10BASE) != 0U);
WRITE_BIT(anar_reg, MII_ADVERTISE_10_HALF_BIT, (adv_speeds & LINK_HALF_10BASE) != 0U);
WRITE_BIT(anar_reg, MII_ADVERTISE_100_FULL_BIT, (adv_speeds & LINK_FULL_100BASE) != 0U);
WRITE_BIT(anar_reg, MII_ADVERTISE_100_HALF_BIT, (adv_speeds & LINK_HALF_100BASE) != 0U);
if (anar_reg == anar_reg_old) {
return -EALREADY;
}
if (phy_write(dev, MII_ANAR, anar_reg) < 0) {
return -EIO;
}
return 0;
}
static inline int phy_mii_set_c1kt_reg(const struct device *dev, enum phy_link_speed adv_speeds)
{
uint32_t c1kt_reg = 0U;
uint32_t c1kt_reg_old;
if (phy_read(dev, MII_1KTCR, &c1kt_reg) < 0) {
return -EIO;
}
c1kt_reg_old = c1kt_reg;
WRITE_BIT(c1kt_reg, MII_ADVERTISE_1000_FULL_BIT, (adv_speeds & LINK_FULL_1000BASE) != 0U);
WRITE_BIT(c1kt_reg, MII_ADVERTISE_1000_HALF_BIT, (adv_speeds & LINK_HALF_1000BASE) != 0U);
if (c1kt_reg == c1kt_reg_old) {
return -EALREADY;
}
if (phy_write(dev, MII_1KTCR, c1kt_reg) < 0) {
return -EIO;
}
return 0;
}
#endif /* ZEPHYR_PHY_MII_H_ */

View file

@ -22,6 +22,8 @@
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(phy_qc_ar8031, CONFIG_PHY_LOG_LEVEL);
#include "phy_mii.h"
#define AR8031_PHY_ID1 0x004DU
#define PHY_READID_TIMEOUT_COUNT 1000U
@ -255,68 +257,24 @@ static void monitor_work_handler(struct k_work *work)
static int qc_ar8031_cfg_link(const struct device *dev, enum phy_link_speed adv_speeds)
{
uint32_t anar_reg;
uint32_t bmcr_reg;
uint32_t c1kt_reg;
if (qc_ar8031_read(dev, MII_ANAR, &anar_reg) < 0) {
return -EIO;
}
if (qc_ar8031_read(dev, MII_BMCR, &bmcr_reg) < 0) {
return -EIO;
}
if (qc_ar8031_read(dev, MII_1KTCR, &c1kt_reg) < 0) {
ret = phy_mii_set_anar_reg(dev, speeds);
if ((ret < 0) && (ret != -EALREADY)) {
return -EIO;
}
if (adv_speeds & LINK_FULL_10BASE) {
anar_reg |= MII_ADVERTISE_10_FULL;
} else {
anar_reg &= ~MII_ADVERTISE_10_FULL;
}
if (adv_speeds & LINK_HALF_10BASE) {
anar_reg |= MII_ADVERTISE_10_HALF;
} else {
anar_reg &= ~MII_ADVERTISE_10_HALF;
}
if (adv_speeds & LINK_FULL_100BASE) {
anar_reg |= MII_ADVERTISE_100_FULL;
} else {
anar_reg &= ~MII_ADVERTISE_100_FULL;
}
if (adv_speeds & LINK_HALF_100BASE) {
anar_reg |= MII_ADVERTISE_100_HALF;
} else {
anar_reg &= ~MII_ADVERTISE_100_HALF;
}
if (adv_speeds & LINK_FULL_1000BASE) {
c1kt_reg |= MII_ADVERTISE_1000_FULL;
} else {
c1kt_reg &= ~MII_ADVERTISE_1000_FULL;
}
if (adv_speeds & LINK_HALF_1000BASE) {
c1kt_reg |= MII_ADVERTISE_1000_HALF;
} else {
c1kt_reg &= ~MII_ADVERTISE_1000_HALF;
}
if (qc_ar8031_write(dev, MII_1KTCR, c1kt_reg) < 0) {
ret = phy_mii_set_c1kt_reg(dev, speeds);
if ((ret < 0) && (ret != -EALREADY)) {
return -EIO;
}
bmcr_reg |= MII_BMCR_AUTONEG_ENABLE | MII_BMCR_AUTONEG_RESTART;
if (qc_ar8031_write(dev, MII_ANAR, anar_reg) < 0) {
return -EIO;
}
if (qc_ar8031_write(dev, MII_BMCR, bmcr_reg) < 0) {
return -EIO;
}

View file

@ -25,6 +25,8 @@
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
#include "phy_mii.h"
#define REALTEK_OUI_MSB (0x1CU)
#define PHY_RT_RTL8211F_PHYSR_REG (0x1A)
@ -283,8 +285,6 @@ static int phy_rt_rtl8211f_cfg_link(const struct device *dev,
{
const struct rt_rtl8211f_config *config = dev->config;
struct rt_rtl8211f_data *data = dev->data;
uint32_t anar;
uint32_t gbcr;
int ret;
/* Lock mutex */
@ -303,60 +303,15 @@ static int phy_rt_rtl8211f_cfg_link(const struct device *dev,
k_work_cancel_delayable(&data->phy_monitor_work);
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
/* Read ANAR register to write back */
ret = phy_rt_rtl8211f_read(dev, MII_ANAR, &anar);
if (ret) {
LOG_ERR("Error reading phy (%d) advertising register", config->addr);
ret = phy_mii_set_anar_reg(dev, speeds);
if (ret < 0 && ret != -EALREADY) {
LOG_ERR("Error setting ANAR register for phy (%d)", config->addr);
goto done;
}
/* Read GBCR register to write back */
ret = phy_rt_rtl8211f_read(dev, MII_1KTCR, &gbcr);
if (ret) {
LOG_ERR("Error reading phy (%d) 1000Base-T control register", config->addr);
goto done;
}
/* Setup advertising register */
if (speeds & LINK_FULL_100BASE) {
anar |= MII_ADVERTISE_100_FULL;
} else {
anar &= ~MII_ADVERTISE_100_FULL;
}
if (speeds & LINK_HALF_100BASE) {
anar |= MII_ADVERTISE_100_HALF;
} else {
anar &= ~MII_ADVERTISE_100_HALF;
}
if (speeds & LINK_FULL_10BASE) {
anar |= MII_ADVERTISE_10_FULL;
} else {
anar &= ~MII_ADVERTISE_10_FULL;
}
if (speeds & LINK_HALF_10BASE) {
anar |= MII_ADVERTISE_10_HALF;
} else {
anar &= ~MII_ADVERTISE_10_HALF;
}
/* Setup 1000Base-T control register */
if (speeds & LINK_FULL_1000BASE) {
gbcr |= MII_ADVERTISE_1000_FULL;
} else {
gbcr &= ~MII_ADVERTISE_1000_FULL;
}
/* Write capabilities to advertising register */
ret = phy_rt_rtl8211f_write(dev, MII_ANAR, anar);
if (ret) {
LOG_ERR("Error writing phy (%d) advertising register", config->addr);
goto done;
}
/* Write capabilities to 1000Base-T control register */
ret = phy_rt_rtl8211f_write(dev, MII_1KTCR, gbcr);
if (ret) {
LOG_ERR("Error writing phy (%d) 1000Base-T control register", config->addr);
ret = phy_mii_set_c1kt_reg(dev, speeds);
if (ret < 0 && ret != -EALREADY) {
LOG_ERR("Error setting C1KT register for phy (%d)", config->addr);
goto done;
}

View file

@ -22,6 +22,8 @@
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
#include "phy_mii.h"
#define PHY_TI_DP83825_PHYSCR_REG 0x11
#define PHY_TI_DP83825_PHYSCR_REG_IE BIT(1)
#define PHY_TI_DP83825_PHYSCR_REG_IOE BIT(0)
@ -363,7 +365,6 @@ static int phy_ti_dp83825_cfg_link(const struct device *dev, enum phy_link_speed
const struct ti_dp83825_config *config = dev->config;
struct ti_dp83825_data *data = dev->data;
int ret;
uint32_t anar;
/* Lock mutex */
ret = k_mutex_lock(&data->mutex, K_FOREVER);
@ -393,42 +394,9 @@ static int phy_ti_dp83825_cfg_link(const struct device *dev, enum phy_link_speed
goto done;
}
/* Read ANAR register to write back */
ret = phy_ti_dp83825_read(dev, MII_ANAR, &anar);
if (ret) {
LOG_ERR("Error reading phy (%d) advertising register", config->addr);
goto done;
}
/* Setup advertising register */
if (speeds & LINK_FULL_100BASE) {
anar |= MII_ADVERTISE_100_FULL;
} else {
anar &= ~MII_ADVERTISE_100_FULL;
}
if (speeds & LINK_HALF_100BASE) {
anar |= MII_ADVERTISE_100_HALF;
} else {
anar &= ~MII_ADVERTISE_100_HALF;
}
if (speeds & LINK_FULL_10BASE) {
anar |= MII_ADVERTISE_10_FULL;
} else {
anar &= ~MII_ADVERTISE_10_FULL;
}
if (speeds & LINK_HALF_10BASE) {
anar |= MII_ADVERTISE_10_HALF;
} else {
anar &= ~MII_ADVERTISE_10_HALF;
}
/* Write capabilities to advertising register */
ret = phy_ti_dp83825_write(dev, MII_ANAR, anar);
if (ret) {
LOG_ERR("Error writing phy (%d) advertising register", config->addr);
ret = phy_mii_set_anar_reg(dev, speeds);
if ((ret < 0) && (ret != -EALREADY)) {
LOG_ERR("Error setting ANAR register for phy (%d)", config->addr);
goto done;
}

View file

@ -20,6 +20,8 @@
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(LOG_MODULE_NAME, LOG_LEVEL);
#include "phy_mii.h"
#define PHY_TI_DP83867_PHYSTS 0x11
#define PHY_TI_DP83867_PHYSTS_LINKSTATUS_MASK BIT(10)
#define PHY_TI_DP83867_PHYSTS_LINKDUPLEX_MASK BIT(13)
@ -309,7 +311,7 @@ static int phy_ti_dp83867_cfg_link(const struct device *dev, enum phy_link_speed
const struct ti_dp83867_config *config = dev->config;
struct ti_dp83867_data *data = dev->data;
int ret;
uint32_t anar, cfg1, val;
uint32_t val;
/* Lock mutex */
ret = k_mutex_lock(&data->mutex, K_FOREVER);
@ -356,60 +358,15 @@ static int phy_ti_dp83867_cfg_link(const struct device *dev, enum phy_link_speed
k_work_cancel_delayable(&data->phy_monitor_work);
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */
/* Read ANAR register to write back */
ret = phy_ti_dp83867_read(dev, MII_ANAR, &anar);
if (ret) {
LOG_ERR("Error reading phy (%d) advertising register", config->addr);
ret = phy_mii_set_anar_reg(dev, speeds);
if ((ret < 0) && (ret != -EALREADY)) {
LOG_ERR("Error setting ANAR register for phy (%d)", config->addr);
goto done;
}
/* Read CFG1 register to write back */
ret = phy_ti_dp83867_read(dev, MII_1KTCR, &cfg1);
if (ret) {
LOG_ERR("Error reading phy (%d) 1000Base-T control register", config->addr);
goto done;
}
/* Setup advertising register */
if (speeds & LINK_FULL_100BASE) {
anar |= MII_ADVERTISE_100_FULL;
} else {
anar &= ~MII_ADVERTISE_100_FULL;
}
if (speeds & LINK_HALF_100BASE) {
anar |= MII_ADVERTISE_100_HALF;
} else {
anar &= ~MII_ADVERTISE_100_HALF;
}
if (speeds & LINK_FULL_10BASE) {
anar |= MII_ADVERTISE_10_FULL;
} else {
anar &= ~MII_ADVERTISE_10_FULL;
}
if (speeds & LINK_HALF_10BASE) {
anar |= MII_ADVERTISE_10_HALF;
} else {
anar &= ~MII_ADVERTISE_10_HALF;
}
/* Setup 1000Base-T control register */
if (speeds & LINK_FULL_1000BASE) {
cfg1 |= MII_ADVERTISE_1000_FULL;
} else {
cfg1 &= ~MII_ADVERTISE_1000_FULL;
}
/* Write capabilities to advertising register */
ret = phy_ti_dp83867_write(dev, MII_ANAR, anar);
if (ret) {
LOG_ERR("Error writing phy (%d) advertising register", config->addr);
goto done;
}
/* Write capabilities to 1000Base-T control register */
ret = phy_ti_dp83867_write(dev, MII_1KTCR, cfg1);
if (ret) {
LOG_ERR("Error writing phy (%d) 1000Base-T control register", config->addr);
ret = phy_mii_set_c1kt_reg(dev, speeds);
if ((ret < 0) && (ret != -EALREADY)) {
LOG_ERR("Error setting C1KT register for phy (%d)", config->addr);
goto done;
}