How to control Eth0 led

We need to control Ethernet led on TX2, due to our product’s requirement.
Is there a way to manage the Etherent led on/off on TX2 as we expect?

Thank you,

Hi HuiW,

You need the Ethernet phy datasheet from Broadcom to know how to configure the register.

There is a patch for configuring LED behvaior.
Please refer to

https://devtalk.nvidia.com/default/topic/1063514/jetson-tx2/tx2-rj45-led-behavior-control-via-gbe_link_act-gbe_link_100-gbe_link_1000/post/5385512/#5385512

and some stories in

https://devtalk.nvidia.com/default/topic/1025850/jetson-tx2/tx2-gbe-led-operation/

Now The LED state is below
1000M : right link led : yallow
left speed led : Orange ,but flashing
100M : right link led : OFF
left speed led : green, but flashing

our_led.png
Our_schematic_design.png
Our_schematic_design1.png

Could you share what pins are green/orange/yellow LED connected to?

Previous patch would have some problems.
Please try this patch.

diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c
index bc4577f..190741c 100644
--- a/drivers/net/phy/broadcom.c
+++ b/drivers/net/phy/broadcom.c
@@ -323,6 +323,21 @@ static int bcm54xx_config_init(struct phy_device *phydev)
                reg |= BIT(14); /* Enable rx of extended pkts */
                bcm89xx_shadow_write(phydev, MII_BCM89XX_SHD_AUX_CONTROL, reg);
        }
+       /* Disable LOM-LED */
+       reg = bcm_phy_read_shadow(phydev, BCM54XX_SHD_RGMII_MODE);
+       reg = reg & ~(1 << 2);
+       reg = reg & ~(1 << 5);
+       reg = reg & ~(3);
+       bcm_phy_write_shadow(phydev, BCM54XX_SHD_RGMII_MODE, reg);
+
+       reg = bcm_phy_read_shadow(phydev, BCM89610_SHD_LEDS1);
+       bcm_phy_write_shadow(phydev, BCM89610_SHD_LEDS1, reg |
+                           BCM89610_SHD_LEDS1_LED2(BCM_LED_SRC_LINKSPD1) |
+                            BCM89610_SHD_LEDS1_LED1(BCM_LED_SRC_ACTIVITYLED));
+
+       reg = bcm_phy_read_shadow(phydev, BCM89610_SHD_LEDS2);
+       bcm_phy_write_shadow(phydev, BCM89610_SHD_LEDS2, reg |
+                            BCM89610_SHD_LEDS2_LED3(BCM_LED_SRC_LINKSPD2));

        bcm54xx_phydsp_config(phydev);

diff --git a/include/linux/brcmphy.h b/include/linux/brcmphy.h
index 4ede6b8..3dcd0c0 100644
--- a/include/linux/brcmphy.h
+++ b/include/linux/brcmphy.h
@@ -170,6 +170,14 @@
 #define BCM5482_SHD_MODE       0x1f    /* 11111: Mode Control Register */
 #define BCM5482_SHD_MODE_1000BX        0x0001  /* Enable 1000BASE-X registers */

+#define BCM89610_SHD_LEDS1       0x0d
+#define BCM89610_SHD_LEDS1_LED2(src)     ((src & 0xf) << 4)
+#define BCM89610_SHD_LEDS1_LED1(src)     ((src & 0xf) << 0)
+
+#define BCM89610_SHD_LEDS2       0x0e
+#define BCM89610_SHD_LEDS2_LED4(src)     ((src & 0xf) << 4)
+#define BCM89610_SHD_LEDS2_LED3(src)     ((src & 0xf) << 0)
+

Hi WayneWWW

I try the patch, but it is not correct

Now The LED state is below
1000M : left link led : green ,but flashing
right speed led : yallow ,but flashing
1000M : left link led : green ,but flashing
right speed led : yallow ,but flashing

We want
1000M : left link led : orange ,on
right speed led : yellow
100M : left link led : green , on
right speed led : yellow

static int bcm54xx_config_init(struct phy_device *phydev)
{
int reg, err;

reg = phy_read(phydev, MII_BCM54XX_ECR);
if (reg < 0)
	return reg;

/* Mask interrupts globally.  */
reg |= MII_BCM54XX_ECR_IM;
err = phy_write(phydev, MII_BCM54XX_ECR, reg);
if (err < 0)
	return err;

/* Unmask events we are interested in.  */
reg = ~(MII_BCM54XX_INT_DUPLEX |
	MII_BCM54XX_INT_SPEED |
	MII_BCM54XX_INT_LINK |
	MII_BCM54XX_INT_ANPR);

/* unmask energy detect interrupt */
if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM89610 ||
    BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610)
	reg &= ~MII_BCM54XX_INT_EDETECT;

err = phy_write(phydev, MII_BCM54XX_IMR, reg);
if (err < 0)
	return err;

if ((BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610 ||
     BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610M) &&
    (phydev->dev_flags & PHY_BRCM_CLEAR_RGMII_MODE))
	bcm_phy_write_shadow(phydev, BCM54XX_SHD_RGMII_MODE, 0);

if ((phydev->dev_flags & PHY_BRCM_RX_REFCLK_UNUSED) ||
    (phydev->dev_flags & PHY_BRCM_DIS_TXCRXC_NOENRGY) ||
    (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
	bcm54xx_adjust_rxrefclk(phydev);

/* enable energy detect interrupt status update */
if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM89610 ||
    BRCM_PHY_MODEL(phydev) == PHY_ID_BCM50610) {
	reg = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3);
	reg |= BCM54XX_SHD_SCR3_EDETECT_EN;
	bcm_phy_write_shadow(phydev, BCM54XX_SHD_SCR3, reg);
	bcm89xx_enable_oob(phydev);

	/* Enable phy to tx/rx jumbo frames. Driver
	* will drop jumbo frames if it is not enabled.
	*/
	reg = phy_read(phydev, MII_BCM54XX_ECR);
	reg |= MII_BCM89XX_ECR_TXFIFOLAT;
	err = phy_write(phydev, MII_BCM54XX_ECR, reg);
	if (err < 0)
		return err;

	reg = bcm89xx_shadow_read(phydev, MII_BCM89XX_SHD_AUX_CONTROL);
	reg |= BIT(14); /* Enable rx of extended pkts */
	bcm89xx_shadow_write(phydev, MII_BCM89XX_SHD_AUX_CONTROL, reg);
            /* Disable LOM-LED */
   		reg = bcm_phy_read_shadow(phydev, BCM54XX_SHD_RGMII_MODE);
   		reg = reg & ~(1 << 2);
   		reg = reg & ~(1 << 5);
  		reg = reg & ~(3);
            bcm_phy_write_shadow(phydev, BCM54XX_SHD_RGMII_MODE, reg);

            reg = bcm_phy_read_shadow(phydev, BCM89610_SHD_LEDS1);
  	        bcm_phy_write_shadow(phydev, BCM89610_SHD_LEDS1, reg |
                        BCM89610_SHD_LEDS1_LED2(BCM_LED_SRC_LINKSPD2) |
                        BCM89610_SHD_LEDS1_LED1(BCM_LED_SRC_ACTIVITYLED));

   		reg = bcm_phy_read_shadow(phydev, BCM89610_SHD_LEDS2);
 			    bcm_phy_write_shadow(phydev, BCM89610_SHD_LEDS1, reg |
                        BCM89610_SHD_LEDS2_LED3(BCM_LED_SRC_LINKSPD2));                

	/* disable AUTOEEEgr in TOP level expansion register 0x40
	 * as needed for Native EEE to work.
	 */
	err = bcm54xx_exp_read(phydev, MII_BCM54XX_TOPL_EXP_EXP40);
	if (err < 0)
		return err;
	err &= ~MII_BCM54XX_TOPL_EXP_EXP40_AUTOGREEE;
	err = bcm54xx_exp_write(phydev, MII_BCM54XX_TOPL_EXP_EXP40,
				err);
	if (err < 0)
		return err;
}

bcm54xx_phydsp_config(phydev);

return 0;

}

/*

  • BCM5482: Shadow registers
  • Shadow values go into bits [14:10] of register 0x1c to select a shadow
  • register to access.
    /
    /
    00101: Spare Control Register 3 */
    #define BCM54XX_SHD_SCR3 0x05
    #define BCM54XX_SHD_SCR3_DEF_CLK125 0x0001
    #define BCM54XX_SHD_SCR3_DLLAPD_DIS 0x0002
    #define BCM54XX_SHD_SCR3_TRDDAPD 0x0004

/* Broadcom IDDQ-LP regs */
#define MII_BCM54XX_SHD 0x1c
#define MII_BCM54XX_SHD_IDDQ 0x3000
#define MII_BCM54XX_IDDQ_LP 0x0001
#define MII_BCM54XX_EXT_CTL_WR_ENABLE 0X8000

#define BCM_IDDQ_EN 1

/* 01010: Auto Power-Down /
#define BCM54XX_SHD_APD 0x0a
#define BCM_APD_CLR_MASK 0xFE9F /
clear bits 5, 6 & 8 /
#define BCM54XX_SHD_APD_EN 0x0020
#define BCM_NO_ANEG_APD_EN 0x0060 /
bits 5 & 6 /
#define BCM_APD_SINGLELP_EN 0x0100 /
Bit 8 */

#define BCM5482_SHD_LEDS1 0x0d /* 01101: LED Selector 1 /
/
LED3 / ~LINKSPD[2] selector /
#define BCM5482_SHD_LEDS1_LED3(src) ((src & 0xf) << 4)
/
LED1 / ~LINKSPD[1] selector /
#define BCM5482_SHD_LEDS1_LED1(src) ((src & 0xf) << 0)
#define BCM54XX_SHD_RGMII_MODE 0x0b /
01011: RGMII Mode Selector /
#define BCM5482_SHD_SSD 0x14 /
10100: Secondary SerDes control /
#define BCM5482_SHD_SSD_LEDM 0x0008 /
SSD LED Mode enable /
#define BCM5482_SHD_SSD_EN 0x0001 /
SSD enable /
#define BCM5482_SHD_MODE 0x1f /
11111: Mode Control Register /
#define BCM5482_SHD_MODE_1000BX 0x0001 /
Enable 1000BASE-X registers */

#define BCM89610_SHD_LEDS1 0x0d
#define BCM89610_SHD_LEDS1_LED2(src) ((src & 0xf) << 4)
#define BCM89610_SHD_LEDS1_LED1(src) ((src & 0xf) << 0)
#define BCM89610_SHD_LEDS2 0x0e
#define BCM89610_SHD_LEDS2_LED4(src) ((src & 0xf) << 4)
#define BCM89610_SHD_LEDS2_LED3(src) ((src & 0xf) << 0)

Hi WayneWWW

I have already to follow your recommend, and Now The LED state is below

1000M : left link led : orange ,but flashing
right speed led : yallow ,but flashing
100M : left link led : orange ,but flashing
right speed led : yallow ,but flashing

Could you try to configure LED3 to Activity, LED2 to SPD2 and LED1 to SPD1?

hi WayneWWW

I don’t understand “try to configure LED3 to Activity, LED2 to SPD2 and LED1 to SPD1?”

thank

hi WayneWWW
how to rewrite code
bcm_phy_write_shadow(phydev, BCM54XX_SHD_RGMII_MODE, reg);

            reg = bcm_phy_read_shadow(phydev, BCM89610_SHD_LEDS1);
  	        bcm_phy_write_shadow(phydev, BCM89610_SHD_LEDS1, reg |
                        BCM89610_SHD_LEDS1_LED2(BCM_LED_SRC_LINKSPD2) |
                        BCM89610_SHD_LEDS1_LED1(BCM_LED_SRC_ACTIVITYLED));

   		reg = bcm_phy_read_shadow(phydev, BCM89610_SHD_LEDS2);
 			    bcm_phy_write_shadow(phydev, BCM89610_SHD_LEDS1, reg |
                        BCM89610_SHD_LEDS2_LED3(BCM_LED_SRC_LINKSPD2));

BCM89610_SHD_LEDS2_LED3 -> This means LED3, please try to modify the value in () to “BCM_LED_SRC_ACTIVITYLED”

and same manner to the other two.

BCM89610_SHD_LEDS1_LED2 -> BCM_LED_SRC_LINKSPD2
BCM89610_SHD_LEDS1_LED1 -> BCM_LED_SRC_LINKSPD1

/home/johnnyli/AIB-TX201/kernel/kernel-4.4/drivers/net/phy/broadcom.c: In function ‘bcm54xx_config_init’:
/home/johnnyli/AIB-TX201/kernel/kernel-4.4/drivers/net/phy/broadcom.c:546:84: error: called object is not a function or function pointer
bcm_phy_write_shadow(phydev, BCM89610_SHD_LEDS1, reg | BCM_LED_SRC_LINKSPD2(BCM_LED_SRC_LINKSPD1) | BCM_LED_SRC_LINKSPD1(BCM_LED_SRC_ACTIVITYLED));
^
/home/johnnyli/AIB-TX201/kernel/kernel-4.4/drivers/net/phy/broadcom.c:546:129: error: called object is not a function or function pointer
bcm_phy_write_shadow(phydev, BCM89610_SHD_LEDS1, reg | BCM_LED_SRC_LINKSPD2(BCM_LED_SRC_LINKSPD1) | BCM_LED_SRC_LINKSPD1(BCM_LED_SRC_ACTIVITYLED));
^
/home/johnnyli/AIB-TX201/kernel/kernel-4.4/scripts/Makefile.build:261: recipe for target ‘drivers/net/phy/broadcom.o’ failed

We are using kernel4.9 but not kernel4.4.

Hi WayneWWW

Is the version difference so big ?

if the answer is yes, can you provide for kernel 4.4 control Eth0 led way.

Hi aesinfo,

Are you using rel-28 release? I don’t get why you hit error in #14.

It is almost same change as you did in #7 and #8, but just swap the value to write into register. If you could make it in #7/8, why do you hit error in #14?

Also, I just read your code in #7 and #8 and they are not correct.

I guess the naming may cause trouble, so I would explain it again.

bcm_phy_write_shadow(phydev, BCM89610_SHD_LEDS1, reg |
                        BCM89610_SHD_LEDS1_LED2(BCM_LED_SRC_LINKSPD1) |
                         BCM89610_SHD_LEDS1_LED1(BCM_LED_SRC_ACTIVITYLED));

BCM89610_SHD_LEDS1 -> This is the register address as 0x0d.
BCM89610_SHD_LEDS1_LED2 -> This is a function for configuring LED2
BCM89610_SHD_LEDS1_LED1 -> This is a function for configuring LED1

If I write BCM89610_SHD_LEDS1_LED2(BCM_LED_SRC_LINKSPD1), it means I configure LED2 as LINKSPD1 function.

Similar idea to BCM89610_SHD_LEDS1_LED1(BCM_LED_SRC_ACTIVITYLED) in which I configure LED1 to activity LED (the one that should be flashing).

LED1 and LED2 belong to register BCM89610_SHD_LEDS1 and LED3 and LED4 belong to BCM89610_SHD_LEDS2 (0x0e).

Thus, there would be no LED1 or LED2 appear to write to BCM89610_SHD_LEDS2.

In #13, I think we should configure
BCM89610_SHD_LEDS2_LED3 to activity led.
BCM89610_SHD_LEDS1_LED2 to BCM_LED_SRC_LINKSPD2
BCM89610_SHD_LEDS1_LED1 to BCM_LED_SRC_LINKSPD1

Please check if it can work.

Hi WayneWWW

As below is you provide example code. Can you tell me why i get error after you suggest.

/* Disable LOM-LED */

  bcm_phy_write_shadow(phydev, BCM54XX_SHD_RGMII_MODE, reg);
  reg = bcm_phy_read_shadow(phydev, BCM89610_SHD_LEDS1);
  bcm_phy_write_shadow(phydev, BCM89610_SHD_LEDS1, reg |
                      [b]BCM89610_SHD_LEDS1_LED2[/b](BCM_LED_SRC_LINKSPD1) |
                      [b]BCM89610_SHD_LEDS1_LED1[/b](BCM_LED_SRC_ACTIVITYLED));

  reg = bcm_phy_read_shadow(phydev, BCM89610_SHD_LEDS2);
  bcm_phy_write_shadow(phydev, BCM89610_SHD_LEDS2, reg |
                      [b]BCM89610_SHD_LEDS2_LED3[/b](BCM_LED_SRC_LINKSPD2));

May I know what is the compilation error? Do you mean the code is not able to compile in the beginning?

export CROSS_COMPILE=/opt/l4t-gcc-toolchain-64-bit-28-2.1/install/bin/aarch64-unknown-linux-gnu-