diff mbox

[RFC,v3,3/3] NET: PHY: Intel XWAY: add LED configuration support

Message ID 1465040728-4904-4-git-send-email-hauke@hauke-m.de
State Superseded, archived
Headers show

Commit Message

Hauke Mehrtens June 4, 2016, 11:45 a.m. UTC
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 <hauke@hauke-m.de>
---
 .../devicetree/bindings/phy/intel-xway.txt         |  77 +++++++++++
 drivers/net/phy/intel-xway.c                       | 152 +++++++++++++++++++++
 2 files changed, 229 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/intel-xway.txt
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/phy/intel-xway.txt b/Documentation/devicetree/bindings/phy/intel-xway.txt
new file mode 100644
index 0000000..02891c4
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/intel-xway.txt
@@ -0,0 +1,77 @@ 
+Intel XWAY Ethernet PHY binding
+------------------------------
+
+This supports the Intel XWAY (former Lantiq) 11G and 22E PHYs. These
+PHYs are also named PEF 7061, PEF 7071 and PEF 7072.
+
+Required properties:
+ - compatible:	should be "ethernet-phy-ieee802.3-c22"
+ - reg:		MDIO address of this PHY
+
+
+LEDs:
+The PEF 7071 PHY supports 3 LEDs, the PEF 7072 PHY supports 4 LEDs. Use
+one subnode for each LED. By default the LEDs 0, 1 and 2 are switched
+to be constant on when a 10MBit/s, 100MBit/s or 1000MBit/s link is
+detected and they blink when TX or RX traffic is detected. All 3 LEDs
+are doing the same as most known devices only have one LED.
+
+To change the behavior create a subnode with the following attributes:
+
+Required properties:
+ - compatible: 	should be: "phy,led"
+ - reg:		led number
+
+optional properties:
+ - led-const-on:	Conditions when being constant on
+			Possible options are one of these:
+			LED_LINK10, LED_LINK100 and LED_LINK1000, or
+			some of these 3 values connected with OR.
+			PHY_LED_PDOWN, PHY_LED_EEE, PHY_LED_ANEG,
+			PHY_LED_ABIST, PHY_LED_CDIAG, PHY_LED_COPPER,
+			PHY_LED_FIBER.
+ - led-pulse:		Conditions when led is pulsed
+			The following values can be connected with OR:
+			PHY_LED_TXACT, PHY_LED_RXACT, PHY_LED_COL
+ - led-blink-slow:	Conditions when led should blink with 2Hz:
+			Possible options are one of these:
+			LED_LINK10, LED_LINK100 and LED_LINK1000, or
+			some of these 3 values connected with OR.
+			PHY_LED_PDOWN, PHY_LED_EEE, PHY_LED_ANEG,
+			PHY_LED_ABIST, PHY_LED_CDIAG.
+ - led-blink-fast:	Conditions when led should blink with 16Hz:
+			Possible options are one of these:
+			LED_LINK10, LED_LINK100 and LED_LINK1000, or
+			some of these 3 values connected with OR.
+			PHY_LED_PDOWN, PHY_LED_EEE, PHY_LED_ANEG,
+			PHY_LED_ABIST, PHY_LED_CDIAG.
+
+When multiple properties are set they are applied with the following priority:
+ 1. led-pulse
+ 2. led-blink-fast
+ 3. led-blink-slow
+ 4. led-const-on
+ 5. off
+
+
+Example:
+
+#include <dt-bindings/phy/phy-leds.h>
+phy@0 {
+	compatible = "intel,phy11g", "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-pulse = <(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/intel-xway.c b/drivers/net/phy/intel-xway.c
index c789462..0c707b6 100644
--- a/drivers/net/phy/intel-xway.c
+++ b/drivers/net/phy/intel-xway.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 XWAY_MDIO_IMASK			0x19	/* interrupt mask */
 #define XWAY_MDIO_ISTAT			0x1A	/* interrupt status */
@@ -152,11 +153,158 @@ 
 #define PHY_ID_PHY11G_VR9		0xD565A409
 #define PHY_ID_PHY22F_VR9		0xD565A419
 
+static void xway_gphy_config_led(struct phy_device *phydev,
+				 struct device_node *led_np)
+{
+	const __be32 *addr, *blink_fast_p, *const_on_p, *pulse_p, *blink_slow_p;
+	u32 num, blink_fast, const_on, pulse, 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 = XWAY_MMD_LEDxH_BLINKF_NONE | XWAY_MMD_LEDxH_CON_LINK10XX;
+	blink_fast_p = of_get_property(led_np, "led-blink-fast", NULL);
+	if (blink_fast_p) {
+		ledxh &= ~XWAY_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 |= XWAY_MMD_LEDxH_BLINKF_LINK10XX;
+		} else if ((blink_fast & PHY_LED_LINK10) &&
+			   (blink_fast & PHY_LED_LINK1000)) {
+			ledxh |= XWAY_MMD_LEDxH_BLINKF_LINK10_0;
+		} else if ((blink_fast & PHY_LED_LINK10) &&
+			   (blink_fast & PHY_LED_LINK100)) {
+			ledxh |= XWAY_MMD_LEDxH_BLINKF_LINK10X;
+		} else if ((blink_fast & PHY_LED_LINK100) &&
+			   (blink_fast & PHY_LED_LINK1000)) {
+			ledxh |= XWAY_MMD_LEDxH_BLINKF_LINK100X;
+		} else if (blink_fast & PHY_LED_LINK10) {
+			ledxh |= XWAY_MMD_LEDxH_BLINKF_LINK10;
+		} else if (blink_fast & PHY_LED_LINK100) {
+			ledxh |= XWAY_MMD_LEDxH_BLINKF_LINK100;
+		} else if (blink_fast & PHY_LED_LINK1000) {
+			ledxh |= XWAY_MMD_LEDxH_BLINKF_LINK1000;
+		} else if (blink_fast & PHY_LED_PDOWN) {
+			ledxh |= XWAY_MMD_LEDxH_BLINKF_PDOWN;
+		} else if (blink_fast & PHY_LED_EEE) {
+			ledxh |= XWAY_MMD_LEDxH_BLINKF_EEE;
+		} else if (blink_fast & PHY_LED_ANEG) {
+			ledxh |= XWAY_MMD_LEDxH_BLINKF_ANEG;
+		} else if (blink_fast & PHY_LED_ABIST) {
+			ledxh |= XWAY_MMD_LEDxH_BLINKF_ABIST;
+		} else if (blink_fast & PHY_LED_CDIAG) {
+			ledxh |= XWAY_MMD_LEDxH_BLINKF_CDIAG;
+		}
+	}
+	const_on_p = of_get_property(led_np, "led-const-on", NULL);
+	if (const_on_p) {
+		ledxh &= ~XWAY_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 |= XWAY_MMD_LEDxH_CON_LINK10XX;
+		} else if ((const_on & PHY_LED_LINK10) &&
+			   (const_on & PHY_LED_LINK1000)) {
+			ledxh |= XWAY_MMD_LEDxH_CON_LINK10_0;
+		} else if ((const_on & PHY_LED_LINK10) &&
+			   (const_on & PHY_LED_LINK100)) {
+			ledxh |= XWAY_MMD_LEDxH_CON_LINK10X;
+		} else if ((const_on & PHY_LED_LINK100) &&
+			   (const_on & PHY_LED_LINK1000)) {
+			ledxh |= XWAY_MMD_LEDxH_CON_LINK100X;
+		} else if (const_on & PHY_LED_LINK10) {
+			ledxh |= XWAY_MMD_LEDxH_CON_LINK10;
+		} else if (const_on & PHY_LED_LINK100) {
+			ledxh |= XWAY_MMD_LEDxH_CON_LINK100;
+		} else if (const_on & PHY_LED_LINK1000) {
+			ledxh |= XWAY_MMD_LEDxH_CON_LINK1000;
+		} else if (const_on & PHY_LED_PDOWN) {
+			ledxh |= XWAY_MMD_LEDxH_CON_PDOWN;
+		} else if (const_on & PHY_LED_EEE) {
+			ledxh |= XWAY_MMD_LEDxH_CON_EEE;
+		} else if (const_on & PHY_LED_ANEG) {
+			ledxh |= XWAY_MMD_LEDxH_CON_ANEG;
+		} else if (const_on & PHY_LED_ABIST) {
+			ledxh |= XWAY_MMD_LEDxH_CON_ABIST;
+		} else if (const_on & PHY_LED_CDIAG) {
+			ledxh |= XWAY_MMD_LEDxH_CON_CDIAG;
+		} else if (const_on & PHY_LED_COPPER) {
+			ledxh |= XWAY_MMD_LEDxH_CON_COPPER;
+		} else if (const_on & PHY_LED_FIBER) {
+			ledxh |= XWAY_MMD_LEDxH_CON_FIBER;
+		}
+	}
+	phy_write_mmd_indirect(phydev, XWAY_MMD_LED0H + (num * 2),
+			       MDIO_MMD_VEND2, ledxh);
+
+	ledxl = XWAY_MMD_LEDxL_PULSE_TXACT | XWAY_MMD_LEDxL_PULSE_RXACT |
+		XWAY_MMD_LEDxL_BLINKS_NONE;
+	pulse_p = of_get_property(led_np, "led-pulse", NULL);
+	if (pulse_p) {
+		ledxl &= ~XWAY_MMD_LEDxL_PULSE_MASK;
+		pulse = be32_to_cpu(*pulse_p);
+		if (pulse & PHY_LED_TXACT)
+			ledxl |= XWAY_MMD_LEDxL_PULSE_TXACT;
+		if (pulse & PHY_LED_RXACT)
+			ledxl |= XWAY_MMD_LEDxL_PULSE_RXACT;
+		if (pulse & PHY_LED_COL)
+			ledxl |= XWAY_MMD_LEDxL_PULSE_COL;
+	}
+	blink_slow_p = of_get_property(led_np, "led-blink-slow", NULL);
+	if (blink_slow_p) {
+		ledxl &= ~XWAY_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 |= XWAY_MMD_LEDxL_BLINKS_LINK10XX;
+		} else if ((blink_slow & PHY_LED_LINK10) &&
+			   (blink_slow & PHY_LED_LINK1000)) {
+			ledxl |= XWAY_MMD_LEDxL_BLINKS_LINK10_0;
+		} else if ((blink_slow & PHY_LED_LINK10) &&
+			   (blink_slow & PHY_LED_LINK100)) {
+			ledxl |= XWAY_MMD_LEDxL_BLINKS_LINK10X;
+		} else if ((blink_slow & PHY_LED_LINK100) &&
+			   (blink_slow & PHY_LED_LINK1000)) {
+			ledxl |= XWAY_MMD_LEDxL_BLINKS_LINK100X;
+		} else if (blink_slow & PHY_LED_LINK10) {
+			ledxl |= XWAY_MMD_LEDxL_BLINKS_LINK10;
+		} else if (blink_slow & PHY_LED_LINK100) {
+			ledxl |= XWAY_MMD_LEDxL_BLINKS_LINK100;
+		} else if (blink_slow & PHY_LED_LINK1000) {
+			ledxl |= XWAY_MMD_LEDxL_BLINKS_LINK1000;
+		} else if (blink_slow & PHY_LED_PDOWN) {
+			ledxl |= XWAY_MMD_LEDxL_BLINKS_PDOWN;
+		} else if (blink_slow & PHY_LED_EEE) {
+			ledxl |= XWAY_MMD_LEDxL_BLINKS_EEE;
+		} else if (blink_slow & PHY_LED_ANEG) {
+			ledxl |= XWAY_MMD_LEDxL_BLINKS_ANEG;
+		} else if (blink_slow & PHY_LED_ABIST) {
+			ledxl |= XWAY_MMD_LEDxL_BLINKS_ABIST;
+		} else if (blink_slow & PHY_LED_CDIAG) {
+			ledxl |= XWAY_MMD_LEDxL_BLINKS_CDIAG;
+		}
+	}
+	phy_write_mmd_indirect(phydev, XWAY_MMD_LED0L + (num * 2),
+			       MDIO_MMD_VEND2, ledxl);
+}
+
 static int xway_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, XWAY_MDIO_IMASK, 0);
@@ -190,6 +338,10 @@  static int xway_gphy_config_init(struct phy_device *phydev)
 	phy_write_mmd_indirect(phydev, XWAY_MMD_LED2H, MDIO_MMD_VEND2, ledxh);
 	phy_write_mmd_indirect(phydev, XWAY_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"))
+			xway_gphy_config_led(phydev, led_np);
+
 	return 0;
 }