Browse Source

generic: 6.1: backport qca808x LED support patch

Backport qca808x LED support patch merged upstream needed to drop
handling of it from the SSDK for ipq807x target.

Signed-off-by: Christian Marangi <[email protected]>
Christian Marangi 1 year ago
parent
commit
2c39269b6e

+ 408 - 0
target/linux/generic/backport-6.1/712-v6.9-net-phy-at803x-add-LED-support-for-qca808x.patch

@@ -0,0 +1,408 @@
+From 7196062b64ee470b91015f3d2e82d225948258ea Mon Sep 17 00:00:00 2001
+From: Christian Marangi <[email protected]>
+Date: Thu, 25 Jan 2024 21:37:01 +0100
+Subject: [PATCH 5/5] net: phy: at803x: add LED support for qca808x
+
+Add LED support for QCA8081 PHY.
+
+Documentation for this LEDs PHY is very scarce even with NDA access
+to Documentation for OEMs. Only the blink pattern are documented and are
+very confusing most of the time. No documentation is present about
+forcing the LED on/off or to always blink.
+
+Those settings were reversed by poking the regs and trying to find the
+correct bits to trigger these modes. Some bits mode are not clear and
+maybe the documentation option are not 100% correct. For the sake of LED
+support the reversed option are enough to add support for current LED
+APIs.
+
+Supported HW control modes are:
+- tx
+- rx
+- link_10
+- link_100
+- link_1000
+- link_2500
+- half_duplex
+- full_duplex
+
+Also add support for LED polarity set to set LED polarity to active
+high or low. QSDK sets this value to high by default but PHY reset value
+doesn't have this enabled by default.
+
+QSDK also sets 2 additional bits but their usage is not clear, info about
+this is added in the header. It was verified that for correct function
+of the LED if active high is needed, only BIT 6 is needed.
+
+Signed-off-by: Christian Marangi <[email protected]>
+Reviewed-by: Andrew Lunn <[email protected]>
+Link: https://lore.kernel.org/r/[email protected]
+Signed-off-by: Jakub Kicinski <[email protected]>
+---
+ drivers/net/phy/at803x.c | 327 +++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 327 insertions(+)
+
+--- a/drivers/net/phy/at803x.c
++++ b/drivers/net/phy/at803x.c
+@@ -301,6 +301,87 @@
+ /* Added for reference of existence but should be handled by wait_for_completion already */
+ #define QCA808X_CDT_STATUS_STAT_BUSY		(BIT(1) | BIT(3))
+ 
++#define QCA808X_MMD7_LED_GLOBAL			0x8073
++#define QCA808X_LED_BLINK_1			GENMASK(11, 6)
++#define QCA808X_LED_BLINK_2			GENMASK(5, 0)
++/* Values are the same for both BLINK_1 and BLINK_2 */
++#define QCA808X_LED_BLINK_FREQ_MASK		GENMASK(5, 3)
++#define QCA808X_LED_BLINK_FREQ_2HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0)
++#define QCA808X_LED_BLINK_FREQ_4HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1)
++#define QCA808X_LED_BLINK_FREQ_8HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2)
++#define QCA808X_LED_BLINK_FREQ_16HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3)
++#define QCA808X_LED_BLINK_FREQ_32HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4)
++#define QCA808X_LED_BLINK_FREQ_64HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5)
++#define QCA808X_LED_BLINK_FREQ_128HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6)
++#define QCA808X_LED_BLINK_FREQ_256HZ		FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7)
++#define QCA808X_LED_BLINK_DUTY_MASK		GENMASK(2, 0)
++#define QCA808X_LED_BLINK_DUTY_50_50		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0)
++#define QCA808X_LED_BLINK_DUTY_75_25		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1)
++#define QCA808X_LED_BLINK_DUTY_25_75		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2)
++#define QCA808X_LED_BLINK_DUTY_33_67		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3)
++#define QCA808X_LED_BLINK_DUTY_67_33		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4)
++#define QCA808X_LED_BLINK_DUTY_17_83		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5)
++#define QCA808X_LED_BLINK_DUTY_83_17		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6)
++#define QCA808X_LED_BLINK_DUTY_8_92		FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7)
++
++#define QCA808X_MMD7_LED2_CTRL			0x8074
++#define QCA808X_MMD7_LED2_FORCE_CTRL		0x8075
++#define QCA808X_MMD7_LED1_CTRL			0x8076
++#define QCA808X_MMD7_LED1_FORCE_CTRL		0x8077
++#define QCA808X_MMD7_LED0_CTRL			0x8078
++#define QCA808X_MMD7_LED_CTRL(x)		(0x8078 - ((x) * 2))
++
++/* LED hw control pattern is the same for every LED */
++#define QCA808X_LED_PATTERN_MASK		GENMASK(15, 0)
++#define QCA808X_LED_SPEED2500_ON		BIT(15)
++#define QCA808X_LED_SPEED2500_BLINK		BIT(14)
++/* Follow blink trigger even if duplex or speed condition doesn't match */
++#define QCA808X_LED_BLINK_CHECK_BYPASS		BIT(13)
++#define QCA808X_LED_FULL_DUPLEX_ON		BIT(12)
++#define QCA808X_LED_HALF_DUPLEX_ON		BIT(11)
++#define QCA808X_LED_TX_BLINK			BIT(10)
++#define QCA808X_LED_RX_BLINK			BIT(9)
++#define QCA808X_LED_TX_ON_10MS			BIT(8)
++#define QCA808X_LED_RX_ON_10MS			BIT(7)
++#define QCA808X_LED_SPEED1000_ON		BIT(6)
++#define QCA808X_LED_SPEED100_ON			BIT(5)
++#define QCA808X_LED_SPEED10_ON			BIT(4)
++#define QCA808X_LED_COLLISION_BLINK		BIT(3)
++#define QCA808X_LED_SPEED1000_BLINK		BIT(2)
++#define QCA808X_LED_SPEED100_BLINK		BIT(1)
++#define QCA808X_LED_SPEED10_BLINK		BIT(0)
++
++#define QCA808X_MMD7_LED0_FORCE_CTRL		0x8079
++#define QCA808X_MMD7_LED_FORCE_CTRL(x)		(0x8079 - ((x) * 2))
++
++/* LED force ctrl is the same for every LED
++ * No documentation exist for this, not even internal one
++ * with NDA as QCOM gives only info about configuring
++ * hw control pattern rules and doesn't indicate any way
++ * to force the LED to specific mode.
++ * These define comes from reverse and testing and maybe
++ * lack of some info or some info are not entirely correct.
++ * For the basic LED control and hw control these finding
++ * are enough to support LED control in all the required APIs.
++ *
++ * On doing some comparison with implementation with qca807x,
++ * it was found that it's 1:1 equal to it and confirms all the
++ * reverse done. It was also found further specification with the
++ * force mode and the blink modes.
++ */
++#define QCA808X_LED_FORCE_EN			BIT(15)
++#define QCA808X_LED_FORCE_MODE_MASK		GENMASK(14, 13)
++#define QCA808X_LED_FORCE_BLINK_1		FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3)
++#define QCA808X_LED_FORCE_BLINK_2		FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2)
++#define QCA808X_LED_FORCE_ON			FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1)
++#define QCA808X_LED_FORCE_OFF			FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0)
++
++#define QCA808X_MMD7_LED_POLARITY_CTRL		0x901a
++/* QSDK sets by default 0x46 to this reg that sets BIT 6 for
++ * LED to active high. It's not clear what BIT 3 and BIT 4 does.
++ */
++#define QCA808X_LED_ACTIVE_HIGH			BIT(6)
++
+ /* QCA808X 1G chip type */
+ #define QCA808X_PHY_MMD7_CHIP_TYPE		0x901d
+ #define QCA808X_PHY_CHIP_TYPE_1G		BIT(0)
+@@ -346,6 +427,7 @@ struct at803x_priv {
+ 	struct regulator_dev *vddio_rdev;
+ 	struct regulator_dev *vddh_rdev;
+ 	u64 stats[ARRAY_SIZE(qca83xx_hw_stats)];
++	int led_polarity_mode;
+ };
+ 
+ struct at803x_context {
+@@ -706,6 +788,9 @@ static int at803x_probe(struct phy_devic
+ 	if (!priv)
+ 		return -ENOMEM;
+ 
++	/* Init LED polarity mode to -1 */
++	priv->led_polarity_mode = -1;
++
+ 	phydev->priv = priv;
+ 
+ 	ret = at803x_parse_dt(phydev);
+@@ -2235,6 +2320,242 @@ static void qca808x_link_change_notify(s
+ 				   phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);
+ }
+ 
++static int qca808x_led_parse_netdev(struct phy_device *phydev, unsigned long rules,
++				    u16 *offload_trigger)
++{
++	/* Parsing specific to netdev trigger */
++	if (test_bit(TRIGGER_NETDEV_TX, &rules))
++		*offload_trigger |= QCA808X_LED_TX_BLINK;
++	if (test_bit(TRIGGER_NETDEV_RX, &rules))
++		*offload_trigger |= QCA808X_LED_RX_BLINK;
++	if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
++		*offload_trigger |= QCA808X_LED_SPEED10_ON;
++	if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
++		*offload_trigger |= QCA808X_LED_SPEED100_ON;
++	if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
++		*offload_trigger |= QCA808X_LED_SPEED1000_ON;
++	if (test_bit(TRIGGER_NETDEV_LINK_2500, &rules))
++		*offload_trigger |= QCA808X_LED_SPEED2500_ON;
++	if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))
++		*offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON;
++	if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))
++		*offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON;
++
++	if (rules && !*offload_trigger)
++		return -EOPNOTSUPP;
++
++	/* Enable BLINK_CHECK_BYPASS by default to make the LED
++	 * blink even with duplex or speed mode not enabled.
++	 */
++	*offload_trigger |= QCA808X_LED_BLINK_CHECK_BYPASS;
++
++	return 0;
++}
++
++static int qca808x_led_hw_control_enable(struct phy_device *phydev, u8 index)
++{
++	u16 reg;
++
++	if (index > 2)
++		return -EINVAL;
++
++	reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
++
++	return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
++				  QCA808X_LED_FORCE_EN);
++}
++
++static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index,
++				       unsigned long rules)
++{
++	u16 offload_trigger = 0;
++
++	if (index > 2)
++		return -EINVAL;
++
++	return qca808x_led_parse_netdev(phydev, rules, &offload_trigger);
++}
++
++static int qca808x_led_hw_control_set(struct phy_device *phydev, u8 index,
++				      unsigned long rules)
++{
++	u16 reg, offload_trigger = 0;
++	int ret;
++
++	if (index > 2)
++		return -EINVAL;
++
++	reg = QCA808X_MMD7_LED_CTRL(index);
++
++	ret = qca808x_led_parse_netdev(phydev, rules, &offload_trigger);
++	if (ret)
++		return ret;
++
++	ret = qca808x_led_hw_control_enable(phydev, index);
++	if (ret)
++		return ret;
++
++	return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
++			      QCA808X_LED_PATTERN_MASK,
++			      offload_trigger);
++}
++
++static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index)
++{
++	u16 reg;
++	int val;
++
++	if (index > 2)
++		return false;
++
++	reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
++
++	val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
++
++	return !(val & QCA808X_LED_FORCE_EN);
++}
++
++static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index,
++				      unsigned long *rules)
++{
++	u16 reg;
++	int val;
++
++	if (index > 2)
++		return -EINVAL;
++
++	/* Check if we have hw control enabled */
++	if (qca808x_led_hw_control_status(phydev, index))
++		return -EINVAL;
++
++	reg = QCA808X_MMD7_LED_CTRL(index);
++
++	val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
++	if (val & QCA808X_LED_TX_BLINK)
++		set_bit(TRIGGER_NETDEV_TX, rules);
++	if (val & QCA808X_LED_RX_BLINK)
++		set_bit(TRIGGER_NETDEV_RX, rules);
++	if (val & QCA808X_LED_SPEED10_ON)
++		set_bit(TRIGGER_NETDEV_LINK_10, rules);
++	if (val & QCA808X_LED_SPEED100_ON)
++		set_bit(TRIGGER_NETDEV_LINK_100, rules);
++	if (val & QCA808X_LED_SPEED1000_ON)
++		set_bit(TRIGGER_NETDEV_LINK_1000, rules);
++	if (val & QCA808X_LED_SPEED2500_ON)
++		set_bit(TRIGGER_NETDEV_LINK_2500, rules);
++	if (val & QCA808X_LED_HALF_DUPLEX_ON)
++		set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules);
++	if (val & QCA808X_LED_FULL_DUPLEX_ON)
++		set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules);
++
++	return 0;
++}
++
++static int qca808x_led_hw_control_reset(struct phy_device *phydev, u8 index)
++{
++	u16 reg;
++
++	if (index > 2)
++		return -EINVAL;
++
++	reg = QCA808X_MMD7_LED_CTRL(index);
++
++	return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
++				  QCA808X_LED_PATTERN_MASK);
++}
++
++static int qca808x_led_brightness_set(struct phy_device *phydev,
++				      u8 index, enum led_brightness value)
++{
++	u16 reg;
++	int ret;
++
++	if (index > 2)
++		return -EINVAL;
++
++	if (!value) {
++		ret = qca808x_led_hw_control_reset(phydev, index);
++		if (ret)
++			return ret;
++	}
++
++	reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
++
++	return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
++			      QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
++			      QCA808X_LED_FORCE_EN | value ? QCA808X_LED_FORCE_ON :
++							     QCA808X_LED_FORCE_OFF);
++}
++
++static int qca808x_led_blink_set(struct phy_device *phydev, u8 index,
++				 unsigned long *delay_on,
++				 unsigned long *delay_off)
++{
++	int ret;
++	u16 reg;
++
++	if (index > 2)
++		return -EINVAL;
++
++	reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
++
++	/* Set blink to 50% off, 50% on at 4Hz by default */
++	ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL,
++			     QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK,
++			     QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50);
++	if (ret)
++		return ret;
++
++	/* We use BLINK_1 for normal blinking */
++	ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
++			     QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
++			     QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1);
++	if (ret)
++		return ret;
++
++	/* We set blink to 4Hz, aka 250ms */
++	*delay_on = 250 / 2;
++	*delay_off = 250 / 2;
++
++	return 0;
++}
++
++static int qca808x_led_polarity_set(struct phy_device *phydev, int index,
++				    unsigned long modes)
++{
++	struct at803x_priv *priv = phydev->priv;
++	bool active_low = false;
++	u32 mode;
++
++	for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) {
++		switch (mode) {
++		case PHY_LED_ACTIVE_LOW:
++			active_low = true;
++			break;
++		default:
++			return -EINVAL;
++		}
++	}
++
++	/* PHY polarity is global and can't be set per LED.
++	 * To detect this, check if last requested polarity mode
++	 * match the new one.
++	 */
++	if (priv->led_polarity_mode >= 0 &&
++	    priv->led_polarity_mode != active_low) {
++		phydev_err(phydev, "PHY polarity is global. Mismatched polarity on different LED\n");
++		return -EINVAL;
++	}
++
++	/* Save the last PHY polarity mode */
++	priv->led_polarity_mode = active_low;
++
++	return phy_modify_mmd(phydev, MDIO_MMD_AN,
++			      QCA808X_MMD7_LED_POLARITY_CTRL,
++			      QCA808X_LED_ACTIVE_HIGH,
++			      active_low ? 0 : QCA808X_LED_ACTIVE_HIGH);
++}
++
+ static struct phy_driver at803x_driver[] = {
+ {
+ 	/* Qualcomm Atheros AR8035 */
+@@ -2411,6 +2732,12 @@ static struct phy_driver at803x_driver[]
+ 	.cable_test_start	= qca808x_cable_test_start,
+ 	.cable_test_get_status	= qca808x_cable_test_get_status,
+ 	.link_change_notify	= qca808x_link_change_notify,
++	.led_brightness_set	= qca808x_led_brightness_set,
++	.led_blink_set		= qca808x_led_blink_set,
++	.led_hw_is_supported	= qca808x_led_hw_is_supported,
++	.led_hw_control_set	= qca808x_led_hw_control_set,
++	.led_hw_control_get	= qca808x_led_hw_control_get,
++	.led_polarity_set	= qca808x_led_polarity_set,
+ }, };
+ 
+ module_phy_driver(at803x_driver);

+ 98 - 0
target/linux/generic/backport-6.1/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch

@@ -0,0 +1,98 @@
+From 7ae215ee7bb855f13c80565470fc7f67db4ba82f Mon Sep 17 00:00:00 2001
+From: Christian Marangi <[email protected]>
+Date: Thu, 25 Jan 2024 21:36:59 +0100
+Subject: [PATCH 3/5] net: phy: add support for PHY LEDs polarity modes
+
+Add support for PHY LEDs polarity modes. Some PHY require LED to be set
+to active low to be turned ON. Adds support for this by declaring
+active-low property in DT.
+
+PHY driver needs to declare .led_polarity_set() to configure LED
+polarity modes. Function will pass the index with the LED index and a
+bitmap with all the required modes to set.
+
+Current supported modes are:
+- active-low with the flag PHY_LED_ACTIVE_LOW. LED is set to active-low
+  to turn it ON.
+- inactive-high-impedance with the flag PHY_LED_INACTIVE_HIGH_IMPEDANCE.
+  LED is set to high impedance to turn it OFF.
+
+Signed-off-by: Christian Marangi <[email protected]>
+Reviewed-by: Andrew Lunn <[email protected]>
+Link: https://lore.kernel.org/r/[email protected]
+Signed-off-by: Jakub Kicinski <[email protected]>
+---
+ drivers/net/phy/phy_device.c | 16 ++++++++++++++++
+ include/linux/phy.h          | 22 ++++++++++++++++++++++
+ 2 files changed, 38 insertions(+)
+
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -3034,6 +3034,7 @@ static int of_phy_led(struct phy_device
+ 	struct device *dev = &phydev->mdio.dev;
+ 	struct led_init_data init_data = {};
+ 	struct led_classdev *cdev;
++	unsigned long modes = 0;
+ 	struct phy_led *phyled;
+ 	u32 index;
+ 	int err;
+@@ -3051,6 +3052,21 @@ static int of_phy_led(struct phy_device
+ 	if (index > U8_MAX)
+ 		return -EINVAL;
+ 
++	if (of_property_read_bool(led, "active-low"))
++		set_bit(PHY_LED_ACTIVE_LOW, &modes);
++	if (of_property_read_bool(led, "inactive-high-impedance"))
++		set_bit(PHY_LED_INACTIVE_HIGH_IMPEDANCE, &modes);
++
++	if (modes) {
++		/* Return error if asked to set polarity modes but not supported */
++		if (!phydev->drv->led_polarity_set)
++			return -EINVAL;
++
++		err = phydev->drv->led_polarity_set(phydev, index, modes);
++		if (err)
++			return err;
++	}
++
+ 	phyled->index = index;
+ 	if (phydev->drv->led_brightness_set)
+ 		cdev->brightness_set_blocking = phy_led_set_brightness;
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -787,6 +787,15 @@ struct phy_led {
+ 
+ #define to_phy_led(d) container_of(d, struct phy_led, led_cdev)
+ 
++/* Modes for PHY LED configuration */
++enum phy_led_modes {
++	PHY_LED_ACTIVE_LOW = 0,
++	PHY_LED_INACTIVE_HIGH_IMPEDANCE = 1,
++
++	/* keep it last */
++	__PHY_LED_MODES_NUM,
++};
++
+ /**
+  * struct phy_driver - Driver structure for a particular PHY type
+  *
+@@ -1055,6 +1064,19 @@ struct phy_driver {
+ 	int (*led_hw_control_get)(struct phy_device *dev, u8 index,
+ 				  unsigned long *rules);
+ 
++	/**
++	 * @led_polarity_set: Set the LED polarity modes
++	 * @dev: PHY device which has the LED
++	 * @index: Which LED of the PHY device
++	 * @modes: bitmap of LED polarity modes
++	 *
++	 * Configure LED with all the required polarity modes in @modes
++	 * to make it correctly turn ON or OFF.
++	 *
++	 * Returns 0, or an error code.
++	 */
++	int (*led_polarity_set)(struct phy_device *dev, int index,
++				unsigned long modes);
+ };
+ #define to_phy_driver(d) container_of(to_mdio_common_driver(d),		\
+ 				      struct phy_driver, mdiodrv)

+ 1 - 1
target/linux/generic/pending-6.1/703-phy-add-detach-callback-to-struct-phy_driver.patch

@@ -23,7 +23,7 @@ Signed-off-by: Gabor Juhos <[email protected]>
  			sysfs_remove_link(&dev->dev.kobj, "phydev");
  			sysfs_remove_link(&dev->dev.kobj, "phydev");
 --- a/include/linux/phy.h
 --- a/include/linux/phy.h
 +++ b/include/linux/phy.h
 +++ b/include/linux/phy.h
-@@ -887,6 +887,12 @@ struct phy_driver {
+@@ -896,6 +896,12 @@ struct phy_driver {
  	/** @handle_interrupt: Override default interrupt handling */
  	/** @handle_interrupt: Override default interrupt handling */
  	irqreturn_t (*handle_interrupt)(struct phy_device *phydev);
  	irqreturn_t (*handle_interrupt)(struct phy_device *phydev);