forked from mirrors/linux
		
	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 <rmk+kernel@armlinux.org.uk> Reviewed-by: Andrew Lunn <andrew@lunn.ch> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
		
							parent
							
								
									fb3d8bcde6
								
							
						
					
					
						commit
						298e54fa81
					
				
					 3 changed files with 84 additions and 0 deletions
				
			
		| 
						 | 
				
			
			@ -23,6 +23,7 @@
 | 
			
		|||
#include <linux/ethtool.h>
 | 
			
		||||
#include <linux/phy.h>
 | 
			
		||||
#include <linux/phy_led_triggers.h>
 | 
			
		||||
#include <linux/sfp.h>
 | 
			
		||||
#include <linux/workqueue.h>
 | 
			
		||||
#include <linux/mdio.h>
 | 
			
		||||
#include <linux/io.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -841,6 +842,9 @@ void phy_stop(struct phy_device *phydev)
 | 
			
		|||
 | 
			
		||||
	mutex_lock(&phydev->lock);
 | 
			
		||||
 | 
			
		||||
	if (phydev->sfp_bus)
 | 
			
		||||
		sfp_upstream_stop(phydev->sfp_bus);
 | 
			
		||||
 | 
			
		||||
	phydev->state = PHY_HALTED;
 | 
			
		||||
 | 
			
		||||
	mutex_unlock(&phydev->lock);
 | 
			
		||||
| 
						 | 
				
			
			@ -875,6 +879,9 @@ void phy_start(struct phy_device *phydev)
 | 
			
		|||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (phydev->sfp_bus)
 | 
			
		||||
		sfp_upstream_start(phydev->sfp_bus);
 | 
			
		||||
 | 
			
		||||
	/* if phy was suspended, bring the physical link up again */
 | 
			
		||||
	__phy_resume(phydev);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,6 +27,7 @@
 | 
			
		|||
#include <linux/bitmap.h>
 | 
			
		||||
#include <linux/phy.h>
 | 
			
		||||
#include <linux/phy_led_triggers.h>
 | 
			
		||||
#include <linux/sfp.h>
 | 
			
		||||
#include <linux/mdio.h>
 | 
			
		||||
#include <linux/io.h>
 | 
			
		||||
#include <linux/uaccess.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -1174,6 +1175,65 @@ phy_standalone_show(struct device *dev, struct device_attribute *attr,
 | 
			
		|||
}
 | 
			
		||||
static DEVICE_ATTR_RO(phy_standalone);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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
 | 
			
		||||
| 
						 | 
				
			
			@ -1249,6 +1309,9 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
 | 
			
		|||
	if (dev) {
 | 
			
		||||
		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
 | 
			
		||||
| 
						 | 
				
			
			@ -2418,6 +2481,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);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -203,6 +203,8 @@ static inline const char *phy_modes(phy_interface_t interface)
 | 
			
		|||
 | 
			
		||||
struct device;
 | 
			
		||||
struct phylink;
 | 
			
		||||
struct sfp_bus;
 | 
			
		||||
struct sfp_upstream_ops;
 | 
			
		||||
struct sk_buff;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			@ -342,6 +344,8 @@ struct phy_c45_device_ids {
 | 
			
		|||
 * dev_flags: Device-specific flags used by the PHY driver.
 | 
			
		||||
 * irq: IRQ number of the PHY's interrupt (-1 if none)
 | 
			
		||||
 * phy_timer: The timer for handling the state machine
 | 
			
		||||
 * 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.
 | 
			
		||||
| 
						 | 
				
			
			@ -432,6 +436,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;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1020,6 +1027,10 @@ int phy_suspend(struct phy_device *phydev);
 | 
			
		|||
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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue