diff options
Diffstat (limited to 'drivers/net/pcs/pcs-lynx.c')
-rw-r--r-- | drivers/net/pcs/pcs-lynx.c | 123 |
1 files changed, 91 insertions, 32 deletions
diff --git a/drivers/net/pcs/pcs-lynx.c b/drivers/net/pcs/pcs-lynx.c index 622c3de3f3a8..9021b96d4f9d 100644 --- a/drivers/net/pcs/pcs-lynx.c +++ b/drivers/net/pcs/pcs-lynx.c @@ -6,6 +6,7 @@ #include <linux/mdio.h> #include <linux/phylink.h> #include <linux/pcs-lynx.h> +#include <linux/property.h> #define SGMII_CLOCK_PERIOD_NS 8 /* PCS is clocked at 125 MHz */ #define LINK_TIMER_VAL(ns) ((u32)((ns) / SGMII_CLOCK_PERIOD_NS)) @@ -34,14 +35,6 @@ enum sgmii_speed { #define phylink_pcs_to_lynx(pl_pcs) container_of((pl_pcs), struct lynx_pcs, pcs) #define lynx_to_phylink_pcs(lynx) (&(lynx)->pcs) -struct mdio_device *lynx_get_mdio_device(struct phylink_pcs *pcs) -{ - struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs); - - return lynx->mdio; -} -EXPORT_SYMBOL(lynx_get_mdio_device); - static void lynx_pcs_get_state_usxgmii(struct mdio_device *pcs, struct phylink_link_state *state) { @@ -119,9 +112,10 @@ static void lynx_pcs_get_state(struct phylink_pcs *pcs, state->link, state->an_complete); } -static int lynx_pcs_config_giga(struct mdio_device *pcs, unsigned int mode, +static int lynx_pcs_config_giga(struct mdio_device *pcs, phy_interface_t interface, - const unsigned long *advertising) + const unsigned long *advertising, + unsigned int neg_mode) { int link_timer_ns; u32 link_timer; @@ -139,8 +133,9 @@ static int lynx_pcs_config_giga(struct mdio_device *pcs, unsigned int mode, if (interface == PHY_INTERFACE_MODE_1000BASEX) { if_mode = 0; } else { + /* SGMII and QSGMII */ if_mode = IF_MODE_SGMII_EN; - if (mode == MLO_AN_INBAND) + if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) if_mode |= IF_MODE_USE_SGMII_AN; } @@ -150,16 +145,18 @@ static int lynx_pcs_config_giga(struct mdio_device *pcs, unsigned int mode, if (err) return err; - return phylink_mii_c22_pcs_config(pcs, mode, interface, advertising); + return phylink_mii_c22_pcs_config(pcs, interface, advertising, + neg_mode); } -static int lynx_pcs_config_usxgmii(struct mdio_device *pcs, unsigned int mode, - const unsigned long *advertising) +static int lynx_pcs_config_usxgmii(struct mdio_device *pcs, + const unsigned long *advertising, + unsigned int neg_mode) { struct mii_bus *bus = pcs->bus; int addr = pcs->addr; - if (!phylink_autoneg_inband(mode)) { + if (neg_mode != PHYLINK_PCS_NEG_INBAND_ENABLED) { dev_err(&pcs->dev, "USXGMII only supports in-band AN for now\n"); return -EOPNOTSUPP; } @@ -171,10 +168,9 @@ static int lynx_pcs_config_usxgmii(struct mdio_device *pcs, unsigned int mode, ADVERTISE_SGMII | ADVERTISE_LPACK); } -static int lynx_pcs_config(struct phylink_pcs *pcs, unsigned int mode, +static int lynx_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, phy_interface_t ifmode, - const unsigned long *advertising, - bool permit) + const unsigned long *advertising, bool permit) { struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs); @@ -182,17 +178,18 @@ static int lynx_pcs_config(struct phylink_pcs *pcs, unsigned int mode, case PHY_INTERFACE_MODE_1000BASEX: case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_QSGMII: - return lynx_pcs_config_giga(lynx->mdio, mode, ifmode, - advertising); + return lynx_pcs_config_giga(lynx->mdio, ifmode, advertising, + neg_mode); case PHY_INTERFACE_MODE_2500BASEX: - if (phylink_autoneg_inband(mode)) { + if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) { dev_err(&lynx->mdio->dev, "AN not supported on 3.125GHz SerDes lane\n"); return -EOPNOTSUPP; } break; case PHY_INTERFACE_MODE_USXGMII: - return lynx_pcs_config_usxgmii(lynx->mdio, mode, advertising); + return lynx_pcs_config_usxgmii(lynx->mdio, advertising, + neg_mode); case PHY_INTERFACE_MODE_10GBASER: /* Nothing to do here for 10GBASER */ break; @@ -210,7 +207,8 @@ static void lynx_pcs_an_restart(struct phylink_pcs *pcs) phylink_mii_c22_pcs_an_restart(lynx->mdio); } -static void lynx_pcs_link_up_sgmii(struct mdio_device *pcs, unsigned int mode, +static void lynx_pcs_link_up_sgmii(struct mdio_device *pcs, + unsigned int neg_mode, int speed, int duplex) { u16 if_mode = 0, sgmii_speed; @@ -218,7 +216,7 @@ static void lynx_pcs_link_up_sgmii(struct mdio_device *pcs, unsigned int mode, /* The PCS needs to be configured manually only * when not operating on in-band mode */ - if (mode == MLO_AN_INBAND) + if (neg_mode != PHYLINK_PCS_NEG_INBAND_ENABLED) return; if (duplex == DUPLEX_HALF) @@ -265,12 +263,12 @@ static void lynx_pcs_link_up_sgmii(struct mdio_device *pcs, unsigned int mode, * 2500 Mbps and we do rate adaptation through pause frames. */ static void lynx_pcs_link_up_2500basex(struct mdio_device *pcs, - unsigned int mode, + unsigned int neg_mode, int speed, int duplex) { u16 if_mode = 0; - if (mode == MLO_AN_INBAND) { + if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) { dev_err(&pcs->dev, "AN not supported for 2500BaseX\n"); return; } @@ -284,7 +282,7 @@ static void lynx_pcs_link_up_2500basex(struct mdio_device *pcs, if_mode); } -static void lynx_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, +static void lynx_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode, phy_interface_t interface, int speed, int duplex) { @@ -293,10 +291,10 @@ static void lynx_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, switch (interface) { case PHY_INTERFACE_MODE_SGMII: case PHY_INTERFACE_MODE_QSGMII: - lynx_pcs_link_up_sgmii(lynx->mdio, mode, speed, duplex); + lynx_pcs_link_up_sgmii(lynx->mdio, neg_mode, speed, duplex); break; case PHY_INTERFACE_MODE_2500BASEX: - lynx_pcs_link_up_2500basex(lynx->mdio, mode, speed, duplex); + lynx_pcs_link_up_2500basex(lynx->mdio, neg_mode, speed, duplex); break; case PHY_INTERFACE_MODE_USXGMII: /* At the moment, only in-band AN is supported for USXGMII @@ -315,26 +313,87 @@ static const struct phylink_pcs_ops lynx_pcs_phylink_ops = { .pcs_link_up = lynx_pcs_link_up, }; -struct phylink_pcs *lynx_pcs_create(struct mdio_device *mdio) +static struct phylink_pcs *lynx_pcs_create(struct mdio_device *mdio) { struct lynx_pcs *lynx; lynx = kzalloc(sizeof(*lynx), GFP_KERNEL); if (!lynx) - return NULL; + return ERR_PTR(-ENOMEM); + mdio_device_get(mdio); lynx->mdio = mdio; lynx->pcs.ops = &lynx_pcs_phylink_ops; + lynx->pcs.neg_mode = true; lynx->pcs.poll = true; return lynx_to_phylink_pcs(lynx); } -EXPORT_SYMBOL(lynx_pcs_create); + +struct phylink_pcs *lynx_pcs_create_mdiodev(struct mii_bus *bus, int addr) +{ + struct mdio_device *mdio; + struct phylink_pcs *pcs; + + mdio = mdio_device_create(bus, addr); + if (IS_ERR(mdio)) + return ERR_CAST(mdio); + + pcs = lynx_pcs_create(mdio); + + /* lynx_create() has taken a refcount on the mdiodev if it was + * successful. If lynx_create() fails, this will free the mdio + * device here. In any case, we don't need to hold our reference + * anymore, and putting it here will allow mdio_device_put() in + * lynx_destroy() to automatically free the mdio device. + */ + mdio_device_put(mdio); + + return pcs; +} +EXPORT_SYMBOL(lynx_pcs_create_mdiodev); + +/* + * lynx_pcs_create_fwnode() creates a lynx PCS instance from the fwnode + * device indicated by node. + * + * Returns: + * -ENODEV if the fwnode is marked unavailable + * -EPROBE_DEFER if we fail to find the device + * -ENOMEM if we fail to allocate memory + * pointer to a phylink_pcs on success + */ +struct phylink_pcs *lynx_pcs_create_fwnode(struct fwnode_handle *node) +{ + struct mdio_device *mdio; + struct phylink_pcs *pcs; + + if (!fwnode_device_is_available(node)) + return ERR_PTR(-ENODEV); + + mdio = fwnode_mdio_find_device(node); + if (!mdio) + return ERR_PTR(-EPROBE_DEFER); + + pcs = lynx_pcs_create(mdio); + + /* lynx_create() has taken a refcount on the mdiodev if it was + * successful. If lynx_create() fails, this will free the mdio + * device here. In any case, we don't need to hold our reference + * anymore, and putting it here will allow mdio_device_put() in + * lynx_destroy() to automatically free the mdio device. + */ + mdio_device_put(mdio); + + return pcs; +} +EXPORT_SYMBOL_GPL(lynx_pcs_create_fwnode); void lynx_pcs_destroy(struct phylink_pcs *pcs) { struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs); + mdio_device_put(lynx->mdio); kfree(lynx); } EXPORT_SYMBOL(lynx_pcs_destroy); |