Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions drivers/ethernet/phy/phy_microchip_ksz9131.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ struct mchp_ksz9131_data {
#define PHY_KSZ9131_ICS_LINK_DOWN_IE_MASK BIT(10)
#define PHY_KSZ9131_ICS_LINK_UP_IE_MASK BIT(8)

#define PHY_KSZ9131_LPBK_REG 0x1E
#define PHY_KSZ9131_LPBK_LED_ERRATA_BIT BIT(9)

#define PHY_KSZ9131_MMD_CTRL_DEVAD 0x2
#define PHY_KSZ9131_MMD_CTRL_REG 0x0
#define PHY_KSZ9131_MMD_CTRL_LED_MODE BIT(4)

#define USING_INTERRUPT_GPIO \
UTIL_OR(DT_ALL_INST_HAS_PROP_STATUS_OKAY(int_gpios), \
UTIL_AND(DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios), \
Expand Down Expand Up @@ -82,6 +89,37 @@ static int ksz9131_write(const struct device *dev, uint16_t reg_addr, uint16_t v
return ret;
}

static int ksz9131_read_mmd(const struct device *dev, uint32_t devad, uint16_t reg_addr,
uint16_t *value)
{
const struct mchp_ksz9131_config *const cfg = dev->config;
int ret = ksz9131_write(dev, MII_MMD_ACR,
(devad & MII_MMD_ACR_DEVAD_MASK) | MII_MMD_ACR_ADDR);
if (ret < 0) {
return ret;
}

ret = ksz9131_write(dev, MII_MMD_AADR, reg_addr);
if (ret < 0) {
return ret;
}

ret = ksz9131_write(dev, MII_MMD_ACR,
(devad & MII_MMD_ACR_DEVAD_MASK) | MII_MMD_ACR_DATA_NO_POS_INC);
if (ret < 0) {
return ret;
}

ret = mdio_read(cfg->mdio, cfg->phy_addr, MII_MMD_AADR, value);
if (ret < 0) {
LOG_ERR("Error reading phy (%d) MMD (%d.%d)", cfg->phy_addr, devad, reg_addr);
}

LOG_DBG("Read 0x%x from phy (%d) MMD (%d.%d)", *value, cfg->phy_addr, devad, reg_addr);

return ret;
}

static int phy_mchp_ksz9131_read(const struct device *dev, uint16_t reg_addr, uint32_t *data)
{
/* Make sure excessive bits 16-31 are reset */
Expand Down Expand Up @@ -539,6 +577,35 @@ static int ksz9131_init_int_gpios(const struct device *dev)
}
#endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(int_gpios) */

static int ksz9131_led_errata(const struct device *dev)
{
int ret;
uint16_t reg_val;

/*
* KSZ9131RNX Silicon Errata: DS80000693B
* When configured for Individual-LED mode, LED1 is ON when KSZ9131RNX has no link.
* Workaround: Set bit 9 of Register 0x1E (Loopback Control Register).
*/

ret = ksz9131_read_mmd(dev, PHY_KSZ9131_MMD_CTRL_DEVAD, PHY_KSZ9131_MMD_CTRL_REG, &reg_val);
if (ret < 0) {
return ret;
}

if (reg_val & PHY_KSZ9131_MMD_CTRL_LED_MODE) {
ret = ksz9131_read(dev, PHY_KSZ9131_LPBK_REG, &reg_val);
if (ret < 0) {
return ret;
}

reg_val |= PHY_KSZ9131_LPBK_LED_ERRATA_BIT;
ret = ksz9131_write(dev, PHY_KSZ9131_LPBK_REG, reg_val);
}

return ret;
}

static int phy_mchp_ksz9131_init(const struct device *dev)
{
const struct mchp_ksz9131_config *const cfg = dev->config;
Expand All @@ -565,6 +632,11 @@ static int phy_mchp_ksz9131_init(const struct device *dev)
return ret;
}

ret = ksz9131_led_errata(dev);
if (ret < 0) {
return ret;
}

k_work_init_delayable(&data->monitor_work, phy_mchp_ksz9131_monitor_work_handler);

phy_mchp_ksz9131_cfg_link(dev, cfg->default_speeds, 0);
Expand Down