From eb156db588ac583cdae7b91eaac9c0ad3a358e63 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 15 Sep 2019 20:05:34 +0100 Subject: [PATCH 635/660] net: phy: add core phylib sfp support Add core phylib help for supporting SFP sockets on PHYs. This provides a mechanism to inform the SFP layer about PHY up/down events, and also unregister the SFP bus when the PHY is going away. Signed-off-by: Russell King --- drivers/net/phy/phy.c | 7 ++++ drivers/net/phy/phy_device.c | 66 ++++++++++++++++++++++++++++++++++++ include/linux/phy.h | 11 ++++++ 3 files changed, 84 insertions(+) --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -871,6 +872,9 @@ void phy_stop(struct phy_device *phydev) if (phy_interrupt_is_valid(phydev)) phy_disable_interrupts(phydev); + if (phydev->sfp_bus) + sfp_upstream_stop(phydev->sfp_bus); + phydev->state = PHY_HALTED; out_unlock: @@ -899,6 +903,9 @@ void phy_start(struct phy_device *phydev mutex_lock(&phydev->lock); + if (phydev->sfp_bus) + sfp_upstream_start(phydev->sfp_bus); + switch (phydev->state) { case PHY_STARTING: phydev->state = PHY_PENDING; --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -948,6 +949,65 @@ void phy_attached_print(struct phy_devic EXPORT_SYMBOL(phy_attached_print); /** + * phy_sfp_attach - attach the SFP bus to the PHY upstream network device + * @upstream: pointer to the phy device + * @bus: sfp bus representing cage being attached + * + * This is used to fill in the sfp_upstream_ops .attach member. + */ +void phy_sfp_attach(void *upstream, struct sfp_bus *bus) +{ + struct phy_device *phydev = upstream; + + if (phydev->attached_dev) + phydev->attached_dev->sfp_bus = bus; + phydev->sfp_bus_attached = true; +} +EXPORT_SYMBOL(phy_sfp_attach); + +/** + * phy_sfp_detach - detach the SFP bus from the PHY upstream network device + * @upstream: pointer to the phy device + * @bus: sfp bus representing cage being attached + * + * This is used to fill in the sfp_upstream_ops .detach member. + */ +void phy_sfp_detach(void *upstream, struct sfp_bus *bus) +{ + struct phy_device *phydev = upstream; + + if (phydev->attached_dev) + phydev->attached_dev->sfp_bus = NULL; + phydev->sfp_bus_attached = false; +} +EXPORT_SYMBOL(phy_sfp_detach); + +/** + * phy_sfp_probe - probe for a SFP cage attached to this PHY device + * @phydev: Pointer to phy_device + * @ops: SFP's upstream operations + */ +int phy_sfp_probe(struct phy_device *phydev, + const struct sfp_upstream_ops *ops) +{ + struct sfp_bus *bus; + int ret; + + if (phydev->mdio.dev.fwnode) { + bus = sfp_bus_find_fwnode(phydev->mdio.dev.fwnode); + if (IS_ERR(bus)) + return PTR_ERR(bus); + + phydev->sfp_bus = bus; + + ret = sfp_bus_add_upstream(bus, phydev, ops); + sfp_bus_put(bus); + } + return 0; +} +EXPORT_SYMBOL(phy_sfp_probe); + +/** * phy_attach_direct - attach a network device to a given PHY device pointer * @dev: network device to attach * @phydev: Pointer to phy_device to attach @@ -1020,6 +1080,9 @@ int phy_attach_direct(struct net_device phydev->attached_dev = dev; dev->phydev = phydev; + if (phydev->sfp_bus_attached) + dev->sfp_bus = phydev->sfp_bus; + /* Some Ethernet drivers try to connect to a PHY device before * calling register_netdevice() -> netdev_register_kobject() and * does the dev->dev.kobj initialization. Here we only check for @@ -1954,6 +2017,9 @@ static int phy_remove(struct device *dev phydev->state = PHY_DOWN; mutex_unlock(&phydev->lock); + sfp_bus_del_upstream(phydev->sfp_bus); + phydev->sfp_bus = NULL; + if (phydev->drv && phydev->drv->remove) { phydev->drv->remove(phydev); --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -184,6 +184,8 @@ static inline const char *phy_modes(phy_ struct device; struct phylink; +struct sfp_bus; +struct sfp_upstream_ops; struct sk_buff; /* @@ -383,6 +385,8 @@ struct phy_c45_device_ids { * irq: IRQ number of the PHY's interrupt (-1 if none) * phy_timer: The timer for handling the state machine * phy_queue: A work_queue for the phy_mac_interrupt + * sfp_bus_attached: flag indicating whether the SFP bus has been attached + * sfp_bus: SFP bus attached to this PHY's fiber port * attached_dev: The attached enet driver's device instance ptr * adjust_link: Callback for the enet controller to respond to * changes in the link state. @@ -473,6 +477,9 @@ struct phy_device { struct mutex lock; + /* This may be modified under the rtnl lock */ + bool sfp_bus_attached; + struct sfp_bus *sfp_bus; struct phylink *phylink; struct net_device *attached_dev; @@ -1033,6 +1040,10 @@ int phy_suspend(struct phy_device *phyde int phy_resume(struct phy_device *phydev); int __phy_resume(struct phy_device *phydev); int phy_loopback(struct phy_device *phydev, bool enable); +void phy_sfp_attach(void *upstream, struct sfp_bus *bus); +void phy_sfp_detach(void *upstream, struct sfp_bus *bus); +int phy_sfp_probe(struct phy_device *phydev, + const struct sfp_upstream_ops *ops); struct phy_device *phy_attach(struct net_device *dev, const char *bus_id, phy_interface_t interface); struct phy_device *phy_find_first(struct mii_bus *bus);