You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
184 lines
5.5 KiB
Diff
184 lines
5.5 KiB
Diff
From eb156db588ac583cdae7b91eaac9c0ad3a358e63 Mon Sep 17 00:00:00 2001
|
|
From: Russell King <rmk+kernel@armlinux.org.uk>
|
|
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 <rmk+kernel@armlinux.org.uk>
|
|
---
|
|
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
|
|
@@ -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);
|
|
@@ -903,6 +907,9 @@ void phy_state_machine(struct work_struc
|
|
|
|
old_state = phydev->state;
|
|
|
|
+ if (phydev->sfp_bus)
|
|
+ sfp_upstream_start(phydev->sfp_bus);
|
|
+
|
|
switch (phydev->state) {
|
|
case PHY_DOWN:
|
|
case PHY_READY:
|
|
--- a/drivers/net/phy/phy_device.c
|
|
+++ b/drivers/net/phy/phy_device.c
|
|
@@ -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>
|
|
@@ -1178,6 +1179,65 @@ phy_standalone_show(struct device *dev,
|
|
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
|
|
* @phydev: Pointer to phy_device to attach
|
|
@@ -1254,6 +1314,9 @@ int phy_attach_direct(struct net_device
|
|
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
|
|
@@ -2282,6 +2345,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
|
|
@@ -203,6 +203,8 @@ static inline const char *phy_modes(phy_
|
|
|
|
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 *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);
|