This makes it possible to configure the behavior of the LEDs connected
to a PHY. The LEDs are controlled by the chip, this makes it possible
to configure the behavior when the hardware should activate and
deactivate the LEDs.

Signed-off-by: Hauke Mehrtens <ha...@hauke-m.de>
---
 Documentation/devicetree/bindings/phy/phy-leds.txt |  45 ++++++
 drivers/net/phy/lantiq.c                           | 152 +++++++++++++++++++++
 include/dt-bindings/phy/phy-leds.h                 |  27 ++++
 3 files changed, 224 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/phy-leds.txt
 create mode 100644 include/dt-bindings/phy/phy-leds.h

diff --git a/Documentation/devicetree/bindings/phy/phy-leds.txt 
b/Documentation/devicetree/bindings/phy/phy-leds.txt
new file mode 100644
index 0000000..6d9f5f3
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/phy-leds.txt
@@ -0,0 +1,45 @@
+LED configuration for Ethernet phys
+
+Property names:
+       led-const-on: conditions the LED should be constant on
+       led-pules: condition the LED should be pulsed on
+       led-blink-slow: condition the LED should slowly blink
+       led-blink-fast: condition the LED should fast blink
+
+property values:
+       PHY_LED_OFF:            LED is off
+       PHY_LED_LINK10:         link is 10MBit/s
+       PHY_LED_LINK100:        link is 100MBit/s
+       PHY_LED_LINK1000:       link is 1000MBit/s
+       PHY_LED_PDOWN:          link is powered down
+       PHY_LED_EEE:            link is in EEE mode
+       PHY_LED_ANEG:           auto negotiation is running
+       PHY_LED_ABIST:          analog self testing is running
+       PHY_LED_CDIAG:          cable diagnostics is running
+       PHY_LED_COPPER:         copper interface detected
+       PHY_LED_FIBER:          fiber interface detected
+       PHY_LED_TXACT:          Transmit activity
+       PHY_LED_RXACT:          Receive activity
+       PHY_LED_COL:            Collision
+
+Example:
+
+#include <dt-bindings/phy/phy-leds.h>
+phy@0 {
+       compatible = "ethernet-phy-ieee802.3-c22";
+       reg = <0x0>;
+       #address-cells = <1>;
+       #size-cells = <0>;
+       led@0 {
+               compatible = "phy,led";
+               reg = <0>;
+               led-const-on = <(PHY_LED_LINK10 | PHY_LED_LINK100 | 
PHY_LED_LINK1000)>;
+               led-pules = <(PHY_LED_TXACT | PHY_LED_RXACT)>;
+       };
+       led@2 {
+               compatible = "phy,led";
+               reg = <2>;
+               led-blink-slow = <PHY_LED_EEE>;
+               led-blink-fast = <PHY_LED_PDOWN>;
+       };
+};
diff --git a/drivers/net/phy/lantiq.c b/drivers/net/phy/lantiq.c
index a2adc4b..2d00b31 100644
--- a/drivers/net/phy/lantiq.c
+++ b/drivers/net/phy/lantiq.c
@@ -17,6 +17,7 @@
 #include <linux/module.h>
 #include <linux/phy.h>
 #include <linux/of.h>
+#include <dt-bindings/phy/phy-leds.h>
 
 #define LANTIQ_MDIO_IMASK              0x19    /* interrupt mask */
 #define LANTIQ_MDIO_ISTAT              0x1A    /* interrupt status */
@@ -152,11 +153,158 @@
 #define PHY_ID_PHY11G_VR9                      0xD565A409
 #define PHY_ID_PHY22F_VR9                      0xD565A419
 
+static void lantiq_gphy_config_led(struct phy_device *phydev,
+                                  struct device_node *led_np)
+{
+       const __be32 *addr, *blink_fast_p, *const_on_p, *pules_p, *blink_slow_p;
+       u32 num, blink_fast, const_on, pules, blink_slow;
+       u32 ledxl;
+       u32 ledxh;
+
+       addr = of_get_property(led_np, "reg", NULL);
+       if (!addr)
+               return;
+       num = be32_to_cpu(*addr);
+
+       if (num < 0 || num > 3)
+               return;
+
+       ledxh = LANTIQ_MMD_LEDxH_BLINKF_NONE | LANTIQ_MMD_LEDxH_CON_LINK10XX;
+       blink_fast_p = of_get_property(led_np, "led-blink-fast", NULL);
+       if (blink_fast_p) {
+               ledxh &= ~LANTIQ_MMD_LEDxH_BLINKF_MASK;
+               blink_fast = be32_to_cpu(*blink_fast_p);
+               if ((blink_fast & PHY_LED_LINK10) &&
+                   (blink_fast & PHY_LED_LINK100) &&
+                   (blink_fast & PHY_LED_LINK1000)) {
+                       ledxh |= LANTIQ_MMD_LEDxH_BLINKF_LINK10XX;
+               } else if ((blink_fast & PHY_LED_LINK10) &&
+                          (blink_fast & PHY_LED_LINK1000)) {
+                       ledxh |= LANTIQ_MMD_LEDxH_BLINKF_LINK10_0;
+               } else if ((blink_fast & PHY_LED_LINK10) &&
+                          (blink_fast & PHY_LED_LINK100)) {
+                       ledxh |= LANTIQ_MMD_LEDxH_BLINKF_LINK10X;
+               } else if ((blink_fast & PHY_LED_LINK100) &&
+                          (blink_fast & PHY_LED_LINK1000)) {
+                       ledxh |= LANTIQ_MMD_LEDxH_BLINKF_LINK100X;
+               } else if (blink_fast & PHY_LED_LINK10) {
+                       ledxh |= LANTIQ_MMD_LEDxH_BLINKF_LINK10;
+               } else if (blink_fast & PHY_LED_LINK100) {
+                       ledxh |= LANTIQ_MMD_LEDxH_BLINKF_LINK100;
+               } else if (blink_fast & PHY_LED_LINK1000) {
+                       ledxh |= LANTIQ_MMD_LEDxH_BLINKF_LINK1000;
+               } else if (blink_fast & PHY_LED_PDOWN) {
+                       ledxh |= LANTIQ_MMD_LEDxH_BLINKF_PDOWN;
+               } else if (blink_fast & PHY_LED_EEE) {
+                       ledxh |= LANTIQ_MMD_LEDxH_BLINKF_EEE;
+               } else if (blink_fast & PHY_LED_ANEG) {
+                       ledxh |= LANTIQ_MMD_LEDxH_BLINKF_ANEG;
+               } else if (blink_fast & PHY_LED_ABIST) {
+                       ledxh |= LANTIQ_MMD_LEDxH_BLINKF_ABIST;
+               } else if (blink_fast & PHY_LED_CDIAG) {
+                       ledxh |= LANTIQ_MMD_LEDxH_BLINKF_CDIAG;
+               }
+       }
+       const_on_p = of_get_property(led_np, "led-const-on", NULL);
+       if (const_on_p) {
+               ledxh &= ~LANTIQ_MMD_LEDxH_CON_MASK;
+               const_on = be32_to_cpu(*const_on_p);
+               if ((const_on & PHY_LED_LINK10) &&
+                   (const_on & PHY_LED_LINK100) &&
+                   (const_on & PHY_LED_LINK1000)) {
+                       ledxh |= LANTIQ_MMD_LEDxH_CON_LINK10XX;
+               } else if ((const_on & PHY_LED_LINK10) &&
+                          (const_on & PHY_LED_LINK1000)) {
+                       ledxh |= LANTIQ_MMD_LEDxH_CON_LINK10_0;
+               } else if ((const_on & PHY_LED_LINK10) &&
+                          (const_on & PHY_LED_LINK100)) {
+                       ledxh |= LANTIQ_MMD_LEDxH_CON_LINK10X;
+               } else if ((const_on & PHY_LED_LINK100) &&
+                          (const_on & PHY_LED_LINK1000)) {
+                       ledxh |= LANTIQ_MMD_LEDxH_CON_LINK100X;
+               } else if (const_on & PHY_LED_LINK10) {
+                       ledxh |= LANTIQ_MMD_LEDxH_CON_LINK10;
+               } else if (const_on & PHY_LED_LINK100) {
+                       ledxh |= LANTIQ_MMD_LEDxH_CON_LINK100;
+               } else if (const_on & PHY_LED_LINK1000) {
+                       ledxh |= LANTIQ_MMD_LEDxH_CON_LINK1000;
+               } else if (const_on & PHY_LED_PDOWN) {
+                       ledxh |= LANTIQ_MMD_LEDxH_CON_PDOWN;
+               } else if (const_on & PHY_LED_EEE) {
+                       ledxh |= LANTIQ_MMD_LEDxH_CON_EEE;
+               } else if (const_on & PHY_LED_ANEG) {
+                       ledxh |= LANTIQ_MMD_LEDxH_CON_ANEG;
+               } else if (const_on & PHY_LED_ABIST) {
+                       ledxh |= LANTIQ_MMD_LEDxH_CON_ABIST;
+               } else if (const_on & PHY_LED_CDIAG) {
+                       ledxh |= LANTIQ_MMD_LEDxH_CON_CDIAG;
+               } else if (const_on & PHY_LED_COPPER) {
+                       ledxh |= LANTIQ_MMD_LEDxH_CON_COPPER;
+               } else if (const_on & PHY_LED_FIBER) {
+                       ledxh |= LANTIQ_MMD_LEDxH_CON_FIBER;
+               }
+       }
+       phy_write_mmd_indirect(phydev, LANTIQ_MMD_LED0H + (num * 2),
+                              MDIO_MMD_VEND2, ledxh);
+
+       ledxl = LANTIQ_MMD_LEDxL_PULSE_TXACT | LANTIQ_MMD_LEDxL_PULSE_RXACT |
+               LANTIQ_MMD_LEDxL_BLINKS_NONE;
+       pules_p = of_get_property(led_np, "led-pules", NULL);
+       if (pules_p) {
+               ledxl &= ~LANTIQ_MMD_LEDxL_PULSE_MASK;
+               pules = be32_to_cpu(*pules_p);
+               if (pules & PHY_LED_TXACT)
+                       ledxl |= LANTIQ_MMD_LEDxL_PULSE_TXACT;
+               if (pules & PHY_LED_RXACT)
+                       ledxl |= LANTIQ_MMD_LEDxL_PULSE_RXACT;
+               if (pules & PHY_LED_COL)
+                       ledxl |= LANTIQ_MMD_LEDxL_PULSE_COL;
+       }
+       blink_slow_p = of_get_property(led_np, "led-blink-slow", NULL);
+       if (blink_slow_p) {
+               ledxl &= ~LANTIQ_MMD_LEDxL_BLINKS_MASK;
+               blink_slow = be32_to_cpu(*blink_slow_p);
+               if ((blink_slow & PHY_LED_LINK10) &&
+                   (blink_slow & PHY_LED_LINK100) &&
+                   (blink_slow & PHY_LED_LINK1000)) {
+                       ledxl |= LANTIQ_MMD_LEDxL_BLINKS_LINK10XX;
+               } else if ((blink_slow & PHY_LED_LINK10) &&
+                          (blink_slow & PHY_LED_LINK1000)) {
+                       ledxl |= LANTIQ_MMD_LEDxL_BLINKS_LINK10_0;
+               } else if ((blink_slow & PHY_LED_LINK10) &&
+                          (blink_slow & PHY_LED_LINK100)) {
+                       ledxl |= LANTIQ_MMD_LEDxL_BLINKS_LINK10X;
+               } else if ((blink_slow & PHY_LED_LINK100) &&
+                          (blink_slow & PHY_LED_LINK1000)) {
+                       ledxl |= LANTIQ_MMD_LEDxL_BLINKS_LINK100X;
+               } else if (blink_slow & PHY_LED_LINK10) {
+                       ledxl |= LANTIQ_MMD_LEDxL_BLINKS_LINK10;
+               } else if (blink_slow & PHY_LED_LINK100) {
+                       ledxl |= LANTIQ_MMD_LEDxL_BLINKS_LINK100;
+               } else if (blink_slow & PHY_LED_LINK1000) {
+                       ledxl |= LANTIQ_MMD_LEDxL_BLINKS_LINK1000;
+               } else if (blink_slow & PHY_LED_PDOWN) {
+                       ledxl |= LANTIQ_MMD_LEDxL_BLINKS_PDOWN;
+               } else if (blink_slow & PHY_LED_EEE) {
+                       ledxl |= LANTIQ_MMD_LEDxL_BLINKS_EEE;
+               } else if (blink_slow & PHY_LED_ANEG) {
+                       ledxl |= LANTIQ_MMD_LEDxL_BLINKS_ANEG;
+               } else if (blink_slow & PHY_LED_ABIST) {
+                       ledxl |= LANTIQ_MMD_LEDxL_BLINKS_ABIST;
+               } else if (blink_slow & PHY_LED_CDIAG) {
+                       ledxl |= LANTIQ_MMD_LEDxL_BLINKS_CDIAG;
+               }
+       }
+       phy_write_mmd_indirect(phydev, LANTIQ_MMD_LED0L + (num * 2),
+                              MDIO_MMD_VEND2, ledxl);
+}
+
 static int lantiq_gphy_config_init(struct phy_device *phydev)
 {
        int err;
        u32 ledxh;
        u32 ledxl;
+       struct device_node *led_np;
 
        /* Mask all interrupts */
        err = phy_write(phydev, LANTIQ_MDIO_IMASK, 0);
@@ -190,6 +338,10 @@ static int lantiq_gphy_config_init(struct phy_device 
*phydev)
        phy_write_mmd_indirect(phydev, LANTIQ_MMD_LED2H, MDIO_MMD_VEND2, ledxh);
        phy_write_mmd_indirect(phydev, LANTIQ_MMD_LED2L, MDIO_MMD_VEND2, ledxl);
 
+       for_each_child_of_node(phydev->mdio.dev.of_node, led_np)
+               if (of_device_is_compatible(led_np, "phy,led"))
+                       lantiq_gphy_config_led(phydev, led_np);
+
        return 0;
 }
 
diff --git a/include/dt-bindings/phy/phy-leds.h 
b/include/dt-bindings/phy/phy-leds.h
new file mode 100644
index 0000000..6e43245
--- /dev/null
+++ b/include/dt-bindings/phy/phy-leds.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2016 Hauke Mehrtens <ha...@hauke-m.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#ifndef _DT_BINDINGS_PHY_LEDS
+#define _DT_BINDINGS_PHY_LEDS
+
+#define PHY_LED_OFF            (1 << 0)        /* is off */
+#define PHY_LED_LINK10         (1 << 1)        /* link is 10MBit/s */
+#define PHY_LED_LINK100                (1 << 2)        /* link is 100MBit/s */
+#define PHY_LED_LINK1000       (1 << 3)        /* link is 1000MBit/s */
+#define PHY_LED_PDOWN          (1 << 4)        /* link is powered down */
+#define PHY_LED_EEE            (1 << 5)        /* link is in EEE mode */
+#define PHY_LED_ANEG           (1 << 6)        /* auto negotiation is running 
*/
+#define PHY_LED_ABIST          (1 << 7)        /* analog self testing is 
running */
+#define PHY_LED_CDIAG          (1 << 8)        /* cable diagnostics is running 
*/
+#define PHY_LED_COPPER         (1 << 9)        /* copper interface detected */
+#define PHY_LED_FIBER          (1 << 10)       /* fiber interface detected */
+#define PHY_LED_TXACT          (1 << 11)       /* Transmit activity */
+#define PHY_LED_RXACT          (1 << 12)       /* Receive activity */
+#define PHY_LED_COL            (1 << 13)       /* Collision */
+
+#endif /* _DT_BINDINGS_PHY_LEDS */
-- 
2.8.1

Reply via email to