diff options
author | David S. Miller <davem@davemloft.net> | 2019-05-31 12:37:46 -0700 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2019-05-31 12:37:46 -0700 |
commit | 6912378d54a593dc8469f0ee18135ea62de48c50 (patch) | |
tree | 4e946042fe296e3fb3213adb5a59e901fe2eb860 | |
parent | b4b12b0d2f02613101a7a667ef7b7cc8d388e597 (diff) | |
parent | 54f70b3ba364f19291dc8b9cb096b02a00fb4461 (diff) |
Merge branch 'phylink-sfp-updates'
Russell King says:
====================
phylink/sfp updates
This is a series of updates to phylink and sfp:
- Remove an unused net device argument from the phylink MII ioctl
emulation code.
- add support for using interrupts when using a GPIO for link status
tracking, rather than polling it at one second intervals. This
reduces the need to wakeup the CPU every second.
- add support to the MII ioctl API to read and write Clause 45 PHY
registers. I don't know how desirable this is for mainline, but I
have used this facility extensively to investigate the Marvell
88x3310 PHY. A recent illustration of use for this was debugging
the PHY-without-firmware problem recently reported.
- add mandatory attach/detach methods for the upstream side of sfp
bus code, which will allow us to remove the "netdev" structure from
the SFP layers.
- remove the "netdev" structure from the SFP upstream registration
calls, which simplifies PHY to SFP links.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/phy/phy.c | 33 | ||||
-rw-r--r-- | drivers/net/phy/phylink.c | 67 | ||||
-rw-r--r-- | drivers/net/phy/sfp-bus.c | 14 | ||||
-rw-r--r-- | include/linux/sfp.h | 12 |
4 files changed, 90 insertions, 36 deletions
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 84671d868a80..0084220d10dc 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -409,6 +409,7 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd) struct mii_ioctl_data *mii_data = if_mii(ifr); u16 val = mii_data->val_in; bool change_autoneg = false; + int prtad, devad; switch (cmd) { case SIOCGMIIPHY: @@ -416,14 +417,29 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd) /* fall through */ case SIOCGMIIREG: - mii_data->val_out = mdiobus_read(phydev->mdio.bus, - mii_data->phy_id, - mii_data->reg_num); + if (mdio_phy_id_is_c45(mii_data->phy_id)) { + prtad = mdio_phy_id_prtad(mii_data->phy_id); + devad = mdio_phy_id_devad(mii_data->phy_id); + devad = MII_ADDR_C45 | devad << 16 | mii_data->reg_num; + } else { + prtad = mii_data->phy_id; + devad = mii_data->reg_num; + } + mii_data->val_out = mdiobus_read(phydev->mdio.bus, prtad, + devad); return 0; case SIOCSMIIREG: - if (mii_data->phy_id == phydev->mdio.addr) { - switch (mii_data->reg_num) { + if (mdio_phy_id_is_c45(mii_data->phy_id)) { + prtad = mdio_phy_id_prtad(mii_data->phy_id); + devad = mdio_phy_id_devad(mii_data->phy_id); + devad = MII_ADDR_C45 | devad << 16 | mii_data->reg_num; + } else { + prtad = mii_data->phy_id; + devad = mii_data->reg_num; + } + if (prtad == phydev->mdio.addr) { + switch (devad) { case MII_BMCR: if ((val & (BMCR_RESET | BMCR_ANENABLE)) == 0) { if (phydev->autoneg == AUTONEG_ENABLE) @@ -456,11 +472,10 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd) } } - mdiobus_write(phydev->mdio.bus, mii_data->phy_id, - mii_data->reg_num, val); + mdiobus_write(phydev->mdio.bus, prtad, devad, val); - if (mii_data->phy_id == phydev->mdio.addr && - mii_data->reg_num == MII_BMCR && + if (prtad == phydev->mdio.addr && + devad == MII_BMCR && val & BMCR_RESET) return phy_init_hw(phydev); diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c index b7300ea2663c..f5b97dab3017 100644 --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c @@ -59,6 +59,7 @@ struct phylink { phy_interface_t cur_interface; struct gpio_desc *link_gpio; + unsigned int link_irq; struct timer_list link_poll; void (*get_fixed_state)(struct net_device *dev, struct phylink_link_state *s); @@ -564,8 +565,7 @@ static int phylink_register_sfp(struct phylink *pl, return ret; } - pl->sfp_bus = sfp_register_upstream(ref.fwnode, pl->netdev, pl, - &sfp_phylink_ops); + pl->sfp_bus = sfp_register_upstream(ref.fwnode, pl, &sfp_phylink_ops); if (!pl->sfp_bus) return -ENOMEM; @@ -665,7 +665,7 @@ void phylink_destroy(struct phylink *pl) { if (pl->sfp_bus) sfp_unregister_upstream(pl->sfp_bus); - if (!IS_ERR_OR_NULL(pl->link_gpio)) + if (pl->link_gpio) gpiod_put(pl->link_gpio); cancel_work_sync(&pl->resolve); @@ -928,6 +928,15 @@ void phylink_mac_change(struct phylink *pl, bool up) } EXPORT_SYMBOL_GPL(phylink_mac_change); +static irqreturn_t phylink_link_handler(int irq, void *data) +{ + struct phylink *pl = data; + + phylink_run_resolve(pl); + + return IRQ_HANDLED; +} + /** * phylink_start() - start a phylink instance * @pl: a pointer to a &struct phylink returned from phylink_create() @@ -964,7 +973,22 @@ void phylink_start(struct phylink *pl) clear_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state); phylink_run_resolve(pl); - if (pl->link_an_mode == MLO_AN_FIXED && !IS_ERR(pl->link_gpio)) + if (pl->link_an_mode == MLO_AN_FIXED && pl->link_gpio) { + int irq = gpiod_to_irq(pl->link_gpio); + + if (irq > 0) { + if (!request_irq(irq, phylink_link_handler, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, + "netdev link", pl)) + pl->link_irq = irq; + else + irq = 0; + } + if (irq <= 0) + mod_timer(&pl->link_poll, jiffies + HZ); + } + if (pl->link_an_mode == MLO_AN_FIXED && pl->get_fixed_state) mod_timer(&pl->link_poll, jiffies + HZ); if (pl->sfp_bus) sfp_upstream_start(pl->sfp_bus); @@ -990,8 +1014,11 @@ void phylink_stop(struct phylink *pl) phy_stop(pl->phydev); if (pl->sfp_bus) sfp_upstream_stop(pl->sfp_bus); - if (pl->link_an_mode == MLO_AN_FIXED && !IS_ERR(pl->link_gpio)) - del_timer_sync(&pl->link_poll); + del_timer_sync(&pl->link_poll); + if (pl->link_irq) { + free_irq(pl->link_irq, pl); + pl->link_irq = 0; + } phylink_run_resolve_and_disable(pl, PHYLINK_DISABLE_STOPPED); } @@ -1395,8 +1422,8 @@ EXPORT_SYMBOL_GPL(phylink_ethtool_set_eee); * * FIXME: should deal with negotiation state too. */ -static int phylink_mii_emul_read(struct net_device *ndev, unsigned int reg, - struct phylink_link_state *state, bool aneg) +static int phylink_mii_emul_read(unsigned int reg, + struct phylink_link_state *state) { struct fixed_phy_status fs; int val; @@ -1411,8 +1438,6 @@ static int phylink_mii_emul_read(struct net_device *ndev, unsigned int reg, if (reg == MII_BMSR) { if (!state->an_complete) val &= ~BMSR_ANEGCOMPLETE; - if (!aneg) - val &= ~BMSR_ANEGCAPABLE; } return val; } @@ -1508,8 +1533,7 @@ static int phylink_mii_read(struct phylink *pl, unsigned int phy_id, case MLO_AN_FIXED: if (phy_id == 0) { phylink_get_fixed_state(pl, &state); - val = phylink_mii_emul_read(pl->netdev, reg, &state, - true); + val = phylink_mii_emul_read(reg, &state); } break; @@ -1522,8 +1546,7 @@ static int phylink_mii_read(struct phylink *pl, unsigned int phy_id, if (val < 0) return val; - val = phylink_mii_emul_read(pl->netdev, reg, &state, - true); + val = phylink_mii_emul_read(reg, &state); } break; } @@ -1626,6 +1649,20 @@ int phylink_mii_ioctl(struct phylink *pl, struct ifreq *ifr, int cmd) } EXPORT_SYMBOL_GPL(phylink_mii_ioctl); +static void phylink_sfp_attach(void *upstream, struct sfp_bus *bus) +{ + struct phylink *pl = upstream; + + pl->netdev->sfp_bus = bus; +} + +static void phylink_sfp_detach(void *upstream, struct sfp_bus *bus) +{ + struct phylink *pl = upstream; + + pl->netdev->sfp_bus = NULL; +} + static int phylink_sfp_module_insert(void *upstream, const struct sfp_eeprom_id *id) { @@ -1744,6 +1781,8 @@ static void phylink_sfp_disconnect_phy(void *upstream) } static const struct sfp_upstream_ops sfp_phylink_ops = { + .attach = phylink_sfp_attach, + .detach = phylink_sfp_detach, .module_insert = phylink_sfp_module_insert, .link_up = phylink_sfp_link_up, .link_down = phylink_sfp_link_down, diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c index e9c187946cca..b23fc41896ef 100644 --- a/drivers/net/phy/sfp-bus.c +++ b/drivers/net/phy/sfp-bus.c @@ -24,7 +24,6 @@ struct sfp_bus { const struct sfp_upstream_ops *upstream_ops; void *upstream; - struct net_device *netdev; struct phy_device *phydev; bool registered; @@ -351,7 +350,7 @@ static int sfp_register_bus(struct sfp_bus *bus) bus->socket_ops->attach(bus->sfp); if (bus->started) bus->socket_ops->start(bus->sfp); - bus->netdev->sfp_bus = bus; + bus->upstream_ops->attach(bus->upstream, bus); bus->registered = true; return 0; } @@ -360,8 +359,8 @@ static void sfp_unregister_bus(struct sfp_bus *bus) { const struct sfp_upstream_ops *ops = bus->upstream_ops; - bus->netdev->sfp_bus = NULL; if (bus->registered) { + bus->upstream_ops->detach(bus->upstream, bus); if (bus->started) bus->socket_ops->stop(bus->sfp); bus->socket_ops->detach(bus->sfp); @@ -443,13 +442,11 @@ static void sfp_upstream_clear(struct sfp_bus *bus) { bus->upstream_ops = NULL; bus->upstream = NULL; - bus->netdev = NULL; } /** * sfp_register_upstream() - Register the neighbouring device * @fwnode: firmware node for the SFP bus - * @ndev: network device associated with the interface * @upstream: the upstream private data * @ops: the upstream's &struct sfp_upstream_ops * @@ -460,7 +457,7 @@ static void sfp_upstream_clear(struct sfp_bus *bus) * On error, returns %NULL. */ struct sfp_bus *sfp_register_upstream(struct fwnode_handle *fwnode, - struct net_device *ndev, void *upstream, + void *upstream, const struct sfp_upstream_ops *ops) { struct sfp_bus *bus = sfp_bus_get(fwnode); @@ -470,7 +467,6 @@ struct sfp_bus *sfp_register_upstream(struct fwnode_handle *fwnode, rtnl_lock(); bus->upstream_ops = ops; bus->upstream = upstream; - bus->netdev = ndev; if (bus->sfp) { ret = sfp_register_bus(bus); @@ -592,7 +588,7 @@ struct sfp_bus *sfp_register_socket(struct device *dev, struct sfp *sfp, bus->sfp = sfp; bus->socket_ops = ops; - if (bus->netdev) { + if (bus->upstream_ops) { ret = sfp_register_bus(bus); if (ret) sfp_socket_clear(bus); @@ -612,7 +608,7 @@ EXPORT_SYMBOL_GPL(sfp_register_socket); void sfp_unregister_socket(struct sfp_bus *bus) { rtnl_lock(); - if (bus->netdev) + if (bus->upstream_ops) sfp_unregister_bus(bus); sfp_socket_clear(bus); rtnl_unlock(); diff --git a/include/linux/sfp.h b/include/linux/sfp.h index d9d9de3fcf8e..1c35428e98bc 100644 --- a/include/linux/sfp.h +++ b/include/linux/sfp.h @@ -464,11 +464,14 @@ enum { struct fwnode_handle; struct ethtool_eeprom; struct ethtool_modinfo; -struct net_device; struct sfp_bus; /** * struct sfp_upstream_ops - upstream operations structure + * @attach: called when the sfp socket driver is bound to the upstream + * (mandatory). + * @detach: called when the sfp socket driver is unbound from the upstream + * (mandatory). * @module_insert: called after a module has been detected to determine * whether the module is supported for the upstream device. * @module_remove: called after the module has been removed. @@ -481,6 +484,8 @@ struct sfp_bus; * been removed. */ struct sfp_upstream_ops { + void (*attach)(void *priv, struct sfp_bus *bus); + void (*detach)(void *priv, struct sfp_bus *bus); int (*module_insert)(void *priv, const struct sfp_eeprom_id *id); void (*module_remove)(void *priv); void (*link_down)(void *priv); @@ -504,7 +509,7 @@ int sfp_get_module_eeprom(struct sfp_bus *bus, struct ethtool_eeprom *ee, void sfp_upstream_start(struct sfp_bus *bus); void sfp_upstream_stop(struct sfp_bus *bus); struct sfp_bus *sfp_register_upstream(struct fwnode_handle *fwnode, - struct net_device *ndev, void *upstream, + void *upstream, const struct sfp_upstream_ops *ops); void sfp_unregister_upstream(struct sfp_bus *bus); #else @@ -549,8 +554,7 @@ static inline void sfp_upstream_stop(struct sfp_bus *bus) } static inline struct sfp_bus *sfp_register_upstream( - struct fwnode_handle *fwnode, - struct net_device *ndev, void *upstream, + struct fwnode_handle *fwnode, void *upstream, const struct sfp_upstream_ops *ops) { return (struct sfp_bus *)-1; |