mxl led mxl-led-kernel
authorDavid Bauer <mail@david-bauer.net>
Mon, 11 Dec 2023 18:06:43 +0000 (19:06 +0100)
committerDavid Bauer <mail@david-bauer.net>
Mon, 11 Dec 2023 18:06:43 +0000 (19:06 +0100)
target/linux/mediatek/dts/mt7981b-zyxel-nwa50ax-pro.dts
target/linux/mediatek/patches-6.1/990-mxl-led.patch [new file with mode: 0644]

index 54df8c054ace3a6b477074a7a38d70c7804bbc43..732487d4e5b17edfd35106004c3b6fa5b1ffa72e 100644 (file)
 
                /* LED0: Amber ; LED1: nc ; LED2: nc ; LED3: Green */
                mxl,led-config = <0x3b0 0x0 0x0 0x3c0>;
+
+               leds {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       led@0 {
+                               reg = <0>;
+                               label = "led0";
+                       };
+
+                       led@1 {
+                               reg = <1>;
+                               label = "led1";
+                       };
+
+                       led@2 {
+                               reg = <2>;
+                               label = "led2";
+                       };
+
+                       led@3 {
+                               reg = <3>;
+                               label = "led3";
+                       };
+               };
        };
 };
 
diff --git a/target/linux/mediatek/patches-6.1/990-mxl-led.patch b/target/linux/mediatek/patches-6.1/990-mxl-led.patch
new file mode 100644 (file)
index 0000000..ea8d44f
--- /dev/null
@@ -0,0 +1,268 @@
+From 6dce7d60705040a71214dd7d03f7652415f9d18c Mon Sep 17 00:00:00 2001
+From: David Bauer <mail@david-bauer.net>
+Date: Thu, 7 Dec 2023 04:00:19 +0100
+Subject: [PATCH] net: phy: mxl-gpy: add LED configuration interface
+
+Signed-off-by: David Bauer <mail@david-bauer.net>
+---
+ drivers/net/phy/mxl-gpy.c | 209 ++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 209 insertions(+)
+
+diff --git a/drivers/net/phy/mxl-gpy.c b/drivers/net/phy/mxl-gpy.c
+index ea1073adc5a1..ce916e52c8a3 100644
+--- a/drivers/net/phy/mxl-gpy.c
++++ b/drivers/net/phy/mxl-gpy.c
+@@ -38,6 +38,7 @@
+ #define PHY_MIISTAT           0x18    /* MII state */
+ #define PHY_IMASK             0x19    /* interrupt mask */
+ #define PHY_ISTAT             0x1A    /* interrupt status */
++#define PHY_LED                       0x1B    /* LED control */
+ #define PHY_FWV                       0x1E    /* firmware version */
+ #define PHY_MIISTAT_SPD_MASK  GENMASK(2, 0)
+@@ -61,6 +62,9 @@
+                                PHY_IMASK_ADSC | \
+                                PHY_IMASK_ANC)
++#define PHY_LED_DA_MASK(x)    BIT(x)
++#define PHY_LED_EN_MASK(x)    BIT((x) + 8)
++
+ #define PHY_FWV_REL_MASK      BIT(15)
+ #define PHY_FWV_MAJOR_MASK    GENMASK(11, 8)
+ #define PHY_FWV_MINOR_MASK    GENMASK(7, 0)
+@@ -72,6 +76,28 @@
+ #define PHY_MDI_MDI_X_CD      0x1
+ #define PHY_MDI_MDI_X_CROSS   0x0
++/* LED Control */
++#include <linux/leds.h>
++#define VSPEC1_LED(x)         (1 + (x))
++#define VSPEC1_LED_BLINK_F_MASK       GENMASK(3, 0)
++#define VSPEC1_LED_CON_MASK   GENMASK(7, 4)
++#define VSPEC1_LED_PULSE_MASK GENMASK(11, 8)
++#define VSPEC1_LED_BLINK_S_MASK       GENMASK(11, 8)
++
++#define VSPEC1_LED_LINK_10    BIT(0)
++#define VSPEC1_LED_LINK_100   BIT(1)
++#define VSPEC1_LED_LINK_1000  BIT(2)
++#define VSPEC1_LED_LINK_2500  BIT(3)
++#define VSPEC1_LED_LINK_ALL   (VSPEC1_LED_LINK_10 | \
++                               VSPEC1_LED_LINK_100 | \
++                               VSPEC1_LED_LINK_1000 | \
++                               VSPEC1_LED_LINK_2500)
++
++#define VSPEC1_LED_PULSE_TXACT        BIT(0)
++#define VSPEC1_LED_PULSE_RXACT        BIT(1)
++#define VSPEC1_LED_PULSE_COL  BIT(2)
++#define VSPEC1_LED_PULSE_NO_CON       BIT(3)
++
+ /* SGMII */
+ #define VSPEC1_SGMII_CTRL     0x08
+ #define VSPEC1_SGMII_CTRL_ANEN        BIT(12)         /* Aneg enable */
+@@ -666,6 +692,181 @@ static irqreturn_t gpy_handle_interrupt(struct phy_device *phydev)
+       return IRQ_HANDLED;
+ }
++#define GPY_MAX_LED_IDX       3
++
++static void gpy_dump_reg(struct phy_device *phydev, u8 reg)
++{
++      int ret;
++
++      ret = phy_read(phydev, reg);
++      if (ret < 0)
++              return;
++
++      phydev_warn(phydev, "[%02X] = %04X\n", reg, ret);
++}
++
++static void gpy_dump_mmd_reg(struct phy_device *phydev, u8 mmd, u16 reg)
++{
++      int ret;
++
++      ret = phy_read_mmd(phydev, mmd, reg);
++      if (ret < 0)
++              return;
++
++      phydev_warn(phydev, "[%02X:%04X] = %04X\n", mmd, reg, ret);
++}
++
++static int gpy_led_brightness_set(struct phy_device *phydev,
++                                u8 index, enum led_brightness value)
++{
++      u16 mask;
++      u16 set;
++      int ret;
++
++      if (index > GPY_MAX_LED_IDX)
++              return -EINVAL;
++
++      /* Need to disable integrated function handling */
++      ret = phy_modify(phydev, PHY_LED, PHY_LED_EN_MASK(index), 0);
++      if (ret < 0)
++              return ret;
++
++      mask = PHY_LED_DA_MASK(index) | PHY_LED_EN_MASK(index);
++      set = (value != LED_OFF) ? PHY_LED_DA_MASK(index) : 0;
++
++      /* Set LED state */
++      ret = phy_modify(phydev, PHY_LED, mask, set);
++      if (ret < 0)
++              return ret;
++
++      gpy_dump_reg(phydev, PHY_LED);
++      return ret;
++}
++
++static const unsigned long supported_rules = BIT(TRIGGER_NETDEV_LINK) |
++                                           BIT(TRIGGER_NETDEV_LINK_10) |
++                                           BIT(TRIGGER_NETDEV_LINK_100) |
++                                           BIT(TRIGGER_NETDEV_LINK_1000) |
++                                           BIT(TRIGGER_NETDEV_TX) |
++                                           BIT(TRIGGER_NETDEV_RX);
++
++static int gpy_led_hw_control_set(struct phy_device *phydev, u8 index,
++                                unsigned long rules)
++{
++      int ret;
++      u16 reg = 0;
++
++      if (index > GPY_MAX_LED_IDX)
++              return -EINVAL;
++
++      /* Disable HW control on unsupported modes */
++      if (!(rules & supported_rules))
++              return phy_clear_bits(phydev, PHY_LED, PHY_LED_EN_MASK(index));
++
++      /* Enable HW control on supported modes */
++      ret = phy_set_bits(phydev, PHY_LED, PHY_LED_EN_MASK(index));
++      if (ret)
++              return ret;
++
++      /* Link state specific to link speed */
++      if (rules & BIT(TRIGGER_NETDEV_LINK_10))
++              reg |= FIELD_PREP(VSPEC1_LED_CON_MASK, VSPEC1_LED_LINK_10);
++
++      if (rules & BIT(TRIGGER_NETDEV_LINK_100))
++              reg |= FIELD_PREP(VSPEC1_LED_CON_MASK, VSPEC1_LED_LINK_100);
++
++      if (rules & BIT(TRIGGER_NETDEV_LINK_1000))
++              reg |= FIELD_PREP(VSPEC1_LED_CON_MASK, VSPEC1_LED_LINK_1000);
++
++      /* Link state regardless of link speed */
++      if (rules & BIT(TRIGGER_NETDEV_LINK) &&
++          !(rules & (BIT(TRIGGER_NETDEV_LINK_10) |
++                     BIT(TRIGGER_NETDEV_LINK_100) |
++                     BIT(TRIGGER_NETDEV_LINK_1000))))
++              reg |= FIELD_PREP(VSPEC1_LED_CON_MASK, VSPEC1_LED_LINK_ALL);
++
++      /* Pulse on TX */
++      if (rules & BIT(TRIGGER_NETDEV_TX))
++              reg |= FIELD_PREP(VSPEC1_LED_PULSE_MASK,
++                                VSPEC1_LED_PULSE_TXACT);
++
++      /* Pulse on RX */
++      if (rules & BIT(TRIGGER_NETDEV_RX))
++              reg |= FIELD_PREP(VSPEC1_LED_PULSE_MASK,
++                                VSPEC1_LED_PULSE_RXACT);
++
++      ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_LED(index), reg);
++      if (ret < 0)
++              return ret;
++
++      gpy_dump_mmd_reg(phydev, MDIO_MMD_VEND1, VSPEC1_LED(index));
++      return ret;
++}
++
++static int gpy_led_hw_control_get(struct phy_device *phydev, u8 index,
++                                unsigned long *rules)
++{
++      int reg;
++
++      if (index > GPY_MAX_LED_IDX)
++              return -EINVAL;
++
++      reg = phy_read(phydev, PHY_LED);
++      if (reg < 0)
++              return reg;
++
++      *rules = 0;
++      if (!(reg & PHY_LED_EN_MASK(index)))
++              return 0;
++
++      reg = phy_read_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_LED(index));
++      if (reg < 0)
++              return reg;
++
++      /* Link state specific to link speed */
++      if (reg & FIELD_PREP(VSPEC1_LED_CON_MASK, VSPEC1_LED_LINK_10))
++              *rules |= BIT(TRIGGER_NETDEV_LINK_10);
++
++      if (reg & FIELD_PREP(VSPEC1_LED_CON_MASK, VSPEC1_LED_LINK_100))
++              *rules |= BIT(TRIGGER_NETDEV_LINK_100);
++
++      if (reg & FIELD_PREP(VSPEC1_LED_CON_MASK, VSPEC1_LED_LINK_1000))
++              *rules |= BIT(TRIGGER_NETDEV_LINK_1000);
++
++      /* Link state regardless of link speed */
++      if (reg & FIELD_PREP(VSPEC1_LED_CON_MASK, VSPEC1_LED_LINK_ALL))
++              *rules |= BIT(TRIGGER_NETDEV_LINK);
++
++      /* Pulse on TX */
++      if (reg & FIELD_PREP(VSPEC1_LED_PULSE_MASK, VSPEC1_LED_PULSE_TXACT))
++              *rules |= BIT(TRIGGER_NETDEV_TX);
++
++      /* Pulse on RX */
++      if (reg & FIELD_PREP(VSPEC1_LED_PULSE_MASK, VSPEC1_LED_PULSE_RXACT))
++              *rules |= BIT(TRIGGER_NETDEV_RX);
++
++      return 0;
++};
++
++static int gpy_led_hw_is_supported(struct phy_device *phydev, u8 index,
++                                 unsigned long rules)
++{
++      if (index > GPY_MAX_LED_IDX)
++              return -EINVAL;
++
++      if (rules & ~supported_rules)
++              return -EOPNOTSUPP;
++
++      if ((rules & (BIT(TRIGGER_NETDEV_RX) | BIT(TRIGGER_NETDEV_TX))) &&
++          !(rules & (BIT(TRIGGER_NETDEV_LINK_10) |
++                     BIT(TRIGGER_NETDEV_LINK_100) |
++                     BIT(TRIGGER_NETDEV_LINK_1000) |
++                     BIT(TRIGGER_NETDEV_LINK))))
++              return -EOPNOTSUPP;
++
++      return 0;
++};
++
+ static int gpy_set_wol(struct phy_device *phydev,
+                      struct ethtool_wolinfo *wol)
+ {
+@@ -879,6 +1080,10 @@ static struct phy_driver gpy_drivers[] = {
+               .set_wol        = gpy_set_wol,
+               .get_wol        = gpy_get_wol,
+               .set_loopback   = gpy_loopback,
++              .led_brightness_set = gpy_led_brightness_set,
++              .led_hw_is_supported = gpy_led_hw_is_supported,
++              .led_hw_control_set = gpy_led_hw_control_set,
++              .led_hw_control_get = gpy_led_hw_control_get,
+       },
+       {
+               PHY_ID_MATCH_MODEL(PHY_ID_GPY211C),
+@@ -896,6 +1101,10 @@ static struct phy_driver gpy_drivers[] = {
+               .set_wol        = gpy_set_wol,
+               .get_wol        = gpy_get_wol,
+               .set_loopback   = gpy_loopback,
++              .led_brightness_set = gpy_led_brightness_set,
++              .led_hw_is_supported = gpy_led_hw_is_supported,
++              .led_hw_control_set = gpy_led_hw_control_set,
++              .led_hw_control_get = gpy_led_hw_control_get,
+       },
+       {
+               .phy_id         = PHY_ID_GPY212B,
+-- 
+2.43.0
+