diff options
author | Russell King <rmk+kernel@armlinux.org.uk> | 2020-01-30 22:42:38 +0000 |
---|---|---|
committer | Russell King <rmk+kernel@armlinux.org.uk> | 2020-05-23 13:11:42 +0100 |
commit | 2da5331d428a83a2f8a2dade88804d46ef620ee2 (patch) | |
tree | 0aa2896ec6947f1c7fdda51c2091cf0a10613088 /drivers | |
parent | d4157cae0b75b70eefea3eb51bb7bd4e185b3ddc (diff) |
dpaa2-mac: add 1000BASE-X/SGMII PCS support
Add support for the PCS block, so we can dynamically configure it for
1000base-X or SGMII depending on the SFP module inserted. This gives
us more flexibility than using the MC firmware with a "fixed" link
type, which can only be setup to support a single interface mode.
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 206 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h | 1 |
2 files changed, 204 insertions, 3 deletions
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c index f3a2611cb76e..39e545ccd697 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c @@ -7,6 +7,132 @@ #define phylink_to_dpaa2_mac(config) \ container_of((config), struct dpaa2_mac, phylink_config) +// ID: 0x0083 0xe400 => OUI 00:20:f9 => Paralink Networks, Inc. +#define MII_LINK_TIMER_1 0x12 +#define MII_LINK_TIMER_2 0x13 +#define LINK_TIMER_US_IEEE8023 10000 +#define LINK_TIMER_US_SGMII 1600 +#define DPAA2_MAC_PCS_CLK_MHZ 125 + +#define MII_IFMODE 0x14 +#define IF_MODE_SGMII_ENA BIT(0) +#define IF_MODE_USE_SGMII_AN BIT(1) +#define IF_MODE_SGMII_SPEED_10 (0 << 2) +#define IF_MODE_SGMII_SPEED_100 (1 << 2) +#define IF_MODE_SGMII_SPEED_1G (2 << 2) +#define IF_MODE_SGMII_SPEED_MSK (3 << 2) +#define IF_MODE_SGMII_DUPLEX BIT(4) // set = half duplex + +static void dpaa2_mac_pcs_get_state(struct phylink_config *config, + struct phylink_link_state *state) +{ + struct dpaa2_mac *mac = phylink_to_dpaa2_mac(config); + + switch (state->interface) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + phylink_mii_c22_pcs_get_state(mac->pcs_sgmii, state); + break; + + default: + break; + } +} + +static void dpaa2_mac_pcs_an_restart(struct phylink_config *config) +{ + struct dpaa2_mac *mac = phylink_to_dpaa2_mac(config); + + phylink_mii_c22_pcs_an_restart(mac->pcs_sgmii); +} + +static int dpaa2_mac_pcs_config(struct phylink_config *config, + unsigned int mode, phy_interface_t interface, + const unsigned long *advertising) +{ + struct dpaa2_mac *mac = phylink_to_dpaa2_mac(config); + u32 link_timer; + u16 if_mode; + int ret; + + switch (interface) { + case PHY_INTERFACE_MODE_SGMII: + if_mode = IF_MODE_SGMII_ENA; + if (mode == MLO_AN_INBAND) + if_mode |= IF_MODE_USE_SGMII_AN; + link_timer = DPAA2_MAC_PCS_CLK_MHZ * LINK_TIMER_US_SGMII; + mdiobus_write(mac->pcs_sgmii->bus, mac->pcs_sgmii->addr, + MII_LINK_TIMER_1, link_timer & 0xffff); + mdiobus_write(mac->pcs_sgmii->bus, mac->pcs_sgmii->addr, + MII_LINK_TIMER_2, link_timer >> 16); + mdiobus_modify(mac->pcs_sgmii->bus, mac->pcs_sgmii->addr, + MII_IFMODE, + IF_MODE_SGMII_ENA | IF_MODE_USE_SGMII_AN, + if_mode); + ret = phylink_mii_c22_pcs_config(mac->pcs_sgmii, mode, + interface, advertising); + break; + + case PHY_INTERFACE_MODE_1000BASEX: + link_timer = DPAA2_MAC_PCS_CLK_MHZ * LINK_TIMER_US_IEEE8023; + mdiobus_write(mac->pcs_sgmii->bus, mac->pcs_sgmii->addr, + MII_LINK_TIMER_1, link_timer & 0xffff); + mdiobus_write(mac->pcs_sgmii->bus, mac->pcs_sgmii->addr, + MII_LINK_TIMER_2, link_timer >> 16); + mdiobus_write(mac->pcs_sgmii->bus, mac->pcs_sgmii->addr, + MII_IFMODE, 0); + ret = phylink_mii_c22_pcs_config(mac->pcs_sgmii, mode, + interface, advertising); + break; + + default: + ret = 0; + break; + } + + return ret; +} + +static void dpaa2_mac_pcs_link_up(struct phylink_config *config, + unsigned int mode, phy_interface_t interface, + int speed, int duplex) +{ + struct dpaa2_mac *mac = phylink_to_dpaa2_mac(config); + u16 if_mode; + + /* The PCS PHY needs to be configured manually for the speed and + * duplex when operating in SGMII mode without in-band negotiation. + */ + if (mode == MLO_AN_INBAND || interface != PHY_INTERFACE_MODE_SGMII) + return; + + switch (speed) { + case SPEED_10: + if_mode = IF_MODE_SGMII_SPEED_10; + break; + + case SPEED_100: + if_mode = IF_MODE_SGMII_SPEED_100; + break; + + default: + if_mode = IF_MODE_SGMII_SPEED_1G; + break; + } + if (duplex == DUPLEX_HALF) + if_mode |= IF_MODE_SGMII_DUPLEX; + + mdiobus_modify(mac->pcs_sgmii->bus, mac->pcs_sgmii->addr, MII_IFMODE, + IF_MODE_SGMII_DUPLEX | IF_MODE_SGMII_SPEED_MSK, if_mode); +} + +static const struct phylink_pcs_ops dpaa2_pcs_phylink_ops = { + .pcs_get_state = dpaa2_mac_pcs_get_state, + .pcs_config = dpaa2_mac_pcs_config, + .pcs_an_restart = dpaa2_mac_pcs_an_restart, + .pcs_link_up = dpaa2_mac_pcs_link_up, +}; + static int phy_mode(enum dpmac_eth_if eth_if, phy_interface_t *if_mode) { *if_mode = PHY_INTERFACE_MODE_NA; @@ -15,6 +141,11 @@ static int phy_mode(enum dpmac_eth_if eth_if, phy_interface_t *if_mode) case DPMAC_ETH_IF_RGMII: *if_mode = PHY_INTERFACE_MODE_RGMII; break; + + case DPMAC_ETH_IF_SGMII: + *if_mode = PHY_INTERFACE_MODE_SGMII; + break; + default: return -EINVAL; } @@ -67,6 +198,10 @@ static bool dpaa2_mac_phy_mode_mismatch(struct dpaa2_mac *mac, phy_interface_t interface) { switch (interface) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + return interface != mac->if_mode && !mac->pcs_sgmii; + case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII_RXID: @@ -95,13 +230,19 @@ static void dpaa2_mac_validate(struct phylink_config *config, phylink_set(mask, Asym_Pause); switch (state->interface) { + case PHY_INTERFACE_MODE_NA: + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_TXID: - phylink_set(mask, 10baseT_Full); - phylink_set(mask, 100baseT_Full); + phylink_set(mask, 1000baseX_Full); phylink_set(mask, 1000baseT_Full); + if (state->interface == PHY_INTERFACE_MODE_1000BASEX) + break; + phylink_set(mask, 100baseT_Full); + phylink_set(mask, 10baseT_Full); break; default: goto empty_set; @@ -227,6 +368,44 @@ out: return fixed; } +static int dpaa2_pcs_create(struct dpaa2_mac *mac, + struct device_node *dpmac_node, int id) +{ + struct mdio_device *mdiodev; + struct device_node *node; + + node = of_parse_phandle(dpmac_node, "pcs-handle", 0); + if (!node) { + /* allow old DT files to work */ + netdev_warn(mac->net_dev, "pcs-handle node not found\n"); + return 0; + } + + if (!of_device_is_available(node) || + !of_device_is_available(node->parent)) { + netdev_err(mac->net_dev, "pcs-handle node not available\n"); + return -ENODEV; + } + + mdiodev = of_mdio_find_device(node); + of_node_put(node); + if (!mdiodev) + return -EPROBE_DEFER; + + mac->pcs_sgmii = mdiodev; + mac->phylink_config.pcs_poll = true; + + return 0; +} + +static void dpaa2_pcs_destroy(struct dpaa2_mac *mac) +{ + if (mac->pcs_sgmii) { + put_device(&mac->pcs_sgmii->dev); + mac->pcs_sgmii = NULL; + } +} + int dpaa2_mac_connect(struct dpaa2_mac *mac) { struct fsl_mc_device *dpmac_dev = mac->mc_dev; @@ -236,6 +415,8 @@ int dpaa2_mac_connect(struct dpaa2_mac *mac) struct dpmac_attr attr; int err; + memset(&mac->phylink_config, 0, sizeof(mac->phylink_config)); + err = dpmac_open(mac->mc_io, 0, dpmac_dev->obj_desc.id, &dpmac_dev->mc_handle); if (err || !dpmac_dev->mc_handle) { @@ -278,18 +459,33 @@ int dpaa2_mac_connect(struct dpaa2_mac *mac) goto err_put_node; } + if (attr.link_type == DPMAC_LINK_TYPE_PHY && + attr.eth_if != DPMAC_ETH_IF_RGMII) { + err = dpaa2_pcs_create(mac, dpmac_node, attr.id); + if (err) + goto err_put_node; + } + mac->phylink_config.dev = &net_dev->dev; mac->phylink_config.type = PHYLINK_NETDEV; + __set_bit(mac->if_mode, mac->phylink_config.supported_interfaces); + if (mac->if_mode == PHY_INTERFACE_MODE_SGMII && mac->pcs_sgmii) + __set_bit(PHY_INTERFACE_MODE_1000BASEX, + mac->phylink_config.supported_interfaces); + phylink = phylink_create(&mac->phylink_config, of_fwnode_handle(dpmac_node), mac->if_mode, &dpaa2_mac_phylink_ops); if (IS_ERR(phylink)) { err = PTR_ERR(phylink); - goto err_put_node; + goto err_pcs_destroy; } mac->phylink = phylink; + if (mac->pcs_sgmii) + phylink_add_pcs(mac->phylink, &dpaa2_pcs_phylink_ops); + rtnl_lock(); err = phylink_of_phy_connect(mac->phylink, dpmac_node, 0); rtnl_unlock(); @@ -304,6 +500,8 @@ int dpaa2_mac_connect(struct dpaa2_mac *mac) err_phylink_destroy: phylink_destroy(mac->phylink); +err_pcs_destroy: + dpaa2_pcs_destroy(mac); err_put_node: of_node_put(dpmac_node); err_close_dpmac: @@ -320,6 +518,8 @@ void dpaa2_mac_disconnect(struct dpaa2_mac *mac) phylink_disconnect_phy(mac->phylink); rtnl_unlock(); phylink_destroy(mac->phylink); + dpaa2_pcs_destroy(mac); + dpmac_close(mac->mc_io, 0, mac->mc_dev->mc_handle); } diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h index 2130d9c7d40e..9df3fd290a47 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h @@ -19,6 +19,7 @@ struct dpaa2_mac { struct phylink_config phylink_config; struct phylink *phylink; + struct mdio_device *pcs_sgmii; phy_interface_t if_mode; enum dpmac_link_type if_link_type; }; |