generic: 6.1: backport qca808x LED support patch
authorChristian Marangi <ansuelsmth@gmail.com>
Sat, 27 Jan 2024 15:31:18 +0000 (16:31 +0100)
committerChristian Marangi <ansuelsmth@gmail.com>
Sat, 27 Jan 2024 15:32:53 +0000 (16:32 +0100)
Backport qca808x LED support patch merged upstream needed to drop
handling of it from the SSDK for ipq807x target.

Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
target/linux/generic/backport-6.1/712-v6.9-net-phy-at803x-add-LED-support-for-qca808x.patch [new file with mode: 0644]
target/linux/generic/backport-6.1/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch [new file with mode: 0644]
target/linux/generic/pending-6.1/703-phy-add-detach-callback-to-struct-phy_driver.patch

diff --git a/target/linux/generic/backport-6.1/712-v6.9-net-phy-at803x-add-LED-support-for-qca808x.patch b/target/linux/generic/backport-6.1/712-v6.9-net-phy-at803x-add-LED-support-for-qca808x.patch
new file mode 100644 (file)
index 0000000..36675e7
--- /dev/null
@@ -0,0 +1,408 @@
+From 7196062b64ee470b91015f3d2e82d225948258ea Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+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 <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/20240125203702.4552-6-ansuelsmth@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ 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);
diff --git a/target/linux/generic/backport-6.1/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch b/target/linux/generic/backport-6.1/835-v6.9-net-phy-add-support-for-PHY-LEDs-polarity-modes.patch
new file mode 100644 (file)
index 0000000..604b3db
--- /dev/null
@@ -0,0 +1,98 @@
+From 7ae215ee7bb855f13c80565470fc7f67db4ba82f Mon Sep 17 00:00:00 2001
+From: Christian Marangi <ansuelsmth@gmail.com>
+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 <ansuelsmth@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://lore.kernel.org/r/20240125203702.4552-4-ansuelsmth@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ 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)
index 425f82376bbd39784a142080dff55e9f0d968aa3..aa852e7fec936f76b78811a1e670089e2e22f77b 100644 (file)
@@ -23,7 +23,7 @@ Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
                        sysfs_remove_link(&dev->dev.kobj, "phydev");
 --- a/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 */
        irqreturn_t (*handle_interrupt)(struct phy_device *phydev);