sunxi: add PHY LEDs support for MAXIO driver

Signed-off-by: Tianling Shen <cnsztl@immortalwrt.org>
This commit is contained in:
Tianling Shen
2026-05-07 14:11:45 +08:00
parent f4b3098ad3
commit 92f2416c60
3 changed files with 440 additions and 0 deletions
@@ -27,6 +27,12 @@ netled_set() {
boot() {
case "$(board_name)" in
radxa,cubie-a5e)
netled_set "stmmac-0:01:amber:lan" "eth0" "link_10 link_100 link_1000" "0" "rx tx" "1"
netled_set "stmmac-0:01:green:lan" "eth0" "link_10 link_100 link_1000" "1" "rx tx" "0"
netled_set "stmmac-1:01:amber:wan" "eth1" "link_10 link_100 link_1000" "0" "rx tx" "1"
netled_set "stmmac-1:01:green:wan" "eth1" "link_10 link_100 link_1000" "1" "rx tx" "0"
;;
xunlong,orangepi-zero3)
netled_set "stmmac-0:01:amber:lan" "eth0" "link_10 link_100 link_1000" "0" "rx tx" "1"
netled_set "stmmac-0:01:green:lan" "eth0" "link_10 link_100 link_1000" "1" "rx tx" "0"
@@ -37,3 +37,55 @@ Signed-off-by: Tianling Shen <cnsztl@gmail.com>
};
use-led {
@@ -129,6 +131,25 @@
reset-gpios = <&pio 7 8 GPIO_ACTIVE_LOW>; /* PH8 */
reset-assert-us = <10000>;
reset-deassert-us = <150000>;
+
+ leds {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ led@1 {
+ reg = <1>;
+ color = <LED_COLOR_ID_GREEN>;
+ function = LED_FUNCTION_LAN;
+ default-state = "keep";
+ };
+
+ led@2 {
+ reg = <2>;
+ color = <LED_COLOR_ID_AMBER>;
+ function = LED_FUNCTION_LAN;
+ default-state = "keep";
+ };
+ };
};
};
@@ -139,6 +160,25 @@
reset-gpios = <&pio 9 16 GPIO_ACTIVE_LOW>; /* PJ16 */
reset-assert-us = <10000>;
reset-deassert-us = <150000>;
+
+ leds {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ led@1 {
+ reg = <1>;
+ color = <LED_COLOR_ID_GREEN>;
+ function = LED_FUNCTION_WAN;
+ default-state = "keep";
+ };
+
+ led@2 {
+ reg = <2>;
+ color = <LED_COLOR_ID_AMBER>;
+ function = LED_FUNCTION_WAN;
+ default-state = "keep";
+ };
+ };
};
};
@@ -0,0 +1,382 @@
--- a/drivers/net/phy/maxio.c
+++ b/drivers/net/phy/maxio.c
@@ -28,6 +28,17 @@
#define MAXIO_MAE0621A_WORK_STATUS_REG 0x1d
#define MAXIO_MAE0621A_CLK_MODE_REG 0x02
+#define MAE0621X_Q3C_LED_COUNT 3
+#define MAE0621X_Q3C_LED_PAGE 0xd04
+#define MAE0621X_Q3C_LED_REG 0x10
+#define MAE0621X_Q3C_LED_MODE BIT(15)
+#define MAE0621X_Q3C_LED_ACT_TXRX BIT(4)
+#define MAE0621X_Q3C_LED_LINK_10 BIT(0)
+#define MAE0621X_Q3C_LED_LINK_100 BIT(1)
+#define MAE0621X_Q3C_LED_LINK_1000 BIT(3)
+#define MAE0621X_Q3C_LED_MASK GENMASK(4, 0)
+#define MAE0621X_Q3C_LED_SHIFT 5
+
#define MAXIO_PHYSR_P_A43 (0X1A)
#define MAXIO_PHY_LINK (1 << 2)
#define MAXIO_PHY_DUPLEX (1 << 3)
@@ -43,32 +54,14 @@
#define PHY_READ(a, b) phy_read((a), (b))
#define PHY_WRITE(a, b, c) phy_write((a), (b), (c))
-int maxio_read_paged(struct phy_device *phydev, int page, u32 regnum)
+static int maxio_read_page(struct phy_device *phydev)
{
- int ret = 0, oldpage;
-
- oldpage = PHY_READ(phydev, MAXIO_PAGE_SELECT);
- if (oldpage >= 0) {
- PHY_WRITE(phydev, MAXIO_PAGE_SELECT, page);
- ret = PHY_READ(phydev, regnum);
- }
- PHY_WRITE(phydev, MAXIO_PAGE_SELECT, oldpage);
-
- return ret;
+ return __phy_read(phydev, MAXIO_PAGE_SELECT);
}
-int maxio_write_paged(struct phy_device *phydev, int page, u32 regnum, u16 val)
+static int maxio_write_page(struct phy_device *phydev, int page)
{
- int ret = 0, oldpage;
-
- oldpage = PHY_READ(phydev, MAXIO_PAGE_SELECT);
- if (oldpage >= 0) {
- PHY_WRITE(phydev, MAXIO_PAGE_SELECT, page);
- ret = PHY_WRITE(phydev, regnum, val);
- }
- PHY_WRITE(phydev, MAXIO_PAGE_SELECT, oldpage);
-
- return ret;
+ return __phy_write(phydev, MAXIO_PAGE_SELECT, page);
}
int maxio_adcc_check(struct phy_device *phydev)
@@ -78,13 +71,13 @@ int maxio_adcc_check(struct phy_device *
u32 regval;
int i;
- maxio_write_paged(phydev, 0xd96, 0x2, 0x1fff);
- maxio_write_paged(phydev, 0xd96, 0x2, 0x1000);
+ phy_write_paged(phydev, 0xd96, 0x2, 0x1fff);
+ phy_write_paged(phydev, 0xd96, 0x2, 0x1000);
for (i = 0; i < 4; i++) {
regval = 0xf908 + i * 0x100;
- maxio_write_paged(phydev, 0xd8f, 0xb, regval);
- adcvalue = maxio_read_paged(phydev, 0xd92, 0xb);
+ phy_write_paged(phydev, 0xd8f, 0xb, regval);
+ adcvalue = phy_read_paged(phydev, 0xd92, 0xb);
if (adcvalue & 0x1ff) {
continue;
} else {
@@ -107,16 +100,16 @@ int maxio_self_check(struct phy_device *
printk("MAE0621A READY\n");
break;
} else {
- maxio_write_paged(phydev, 0x0, 0x0, 0x1940);
+ phy_write_paged(phydev, 0x0, 0x0, 0x1940);
PHY_WRITE(phydev, MAXIO_PAGE_SELECT, 0x0);
msleep(10);
- maxio_write_paged(phydev, 0x0, 0x0, 0x1140);
- maxio_write_paged(phydev, 0x0, 0x0, 0x9140);
+ phy_write_paged(phydev, 0x0, 0x0, 0x1140);
+ phy_write_paged(phydev, 0x0, 0x0, 0x9140);
}
}
- maxio_write_paged(phydev, 0xd96, 0x2, 0xfff);
- maxio_write_paged(phydev, 0x0, 0x0, 0x9140);
+ phy_write_paged(phydev, 0xd96, 0x2, 0xfff);
+ phy_write_paged(phydev, 0x0, 0x0, 0x9140);
PHY_WRITE(phydev, MAXIO_PAGE_SELECT, 0x0);
return ret;
@@ -161,7 +154,7 @@ int maxio_restart_aneg(struct phy_device
static void phy_resolve_aneg_linkmode_maxio(struct phy_device *phydev)
{
- int physr_p_a43 = maxio_read_paged(phydev, 0xa43, MAXIO_PHYSR_P_A43);
+ int physr_p_a43 = phy_read_paged(phydev, 0xa43, MAXIO_PHYSR_P_A43);
if ((physr_p_a43 & MAXIO_PHY_SPEED) == MAXIO_PHY_1000M) {
phydev->speed = SPEED_1000;
@@ -193,11 +186,11 @@ void phy_resolve_link_compatibility_maxi
int maxio_an_times = paras[0];
int link_stable = paras[1];
int iner, physr, insr;
- iner = maxio_read_paged(phydev, 0xa42, 0x12);
+ iner = phy_read_paged(phydev, 0xa42, 0x12);
if (iner & AUTONEG_COMPLETED_INT_EN) {
- physr = maxio_read_paged(phydev, 0xa43, 0x1a);
+ physr = phy_read_paged(phydev, 0xa43, 0x1a);
if (physr & LINKOK) {
- insr = maxio_read_paged(phydev, 0xa43, 0x1d);
+ insr = phy_read_paged(phydev, 0xa43, 0x1d);
if ((insr & AUTONEG_COMPLETED) == 0 &&
(link_stable == 0)) {
if (maxio_an_times < 4) {
@@ -258,27 +251,27 @@ static int maxio_mae0621a_config_init(st
printk("MAXIO_PHY_VER: %s \n", MAXIO_PHY_VER);
- ret |= maxio_write_paged(phydev, 0xda0, 0x10, 0xc13);
- ret |= maxio_write_paged(phydev, 0x0, 0xd, 0x7);
- ret |= maxio_write_paged(phydev, 0x0, 0xe, 0x3c);
- ret |= maxio_write_paged(phydev, 0x0, 0xd, 0x4007);
- ret |= maxio_write_paged(phydev, 0x0, 0xe, 0x0);
- ret |= maxio_write_paged(phydev, 0xd96, 0x13, 0x7bc);
- ret |= maxio_write_paged(phydev, 0xd8f, 0x8, 0x2500);
- ret |= maxio_write_paged(phydev, 0xd90, 0x2, 0x1555);
- ret |= maxio_write_paged(phydev, 0xd90, 0x5, 0x2b15);
- ret |= maxio_write_paged(phydev, 0xd92, 0x14, 0xa);
- ret |= maxio_write_paged(phydev, 0xd91, 0x7, 0x5b00);
- ret |= maxio_write_paged(phydev, 0xd8f, 0x0, 0x300);
- ret |= maxio_write_paged(phydev, 0xd92, 0xa, 0x8506);
- ret |= maxio_write_paged(phydev, 0xd91, 0x6, 0x6870);
- ret |= maxio_write_paged(phydev, 0xd91, 0x1, 0x940);
- ret |= maxio_write_paged(phydev, 0xda0, 0x13, 0x1303);
- ret |= maxio_write_paged(phydev, 0xd97, 0xc, 0x177);
- ret |= maxio_write_paged(phydev, 0xd97, 0xb, 0x9a9);
- ret |= maxio_write_paged(phydev, 0xa42, 0x12, 0x28);
- ret |= maxio_write_paged(phydev, 0x0, 0x4, 0xde1);
- ret |= maxio_write_paged(phydev, 0x0, 0x0, 0x9140);
+ ret |= phy_write_paged(phydev, 0xda0, 0x10, 0xc13);
+ ret |= phy_write_paged(phydev, 0x0, 0xd, 0x7);
+ ret |= phy_write_paged(phydev, 0x0, 0xe, 0x3c);
+ ret |= phy_write_paged(phydev, 0x0, 0xd, 0x4007);
+ ret |= phy_write_paged(phydev, 0x0, 0xe, 0x0);
+ ret |= phy_write_paged(phydev, 0xd96, 0x13, 0x7bc);
+ ret |= phy_write_paged(phydev, 0xd8f, 0x8, 0x2500);
+ ret |= phy_write_paged(phydev, 0xd90, 0x2, 0x1555);
+ ret |= phy_write_paged(phydev, 0xd90, 0x5, 0x2b15);
+ ret |= phy_write_paged(phydev, 0xd92, 0x14, 0xa);
+ ret |= phy_write_paged(phydev, 0xd91, 0x7, 0x5b00);
+ ret |= phy_write_paged(phydev, 0xd8f, 0x0, 0x300);
+ ret |= phy_write_paged(phydev, 0xd92, 0xa, 0x8506);
+ ret |= phy_write_paged(phydev, 0xd91, 0x6, 0x6870);
+ ret |= phy_write_paged(phydev, 0xd91, 0x1, 0x940);
+ ret |= phy_write_paged(phydev, 0xda0, 0x13, 0x1303);
+ ret |= phy_write_paged(phydev, 0xd97, 0xc, 0x177);
+ ret |= phy_write_paged(phydev, 0xd97, 0xb, 0x9a9);
+ ret |= phy_write_paged(phydev, 0xa42, 0x12, 0x28);
+ ret |= phy_write_paged(phydev, 0x0, 0x4, 0xde1);
+ ret |= phy_write_paged(phydev, 0x0, 0x0, 0x9140);
PHY_WRITE(phydev, MAXIO_PAGE_SELECT, 0x0);
@@ -318,30 +311,30 @@ static int maxio_mae0621aq3ci_config_ini
int ret = 0;
printk("MAXIO_PHY_VER: %s \n", MAXIO_PHY_VER);
- ret |= maxio_write_paged(phydev, 0xa43, 0x19, 0x823);
+ ret |= phy_write_paged(phydev, 0xa43, 0x19, 0x823);
- ret |= maxio_write_paged(phydev, 0xdab, 0x17, 0xC13);
- ret |= maxio_write_paged(phydev, 0xd96, 0x15, 0xc08a);
- ret |= maxio_write_paged(phydev, 0xda4, 0x12, 0x7bc);
- ret |= maxio_write_paged(phydev, 0xd8f, 0x16, 0x2500);
- ret |= maxio_write_paged(phydev, 0xd90, 0x16, 0x1555);
- ret |= maxio_write_paged(phydev, 0xd92, 0x11, 0x2b15);
- ret |= maxio_write_paged(phydev, 0xd96, 0x16, 0x4010);
- ret |= maxio_write_paged(phydev, 0xda5, 0x11, 0x4a12);
- ret |= maxio_write_paged(phydev, 0xda5, 0x12, 0x4a12);
- ret |= maxio_write_paged(phydev, 0xd99, 0x16, 0xa);
- ret |= maxio_write_paged(phydev, 0xd95, 0x13, 0x5b00);
- ret |= maxio_write_paged(phydev, 0xd8f, 0x10, 0x300);
- ret |= maxio_write_paged(phydev, 0xd98, 0x17, 0x8506);
- ret |= maxio_write_paged(phydev, 0xd95, 0x12, 0x6870);
- ret |= maxio_write_paged(phydev, 0xd93, 0x15, 0x940);
- ret |= maxio_write_paged(phydev, 0xdad, 0x12, 0x1303);
- ret |= maxio_write_paged(phydev, 0xda8, 0x11, 0x177);
- ret |= maxio_write_paged(phydev, 0xda8, 0x10, 0x9a9);
- ret |= maxio_write_paged(phydev, 0xda8, 0x12, 0x868);
- ret |= maxio_write_paged(phydev, 0xa42, 0x12, 0x28);
- ret |= maxio_write_paged(phydev, 0x0, 0x4, 0xde1);
- ret |= maxio_write_paged(phydev, 0x0, 0x0, 0x9140);
+ ret |= phy_write_paged(phydev, 0xdab, 0x17, 0xC13);
+ ret |= phy_write_paged(phydev, 0xd96, 0x15, 0xc08a);
+ ret |= phy_write_paged(phydev, 0xda4, 0x12, 0x7bc);
+ ret |= phy_write_paged(phydev, 0xd8f, 0x16, 0x2500);
+ ret |= phy_write_paged(phydev, 0xd90, 0x16, 0x1555);
+ ret |= phy_write_paged(phydev, 0xd92, 0x11, 0x2b15);
+ ret |= phy_write_paged(phydev, 0xd96, 0x16, 0x4010);
+ ret |= phy_write_paged(phydev, 0xda5, 0x11, 0x4a12);
+ ret |= phy_write_paged(phydev, 0xda5, 0x12, 0x4a12);
+ ret |= phy_write_paged(phydev, 0xd99, 0x16, 0xa);
+ ret |= phy_write_paged(phydev, 0xd95, 0x13, 0x5b00);
+ ret |= phy_write_paged(phydev, 0xd8f, 0x10, 0x300);
+ ret |= phy_write_paged(phydev, 0xd98, 0x17, 0x8506);
+ ret |= phy_write_paged(phydev, 0xd95, 0x12, 0x6870);
+ ret |= phy_write_paged(phydev, 0xd93, 0x15, 0x940);
+ ret |= phy_write_paged(phydev, 0xdad, 0x12, 0x1303);
+ ret |= phy_write_paged(phydev, 0xda8, 0x11, 0x177);
+ ret |= phy_write_paged(phydev, 0xda8, 0x10, 0x9a9);
+ ret |= phy_write_paged(phydev, 0xda8, 0x12, 0x868);
+ ret |= phy_write_paged(phydev, 0xa42, 0x12, 0x28);
+ ret |= phy_write_paged(phydev, 0x0, 0x4, 0xde1);
+ ret |= phy_write_paged(phydev, 0x0, 0x0, 0x9140);
ret |= PHY_WRITE(phydev, MAXIO_PAGE_SELECT, 0);
@@ -352,8 +345,8 @@ static int maxio_mae0621aq3ci_resume(str
{
int ret = 0;
ret = genphy_resume(phydev);
- ret |= maxio_write_paged(phydev, 0xdaa, 0x17, 0x1001);
- ret |= maxio_write_paged(phydev, 0xdab, 0x15, 0x0);
+ ret |= phy_write_paged(phydev, 0xdaa, 0x17, 0x1001);
+ ret |= phy_write_paged(phydev, 0xdab, 0x15, 0x0);
ret |= PHY_WRITE(phydev, MAXIO_PAGE_SELECT, 0);
return ret;
@@ -363,8 +356,8 @@ static int maxio_mae0621aq3ci_suspend(st
{
int ret = 0;
- ret = maxio_write_paged(phydev, 0xdaa, 0x17, 0x1011);
- ret |= maxio_write_paged(phydev, 0xdab, 0x15, 0x5550);
+ ret = phy_write_paged(phydev, 0xdaa, 0x17, 0x1011);
+ ret |= phy_write_paged(phydev, 0xdab, 0x15, 0x5550);
ret |= PHY_WRITE(phydev, MAXIO_PAGE_SELECT, 0);
ret |= genphy_suspend(phydev);
@@ -374,6 +367,114 @@ static int maxio_mae0621aq3ci_suspend(st
return ret;
}
+static int maxio_mae0621aq3ci_led_hw_is_supported(struct phy_device *phydev,
+ u8 index, unsigned long rules)
+{
+ const unsigned long mask = BIT(TRIGGER_NETDEV_LINK) |
+ BIT(TRIGGER_NETDEV_LINK_10) |
+ BIT(TRIGGER_NETDEV_LINK_100) |
+ BIT(TRIGGER_NETDEV_LINK_1000) |
+ BIT(TRIGGER_NETDEV_RX) |
+ BIT(TRIGGER_NETDEV_TX);
+
+ /* The MAE0621A/B-Q3C PHY supports these LED settings on up to three LEDs:
+ * - Link: Configurable subset of 10/100/1000 link rates
+ * - Active: Blink on activity, RX or TX is not differentiated
+ * The Active option has two modes, A and B:
+ * - A: Link and Active indication at configurable, but matching,
+ * subset of 10/100/1000 link rates
+ * - B: Link indication at configurable subset of 10/100/1000 link
+ * rates and Active indication always at all three 10+100+1000
+ * link rates.
+ * This code currently uses mode B only.
+ */
+
+ if (index >= MAE0621X_Q3C_LED_COUNT)
+ return -EINVAL;
+
+ if (rules & ~mask)
+ return -EOPNOTSUPP;
+
+ if (!(rules & BIT(TRIGGER_NETDEV_RX)) ^ !(rules & BIT(TRIGGER_NETDEV_TX)))
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static int maxio_mae0621aq3ci_led_hw_control_get(struct phy_device *phydev,
+ u8 index, unsigned long *rules)
+{
+ int val;
+
+ if (index >= MAE0621X_Q3C_LED_COUNT)
+ return -EINVAL;
+
+ val = phy_read_paged(phydev, MAE0621X_Q3C_LED_PAGE, MAE0621X_Q3C_LED_REG);
+ if (val < 0)
+ return val;
+
+ val >>= MAE0621X_Q3C_LED_SHIFT * index;
+ val &= MAE0621X_Q3C_LED_MASK;
+
+ if (val & MAE0621X_Q3C_LED_LINK_10)
+ __set_bit(TRIGGER_NETDEV_LINK_10, rules);
+
+ if (val & MAE0621X_Q3C_LED_LINK_100)
+ __set_bit(TRIGGER_NETDEV_LINK_100, rules);
+
+ if (val & MAE0621X_Q3C_LED_LINK_1000)
+ __set_bit(TRIGGER_NETDEV_LINK_1000, rules);
+
+ if ((val & MAE0621X_Q3C_LED_LINK_10) &&
+ (val & MAE0621X_Q3C_LED_LINK_100) &&
+ (val & MAE0621X_Q3C_LED_LINK_1000)) {
+ __set_bit(TRIGGER_NETDEV_LINK, rules);
+ }
+
+ if (val & MAE0621X_Q3C_LED_ACT_TXRX) {
+ __set_bit(TRIGGER_NETDEV_RX, rules);
+ __set_bit(TRIGGER_NETDEV_TX, rules);
+ }
+
+ return 0;
+}
+
+static int maxio_mae0621aq3ci_led_hw_control_set(struct phy_device *phydev,
+ u8 index, unsigned long rules)
+{
+ const u16 mask = MAE0621X_Q3C_LED_MASK << (MAE0621X_Q3C_LED_SHIFT * index);
+ u16 val = 0;
+
+ if (index >= MAE0621X_Q3C_LED_COUNT)
+ return -EINVAL;
+
+ if (test_bit(TRIGGER_NETDEV_LINK, &rules) ||
+ test_bit(TRIGGER_NETDEV_LINK_10, &rules)) {
+ val |= MAE0621X_Q3C_LED_LINK_10;
+ }
+
+ if (test_bit(TRIGGER_NETDEV_LINK, &rules) ||
+ test_bit(TRIGGER_NETDEV_LINK_100, &rules)) {
+ val |= MAE0621X_Q3C_LED_LINK_100;
+ }
+
+ if (test_bit(TRIGGER_NETDEV_LINK, &rules) ||
+ test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) {
+ val |= MAE0621X_Q3C_LED_LINK_1000;
+ }
+
+ if (test_bit(TRIGGER_NETDEV_RX, &rules) ||
+ test_bit(TRIGGER_NETDEV_TX, &rules)) {
+ val |= MAE0621X_Q3C_LED_ACT_TXRX;
+ }
+
+ val <<= MAE0621X_Q3C_LED_SHIFT * index;
+ val |= MAE0621X_Q3C_LED_MODE; /* Mode B */
+
+ return phy_modify_paged(phydev, MAE0621X_Q3C_LED_PAGE,
+ MAE0621X_Q3C_LED_REG, mask, val);
+}
+
static struct phy_driver maxio_nc_drvs[] = {
{
.phy_id = 0x7b744411,
@@ -387,6 +488,8 @@ static struct phy_driver maxio_nc_drvs[]
.suspend = maxio_mae0621a_suspend,
.resume = maxio_mae0621a_resume,
.remove = maxio_mae0621a_remove,
+ .read_page = maxio_read_page,
+ .write_page = maxio_write_page,
},
{
.phy_id = 0x7b744412,
@@ -400,6 +503,11 @@ static struct phy_driver maxio_nc_drvs[]
.suspend = maxio_mae0621aq3ci_suspend,
.resume = maxio_mae0621aq3ci_resume,
.remove = maxio_mae0621a_remove,
+ .read_page = maxio_read_page,
+ .write_page = maxio_write_page,
+ .led_hw_is_supported = maxio_mae0621aq3ci_led_hw_is_supported,
+ .led_hw_control_set = maxio_mae0621aq3ci_led_hw_control_set,
+ .led_hw_control_get = maxio_mae0621aq3ci_led_hw_control_get,
},
};