static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port, struct fixed_phy_status *status) { struct bcm_sf2_priv *priv = ds_to_priv(ds); u32 duplex, pause; u32 reg; duplex = core_readl(priv, CORE_DUPSTS); pause = core_readl(priv, CORE_PAUSESTS); status->link = 0; /* MoCA port is special as we do not get link status from CORE_LNKSTS, * which means that we need to force the link at the port override * level to get the data to flow. We do use what the interrupt handler * did determine before. * * For the other ports, we just force the link status, since this is * a fixed PHY device. */ if (port == priv->moca_port) { status->link = priv->port_sts[port].link; /* For MoCA interfaces, also force a link down notification * since some version of the user-space daemon (mocad) use * cmd->autoneg to force the link, which messes up the PHY * state machine and make it go in PHY_FORCING state instead. */ if (!status->link) netif_carrier_off(ds->ports[port].netdev); status->duplex = 1; } else { status->link = 1; status->duplex = !!(duplex & (1 << port)); } reg = core_readl(priv, CORE_STS_OVERRIDE_GMIIP_PORT(port)); reg |= SW_OVERRIDE; if (status->link) reg |= LINK_STS; else reg &= ~LINK_STS; core_writel(priv, reg, CORE_STS_OVERRIDE_GMIIP_PORT(port)); if ((pause & (1 << port)) && (pause & (1 << (port + PAUSESTS_TX_PAUSE_SHIFT)))) { status->asym_pause = 1; status->pause = 1; } if (pause & (1 << port)) status->pause = 1; }
static void bcm_sf2_sw_fixed_link_update(struct dsa_switch *ds, int port, struct fixed_phy_status *status) { struct bcm_sf2_priv *priv = ds_to_priv(ds); u32 link, duplex, pause; u32 reg; link = core_readl(priv, CORE_LNKSTS); duplex = core_readl(priv, CORE_DUPSTS); pause = core_readl(priv, CORE_PAUSESTS); status->link = 0; /* Port 7 is special as we do not get link status from CORE_LNKSTS, * which means that we need to force the link at the port override * level to get the data to flow. We do use what the interrupt handler * did determine before. */ if (port == 7) { status->link = priv->port_sts[port].link; reg = core_readl(priv, CORE_STS_OVERRIDE_GMIIP_PORT(7)); reg |= SW_OVERRIDE; if (status->link) reg |= LINK_STS; else reg &= ~LINK_STS; core_writel(priv, reg, CORE_STS_OVERRIDE_GMIIP_PORT(7)); status->duplex = 1; } else { status->link = !!(link & (1 << port)); status->duplex = !!(duplex & (1 << port)); } if ((pause & (1 << port)) && (pause & (1 << (port + PAUSESTS_TX_PAUSE_SHIFT)))) { status->asym_pause = 1; status->pause = 1; } if (pause & (1 << port)) status->pause = 1; }
static void bcm_sf2_sw_adjust_link(struct dsa_switch *ds, int port, struct phy_device *phydev) { struct bcm_sf2_priv *priv = ds_to_priv(ds); u32 id_mode_dis = 0, port_mode; const char *str = NULL; u32 reg; switch (phydev->interface) { case PHY_INTERFACE_MODE_RGMII: str = "RGMII (no delay)"; id_mode_dis = 1; case PHY_INTERFACE_MODE_RGMII_TXID: if (!str) str = "RGMII (TX delay)"; port_mode = EXT_GPHY; break; case PHY_INTERFACE_MODE_MII: str = "MII"; port_mode = EXT_EPHY; break; case PHY_INTERFACE_MODE_REVMII: str = "Reverse MII"; port_mode = EXT_REVMII; break; default: /* All other PHYs: internal and MoCA */ goto force_link; } /* If the link is down, just disable the interface to conserve power */ if (!phydev->link) { reg = reg_readl(priv, REG_RGMII_CNTRL_P(port)); reg &= ~RGMII_MODE_EN; reg_writel(priv, reg, REG_RGMII_CNTRL_P(port)); goto force_link; } /* Clear id_mode_dis bit, and the existing port mode, but * make sure we enable the RGMII block for data to pass */ reg = reg_readl(priv, REG_RGMII_CNTRL_P(port)); reg &= ~ID_MODE_DIS; reg &= ~(PORT_MODE_MASK << PORT_MODE_SHIFT); reg &= ~(RX_PAUSE_EN | TX_PAUSE_EN); reg |= port_mode | RGMII_MODE_EN; if (id_mode_dis) reg |= ID_MODE_DIS; if (phydev->pause) { if (phydev->asym_pause) reg |= TX_PAUSE_EN; reg |= RX_PAUSE_EN; } reg_writel(priv, reg, REG_RGMII_CNTRL_P(port)); pr_info("Port %d configured for %s\n", port, str); force_link: /* Force link settings detected from the PHY */ reg = SW_OVERRIDE; switch (phydev->speed) { case SPEED_1000: reg |= SPDSTS_1000 << SPEED_SHIFT; break; case SPEED_100: reg |= SPDSTS_100 << SPEED_SHIFT; break; } if (phydev->link) reg |= LINK_STS; if (phydev->duplex == DUPLEX_FULL) reg |= DUPLX_MODE; core_writel(priv, reg, CORE_STS_OVERRIDE_GMIIP_PORT(port)); }
static void bcm_sf2_sw_mac_config(struct dsa_switch *ds, int port, unsigned int mode, const struct phylink_link_state *state) { struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); u32 id_mode_dis = 0, port_mode; u32 reg, offset; if (priv->type == BCM7445_DEVICE_ID) offset = CORE_STS_OVERRIDE_GMIIP_PORT(port); else offset = CORE_STS_OVERRIDE_GMIIP2_PORT(port); switch (state->interface) { case PHY_INTERFACE_MODE_RGMII: id_mode_dis = 1; /* fallthrough */ case PHY_INTERFACE_MODE_RGMII_TXID: port_mode = EXT_GPHY; break; case PHY_INTERFACE_MODE_MII: port_mode = EXT_EPHY; break; case PHY_INTERFACE_MODE_REVMII: port_mode = EXT_REVMII; break; default: /* all other PHYs: internal and MoCA */ goto force_link; } /* Clear id_mode_dis bit, and the existing port mode, let * RGMII_MODE_EN bet set by mac_link_{up,down} */ reg = reg_readl(priv, REG_RGMII_CNTRL_P(port)); reg &= ~ID_MODE_DIS; reg &= ~(PORT_MODE_MASK << PORT_MODE_SHIFT); reg &= ~(RX_PAUSE_EN | TX_PAUSE_EN); reg |= port_mode; if (id_mode_dis) reg |= ID_MODE_DIS; if (state->pause & MLO_PAUSE_TXRX_MASK) { if (state->pause & MLO_PAUSE_TX) reg |= TX_PAUSE_EN; reg |= RX_PAUSE_EN; } reg_writel(priv, reg, REG_RGMII_CNTRL_P(port)); force_link: /* Force link settings detected from the PHY */ reg = SW_OVERRIDE; switch (state->speed) { case SPEED_1000: reg |= SPDSTS_1000 << SPEED_SHIFT; break; case SPEED_100: reg |= SPDSTS_100 << SPEED_SHIFT; break; } if (state->link) reg |= LINK_STS; if (state->duplex == DUPLEX_FULL) reg |= DUPLX_MODE; core_writel(priv, reg, offset); }