static int __amd_xgbe_phy_config_aneg(struct phy_device *phydev) { struct amd_xgbe_phy_priv *priv = phydev->priv; u32 mmd_mask = phydev->c45_ids.devices_in_package; int ret; if (phydev->autoneg != AUTONEG_ENABLE) return amd_xgbe_phy_setup_forced(phydev); /* Make sure we have the AN MMD present */ if (!(mmd_mask & MDIO_DEVS_AN)) return -EINVAL; /* Disable auto-negotiation interrupt */ disable_irq(priv->an_irq); /* Start auto-negotiation in a supported mode */ if (phydev->supported & SUPPORTED_10000baseKR_Full) ret = amd_xgbe_phy_set_mode(phydev, AMD_XGBE_MODE_KR); else if ((phydev->supported & SUPPORTED_1000baseKX_Full) || (phydev->supported & SUPPORTED_2500baseX_Full)) ret = amd_xgbe_phy_set_mode(phydev, AMD_XGBE_MODE_KX); else ret = -EINVAL; if (ret < 0) { enable_irq(priv->an_irq); return ret; } /* Disable and stop any in progress auto-negotiation */ ret = amd_xgbe_phy_disable_an(phydev); if (ret < 0) return ret; /* Clear any auto-negotitation interrupts */ phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0); priv->an_result = AMD_XGBE_AN_READY; priv->an_state = AMD_XGBE_AN_READY; priv->kr_state = AMD_XGBE_RX_BPA; priv->kx_state = AMD_XGBE_RX_BPA; /* Re-enable auto-negotiation interrupt */ enable_irq(priv->an_irq); /* Set up advertisement registers based on current settings */ ret = amd_xgbe_an_init(phydev); if (ret) return ret; /* Enable and start auto-negotiation */ return amd_xgbe_phy_restart_an(phydev); }
static int amd_xgbe_an_disable_kr_training(struct phy_device *phydev) { int ret; ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL); if (ret < 0) return ret; ret &= ~XGBE_PHY_KR_TRAINING_ENABLE; phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, ret); return 0; }
static int amd_xgbe_phy_resume(struct phy_device *phydev) { struct amd_xgbe_phy_priv *priv = phydev->priv; mutex_lock(&phydev->lock); priv->lpm_ctrl &= ~MDIO_CTRL1_LPOWER; phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, priv->lpm_ctrl); mutex_unlock(&phydev->lock); return 0; }
static int dp83867_config_port_mirroring(struct phy_device *phydev) { struct dp83867_private *dp83867 = (struct dp83867_private *)phydev->priv; u16 val; val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4); if (dp83867->port_mirroring == DP83867_PORT_MIRROING_EN) val |= DP83867_CFG4_PORT_MIRROR_EN; else val &= ~DP83867_CFG4_PORT_MIRROR_EN; phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4, val); return 0; }
static int mv3310_modify(struct phy_device *phydev, int devad, u16 reg, u16 mask, u16 bits) { int old, val, ret; old = phy_read_mmd(phydev, devad, reg); if (old < 0) return old; val = (old & ~mask) | (bits & mask); if (val == old) return 0; ret = phy_write_mmd(phydev, devad, reg, val); return ret < 0 ? ret : 1; }
static int amd_xgbe_phy_set_an(struct phy_device *phydev, bool enable, bool restart) { int ret; ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1); if (ret < 0) return ret; ret &= ~MDIO_AN_CTRL1_ENABLE; if (enable) ret |= MDIO_AN_CTRL1_ENABLE; if (restart) ret |= MDIO_AN_CTRL1_RESTART; phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, ret); return 0; }
static int amd_xgbe_phy_suspend(struct phy_device *phydev) { struct amd_xgbe_phy_priv *priv = phydev->priv; int ret; mutex_lock(&phydev->lock); ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1); if (ret < 0) goto unlock; priv->lpm_ctrl = ret; ret |= MDIO_CTRL1_LPOWER; phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret); ret = 0; unlock: mutex_unlock(&phydev->lock); return ret; }
static int dp83867_config_init(struct phy_device *phydev) { struct dp83867_private *dp83867; int ret, val, bs; u16 delay; if (!phydev->priv) { dp83867 = devm_kzalloc(&phydev->mdio.dev, sizeof(*dp83867), GFP_KERNEL); if (!dp83867) return -ENOMEM; phydev->priv = dp83867; ret = dp83867_of_init(phydev); if (ret) return ret; } else { dp83867 = (struct dp83867_private *)phydev->priv; } /* RX_DV/RX_CTRL strapped in mode 1 or mode 2 workaround */ if (dp83867->rxctrl_strap_quirk) { val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4); val &= ~BIT(7); phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4, val); } if (phy_interface_is_rgmii(phydev)) { val = phy_read(phydev, MII_DP83867_PHYCTRL); if (val < 0) return val; val &= ~DP83867_PHYCR_FIFO_DEPTH_MASK; val |= (dp83867->fifo_depth << DP83867_PHYCR_FIFO_DEPTH_SHIFT); /* The code below checks if "port mirroring" N/A MODE4 has been * enabled during power on bootstrap. * * Such N/A mode enabled by mistake can put PHY IC in some * internal testing mode and disable RGMII transmission. * * In this particular case one needs to check STRAP_STS1 * register's bit 11 (marked as RESERVED). */ bs = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_STRAP_STS1); if (bs & DP83867_STRAP_STS1_RESERVED) val &= ~DP83867_PHYCR_RESERVED_MASK; ret = phy_write(phydev, MII_DP83867_PHYCTRL, val); if (ret) return ret; } if ((phydev->interface >= PHY_INTERFACE_MODE_RGMII_ID) && (phydev->interface <= PHY_INTERFACE_MODE_RGMII_RXID)) { val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIICTL); if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) val |= (DP83867_RGMII_TX_CLK_DELAY_EN | DP83867_RGMII_RX_CLK_DELAY_EN); if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) val |= DP83867_RGMII_TX_CLK_DELAY_EN; if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) val |= DP83867_RGMII_RX_CLK_DELAY_EN; phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIICTL, val); delay = (dp83867->rx_id_delay | (dp83867->tx_id_delay << DP83867_RGMII_TX_CLK_DELAY_SHIFT)); phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIIDCTL, delay); if (dp83867->io_impedance >= 0) { val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_IO_MUX_CFG); val &= ~DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL; val |= dp83867->io_impedance & DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL; phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_IO_MUX_CFG, val); } } /* Enable Interrupt output INT_OE in CFG3 register */ if (phy_interrupt_is_valid(phydev)) { val = phy_read(phydev, DP83867_CFG3); val |= BIT(7); phy_write(phydev, DP83867_CFG3, val); } if (dp83867->port_mirroring != DP83867_PORT_MIRROING_KEEP) dp83867_config_port_mirroring(phydev); /* Clock output selection if muxing property is set */ if (dp83867->clk_output_sel != DP83867_CLK_O_SEL_REF_CLK) { val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_IO_MUX_CFG); val &= ~DP83867_IO_MUX_CFG_CLK_O_SEL_MASK; val |= (dp83867->clk_output_sel << DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT); phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_IO_MUX_CFG, val); } return 0; }
static int dp83867_config_init(struct phy_device *phydev) { struct dp83867_private *dp83867; int ret, bs; u16 val, delay, cfg2; if (!phydev->priv) { dp83867 = devm_kzalloc(&phydev->mdio.dev, sizeof(*dp83867), GFP_KERNEL); if (!dp83867) return -ENOMEM; phydev->priv = dp83867; ret = dp83867_of_init(phydev); if (ret) return ret; } else { dp83867 = (struct dp83867_private *)phydev->priv; } /* RX_DV/RX_CTRL strapped in mode 1 or mode 2 workaround */ if (dp83867->rxctrl_strap_quirk) { val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4); val &= ~BIT(7); phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4, val); } if (phy_interface_is_rgmii(phydev)) { ret = phy_write(phydev, MII_DP83867_PHYCTRL, (DP83867_MDI_CROSSOVER_AUTO << DP83867_MDI_CROSSOVER) | (dp83867->fifo_depth << DP83867_PHYCR_FIFO_DEPTH_SHIFT)); if (ret) return ret; val = phy_read(phydev, MII_DP83867_PHYCTRL); if (val < 0) return val; val &= ~DP83867_PHYCR_FIFO_DEPTH_MASK; val |= (dp83867->fifo_depth << DP83867_PHYCR_FIFO_DEPTH_SHIFT); /* The code below checks if "port mirroring" N/A MODE4 has been * enabled during power on bootstrap. * * Such N/A mode enabled by mistake can put PHY IC in some * internal testing mode and disable RGMII transmission. * * In this particular case one needs to check STRAP_STS1 * register's bit 11 (marked as RESERVED). */ bs = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_STRAP_STS1); if (bs & DP83867_STRAP_STS1_RESERVED) val &= ~DP83867_PHYCR_RESERVED_MASK; ret = phy_write(phydev, MII_DP83867_PHYCTRL, val); if (ret) return ret; } else { /* Set SGMIICTL1 6-wire mode */ if (dp83867->wiremode_6) phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_SGMIITYPE, DP83867_SGMIICLK_EN); phy_write(phydev, MII_BMCR, (BMCR_ANENABLE | BMCR_FULLDPLX | BMCR_SPEED1000)); cfg2 = phy_read(phydev, MII_DP83867_CFG2); cfg2 &= MII_DP83867_CFG2_MASK; cfg2 |= (MII_DP83867_CFG2_SPEEDOPT_10EN | MII_DP83867_CFG2_SGMII_AUTONEGEN | MII_DP83867_CFG2_SPEEDOPT_ENH | MII_DP83867_CFG2_SPEEDOPT_CNT | MII_DP83867_CFG2_SPEEDOPT_INTLOW); phy_write(phydev, MII_DP83867_CFG2, cfg2); phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIICTL, 0x0); phy_write(phydev, MII_DP83867_PHYCTRL, DP83867_PHYCTRL_SGMIIEN | (DP83867_MDI_CROSSOVER_MDIX << DP83867_MDI_CROSSOVER) | (dp83867->fifo_depth << DP83867_PHYCTRL_RXFIFO_SHIFT) | (dp83867->fifo_depth << DP83867_PHYCTRL_TXFIFO_SHIFT)); phy_write(phydev, MII_DP83867_BISCR, 0x0); /* This is a SW workaround for link instability if * RX_CTRL is not strapped to mode 3 or 4 in HW. */ if (dp83867->rxctrl_strap_quirk) { val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4); val &= ~DP83867_CFG4_RESVDBIT7; val |= DP83867_CFG4_RESVDBIT8; val &= ~DP83867_CFG4_SGMII_AUTONEG_TIMER_MASK; val |= DP83867_CFG4_SGMII_AUTONEG_TIMER_11MS; phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_CFG4, val); } } if ((phydev->interface >= PHY_INTERFACE_MODE_RGMII_ID) && (phydev->interface <= PHY_INTERFACE_MODE_RGMII_RXID)) { val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIICTL); if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) val |= (DP83867_RGMII_TX_CLK_DELAY_EN | DP83867_RGMII_RX_CLK_DELAY_EN); if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) val |= DP83867_RGMII_TX_CLK_DELAY_EN; if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) val |= DP83867_RGMII_RX_CLK_DELAY_EN; phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIICTL, val); delay = (dp83867->rx_id_delay | (dp83867->tx_id_delay << DP83867_RGMII_TX_CLK_DELAY_SHIFT)); phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_RGMIIDCTL, delay); if (dp83867->io_impedance >= 0) { val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_IO_MUX_CFG); val &= ~DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL; val |= dp83867->io_impedance & DP83867_IO_MUX_CFG_IO_IMPEDANCE_CTRL; phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_IO_MUX_CFG, val); } } /* Enable Interrupt output INT_OE in CFG3 register */ if (phy_interrupt_is_valid(phydev)) { val = phy_read(phydev, DP83867_CFG3); val |= BIT(7); phy_write(phydev, DP83867_CFG3, val); } if (dp83867->port_mirroring != DP83867_PORT_MIRROING_KEEP) dp83867_config_port_mirroring(phydev); /* Clock output selection if muxing property is set */ if (dp83867->clk_output_sel != DP83867_CLK_O_SEL_REF_CLK) { val = phy_read_mmd(phydev, DP83867_DEVADDR, DP83867_IO_MUX_CFG); val &= ~DP83867_IO_MUX_CFG_CLK_O_SEL_MASK; val |= (dp83867->clk_output_sel << DP83867_IO_MUX_CFG_CLK_O_SEL_SHIFT); phy_write_mmd(phydev, DP83867_DEVADDR, DP83867_IO_MUX_CFG, val); } return 0; }
static void amd_xgbe_an_state_machine(struct work_struct *work) { struct amd_xgbe_phy_priv *priv = container_of(work, struct amd_xgbe_phy_priv, an_work); struct phy_device *phydev = priv->phydev; enum amd_xgbe_phy_an cur_state = priv->an_state; int int_reg, int_mask; mutex_lock(&priv->an_mutex); /* Read the interrupt */ int_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT); if (!int_reg) goto out; next_int: if (int_reg < 0) { priv->an_state = AMD_XGBE_AN_ERROR; int_mask = XGBE_AN_INT_MASK; } else if (int_reg & XGBE_AN_PG_RCV) { priv->an_state = AMD_XGBE_AN_PAGE_RECEIVED; int_mask = XGBE_AN_PG_RCV; } else if (int_reg & XGBE_AN_INC_LINK) { priv->an_state = AMD_XGBE_AN_INCOMPAT_LINK; int_mask = XGBE_AN_INC_LINK; } else if (int_reg & XGBE_AN_INT_CMPLT) { priv->an_state = AMD_XGBE_AN_COMPLETE; int_mask = XGBE_AN_INT_CMPLT; } else { priv->an_state = AMD_XGBE_AN_ERROR; int_mask = 0; } /* Clear the interrupt to be processed */ int_reg &= ~int_mask; phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, int_reg); priv->an_result = priv->an_state; again: cur_state = priv->an_state; switch (priv->an_state) { case AMD_XGBE_AN_READY: priv->an_supported = 0; break; case AMD_XGBE_AN_PAGE_RECEIVED: priv->an_state = amd_xgbe_an_page_received(phydev); priv->an_supported++; break; case AMD_XGBE_AN_INCOMPAT_LINK: priv->an_supported = 0; priv->parallel_detect = 0; priv->an_state = amd_xgbe_an_incompat_link(phydev); break; case AMD_XGBE_AN_COMPLETE: priv->parallel_detect = priv->an_supported ? 0 : 1; netdev_dbg(phydev->attached_dev, "%s successful\n", priv->an_supported ? "Auto negotiation" : "Parallel detection"); break; case AMD_XGBE_AN_NO_LINK: break; default: priv->an_state = AMD_XGBE_AN_ERROR; } if (priv->an_state == AMD_XGBE_AN_NO_LINK) { int_reg = 0; phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0); } else if (priv->an_state == AMD_XGBE_AN_ERROR) { netdev_err(phydev->attached_dev, "error during auto-negotiation, state=%u\n", cur_state); int_reg = 0; phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0); } if (priv->an_state >= AMD_XGBE_AN_COMPLETE) { priv->an_result = priv->an_state; priv->an_state = AMD_XGBE_AN_READY; priv->kr_state = AMD_XGBE_RX_BPA; priv->kx_state = AMD_XGBE_RX_BPA; } if (cur_state != priv->an_state) goto again; if (int_reg) goto next_int; out: enable_irq(priv->an_irq); mutex_unlock(&priv->an_mutex); }
static int amd_xgbe_phy_config_init(struct phy_device *phydev) { struct amd_xgbe_phy_priv *priv = phydev->priv; struct net_device *netdev = phydev->attached_dev; int ret; if (!priv->an_irq_allocated) { /* Allocate the auto-negotiation workqueue and interrupt */ snprintf(priv->an_irq_name, sizeof(priv->an_irq_name) - 1, "%s-pcs", netdev_name(netdev)); priv->an_workqueue = create_singlethread_workqueue(priv->an_irq_name); if (!priv->an_workqueue) { netdev_err(netdev, "phy workqueue creation failed\n"); return -ENOMEM; } ret = devm_request_irq(priv->dev, priv->an_irq, amd_xgbe_an_isr, 0, priv->an_irq_name, priv); if (ret) { netdev_err(netdev, "phy irq request failed\n"); destroy_workqueue(priv->an_workqueue); return ret; } priv->an_irq_allocated = 1; } ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FEC_ABILITY); if (ret < 0) return ret; priv->fec_ability = ret & XGBE_PHY_FEC_MASK; /* Initialize supported features */ phydev->supported = SUPPORTED_Autoneg; phydev->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; phydev->supported |= SUPPORTED_Backplane; phydev->supported |= SUPPORTED_10000baseKR_Full; switch (priv->speed_set) { case AMD_XGBE_PHY_SPEEDSET_1000_10000: phydev->supported |= SUPPORTED_1000baseKX_Full; break; case AMD_XGBE_PHY_SPEEDSET_2500_10000: phydev->supported |= SUPPORTED_2500baseX_Full; break; } if (priv->fec_ability & XGBE_PHY_FEC_ENABLE) phydev->supported |= SUPPORTED_10000baseR_FEC; phydev->advertising = phydev->supported; /* Set initial mode - call the mode setting routines * directly to insure we are properly configured */ if (phydev->supported & SUPPORTED_10000baseKR_Full) ret = amd_xgbe_phy_xgmii_mode(phydev); else if (phydev->supported & SUPPORTED_1000baseKX_Full) ret = amd_xgbe_phy_gmii_mode(phydev); else if (phydev->supported & SUPPORTED_2500baseX_Full) ret = amd_xgbe_phy_gmii_2500_mode(phydev); else ret = -EINVAL; if (ret < 0) return ret; /* Set up advertisement registers based on current settings */ ret = amd_xgbe_an_init(phydev); if (ret) return ret; /* Enable auto-negotiation interrupts */ phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INTMASK, 0x07); return 0; }