From 27900f6c502bf248ecb713ed8f52922098df398d Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 30 Jan 2020 22:42:38 +0000 Subject: 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. We add support for the "backplane" mode, which is the mode we should be using. Signed-off-by: Russell King --- drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 231 ++++++++++++++++++++++- drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h | 3 + 2 files changed, 231 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..49af3f1af32b 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c @@ -7,6 +7,143 @@ #define phylink_to_dpaa2_mac(config) \ container_of((config), struct dpaa2_mac, phylink_config) +struct lynx_pcs { + struct mdio_device *mdio; + struct phylink_pcs pcs; +}; + +static struct lynx_pcs *phylink_pcs_to_lynx(struct phylink_pcs *pcs) +{ + return container_of(pcs, struct lynx_pcs, pcs); +} + +// 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 lynx_pcs_get_state(struct phylink_pcs *pcs, + struct phylink_link_state *state) +{ + struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs); + + switch (state->interface) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + phylink_mii_c22_pcs_get_state(lynx->mdio, state); + break; + + default: + break; + } +} + +static void lynx_pcs_an_restart(struct phylink_pcs *pcs) +{ + struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs); + + phylink_mii_c22_pcs_an_restart(lynx->mdio); +} + +static int lynx_pcs_config(struct phylink_pcs *pcs, + unsigned int mode, phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) +{ + struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs); + 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(lynx->mdio->bus, lynx->mdio->addr, + MII_LINK_TIMER_1, link_timer & 0xffff); + mdiobus_write(lynx->mdio->bus, lynx->mdio->addr, + MII_LINK_TIMER_2, link_timer >> 16); + mdiobus_modify(lynx->mdio->bus, lynx->mdio->addr, + MII_IFMODE, + IF_MODE_SGMII_ENA | IF_MODE_USE_SGMII_AN, + if_mode); + ret = phylink_mii_c22_pcs_config(lynx->mdio, mode, + interface, advertising); + break; + + case PHY_INTERFACE_MODE_1000BASEX: + link_timer = DPAA2_MAC_PCS_CLK_MHZ * LINK_TIMER_US_IEEE8023; + mdiobus_write(lynx->mdio->bus, lynx->mdio->addr, + MII_LINK_TIMER_1, link_timer & 0xffff); + mdiobus_write(lynx->mdio->bus, lynx->mdio->addr, + MII_LINK_TIMER_2, link_timer >> 16); + mdiobus_write(lynx->mdio->bus, lynx->mdio->addr, + MII_IFMODE, 0); + ret = phylink_mii_c22_pcs_config(lynx->mdio, mode, + interface, advertising); + break; + + default: + ret = 0; + break; + } + + return ret; +} + +static void lynx_pcs_link_up(struct phylink_pcs *pcs, + unsigned int mode, phy_interface_t interface, + int speed, int duplex) +{ + struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs); + 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(lynx->mdio->bus, lynx->mdio->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 = lynx_pcs_get_state, + .pcs_config = lynx_pcs_config, + .pcs_an_restart = lynx_pcs_an_restart, + .pcs_link_up = lynx_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 +152,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 +209,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; + case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII_RXID: @@ -95,13 +241,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 +379,57 @@ 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; + struct lynx_pcs *lynx; + + 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; + + lynx = kmalloc(sizeof(*lynx), GFP_KERNEL); + if (!lynx) { + put_device(&mdiodev->dev); + return -ENOMEM; + } + + lynx->mdio = mdiodev; + lynx->pcs.ops = &dpaa2_pcs_phylink_ops; + lynx->pcs.poll = true; + + mac->pcs = lynx; + + return 0; +} + +static void dpaa2_pcs_destroy(struct dpaa2_mac *mac) +{ + struct lynx_pcs *pcs = mac->pcs; + + if (pcs) { + mac->pcs = NULL; + put_device(&pcs->mdio->dev); + kfree(pcs); + } +} + int dpaa2_mac_connect(struct dpaa2_mac *mac) { struct fsl_mc_device *dpmac_dev = mac->mc_dev; @@ -236,6 +439,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 +483,34 @@ 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) || + attr.link_type == DPMAC_LINK_TYPE_BACKPLANE) { + 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) + __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) + phylink_set_pcs(mac->phylink, &mac->pcs->pcs); + rtnl_lock(); err = phylink_of_phy_connect(mac->phylink, dpmac_node, 0); rtnl_unlock(); @@ -304,6 +525,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 +543,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..e6acb357b214 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h @@ -11,6 +11,8 @@ #include "dpmac.h" #include "dpmac-cmd.h" +struct lynx_pcs; + struct dpaa2_mac { struct fsl_mc_device *mc_dev; struct dpmac_link_state state; @@ -19,6 +21,7 @@ struct dpaa2_mac { struct phylink_config phylink_config; struct phylink *phylink; + struct lynx_pcs *pcs; phy_interface_t if_mode; enum dpmac_link_type if_link_type; }; -- cgit