rtl930x: Rework per port LED configuration

Use led_setX to determine number of LEDs per port. Introduce macros to
calculate register value and shift for particular LED in a particular
set.

Problem with previous implementation is that it uses is10G status to
determine leds per port. However with usxgmii, driver sets 10g, 5g and
2.5g so even though there are only 2 leds per port it selects 4 leds per
port

This implementation relies on configured led_set node.

Acked-by: Simon Wunderlich <sw@simonwunderlich.de>
Signed-off-by: Harshal Gohel <hg@simonwunderlich.de>
This commit is contained in:
Harshal Gohel 2023-12-12 15:48:56 +01:00 committed by Sander Vanheule
parent a376508216
commit 2cfb1ecf10
4 changed files with 65 additions and 29 deletions

View File

@ -66,10 +66,10 @@
compatible = "realtek,rtl9300-leds";
active-low;
led_set0 = <0x0000 0xffff 0x0a20 0x0b80>; // LED set 0: 1000Mbps, 10/100Mbps
led_set0 = <0x0a20 0x0b80>; // LED set 0: 1000Mbps, 10/100Mbps
led_set1 = <0x0a0b 0x0a28 0x0a82 0x0a0b>; // LED set 1: (10G, 5G, 2.5G) (2.5G, 1G)
// (5G, 10/100) (10G, 5G, 2.5G)
led_set2 = <0x0000 0xffff 0x0a20 0x0a01>; // LED set 2: 1000MBit, 10GBit
led_set2 = <0x0a20 0x0a01>; // LED set 2: 1000MBit, 10GBit
};
};

View File

@ -271,7 +271,7 @@ int write_phy(u32 port, u32 page, u32 reg, u32 val)
static int __init rtl83xx_mdio_probe(struct rtl838x_switch_priv *priv)
{
struct device *dev = priv->dev;
struct device_node *dn, *phy_node, *mii_np = dev->of_node;
struct device_node *dn, *phy_node, *led_node, *mii_np = dev->of_node;
struct mii_bus *bus;
int ret;
u32 pn;
@ -325,9 +325,12 @@ static int __init rtl83xx_mdio_probe(struct rtl838x_switch_priv *priv)
return -ENODEV;
}
led_node = of_find_compatible_node(NULL, NULL, "realtek,rtl9300-leds");
for_each_node_by_name(dn, "port") {
phy_interface_t interface;
u32 led_set;
char led_set_str[16] = {0};
if (!of_device_is_available(dn))
continue;
@ -355,9 +358,18 @@ static int __init rtl83xx_mdio_probe(struct rtl838x_switch_priv *priv)
if (interface == PHY_INTERFACE_MODE_10GBASER)
priv->ports[pn].is10G = true;
if (of_property_read_u32(dn, "led-set", &led_set))
led_set = 0;
priv->ports[pn].led_set = led_set;
priv->ports[pn].leds_on_this_port = 0;
if (led_node) {
if (of_property_read_u32(dn, "led-set", &led_set))
led_set = 0;
priv->ports[pn].led_set = led_set;
sprintf(led_set_str, "led_set%d", led_set);
priv->ports[pn].leds_on_this_port = of_property_count_u32_elems(led_node, led_set_str);
if (priv->ports[pn].leds_on_this_port > 4) {
dev_err(priv->dev, "led_set %d for port %d configuration is invalid\n", led_set, pn);
return -ENODEV;
}
}
/* Check for the integrated SerDes of the RTL8380M first */
if (of_property_read_bool(phy_node, "phy-is-integrated")

View File

@ -635,6 +635,7 @@ struct rtl838x_port {
bool is2G5;
int sds_num;
int led_set;
int leds_on_this_port;
const struct dsa_port *dp;
};

View File

@ -24,6 +24,15 @@
#define RTL930X_LED_GLB_ACTIVE_LOW BIT(22)
#define RTL930X_LED_SETX_0_CTRL(x) (RTL930X_LED_SET0_0_CTRL - (x * 8))
#define RTL930X_LED_SETX_1_CTRL(x) (RTL930X_LED_SETX_0_CTRL(x) - 4)
/* get register for given set and led in the set */
#define RTL930X_LED_SETX_LEDY(x,y) (RTL930X_LED_SETX_0_CTRL(x) - 4 * (y / 2))
/* get shift for given led in any set */
#define RTL930X_LED_SET_LEDX_SHIFT(x) (16 * (x % 2))
extern struct mutex smi_lock;
extern struct rtl83xx_soc_info soc_info;
@ -2396,10 +2405,44 @@ static void rtl930x_led_init(struct rtl838x_switch_priv *priv)
return;
}
for (int set = 0; set < 4; set++) {
char set_name[16] = {0};
u32 set_config[4];
int leds_in_this_set = 0;
/* Reset LED set configuration */
sw_w32(0, RTL930X_LED_SETX_0_CTRL(set));
sw_w32(0, RTL930X_LED_SETX_1_CTRL(set));
/**
* Each led set has 4 number of leds, and each LED is configured with 16 bits
* So each 32bit register holds configuration for 2 leds
* And therefore each set requires 2 registers for configuring 4 LEDs
*
*/
sprintf(set_name, "led_set%d", set);
leds_in_this_set = of_property_count_u32_elems(node, set_name);
if (leds_in_this_set == 0 || leds_in_this_set > sizeof(set_config)) {
pr_err("%s led_set configuration invalid skipping over this set\n", __func__);
continue;
}
if (of_property_read_u32_array(node, set_name, set_config, leds_in_this_set)) {
break;
}
/* Write configuration as per number of LEDs */
for (int i=0, led = leds_in_this_set-1; led >= 0; led--,i++) {
sw_w32_mask(0xffff << RTL930X_LED_SET_LEDX_SHIFT(led),
(0xffff & set_config[i]) << RTL930X_LED_SET_LEDX_SHIFT(led),
RTL930X_LED_SETX_LEDY(set, led));
}
}
for (int i = 0; i < priv->cpu_port; i++) {
int pos = (i << 1) % 32;
u32 set;
u32 v;
sw_w32_mask(0x3 << pos, 0, RTL930X_LED_PORT_FIB_SET_SEL_CTRL(i));
sw_w32_mask(0x3 << pos, 0, RTL930X_LED_PORT_COPR_SET_SEL_CTRL(i));
@ -2407,12 +2450,8 @@ static void rtl930x_led_init(struct rtl838x_switch_priv *priv)
if (!priv->ports[i].phy)
continue;
v = 0x1;
if (priv->ports[i].is10G)
v = 0x3;
if (priv->ports[i].phy_is_integrated)
v = 0x1;
sw_w32_mask(0x3 << pos, v << pos, RTL930X_LED_PORT_NUM_CTRL(i));
/* 0x0 = 1 led, 0x1 = 2 leds, 0x2 = 3 leds, 0x3 = 4 leds per port */
sw_w32_mask(0x3 << pos, (priv->ports[i].leds_on_this_port -1) << pos, RTL930X_LED_PORT_NUM_CTRL(i));
pm |= BIT(i);
@ -2421,22 +2460,6 @@ static void rtl930x_led_init(struct rtl838x_switch_priv *priv)
sw_w32_mask(0, set << pos, RTL930X_LED_PORT_FIB_SET_SEL_CTRL(i));
}
for (int i = 0; i < 4; i++) {
const __be32 *led_set;
char set_name[9];
u32 setlen;
u32 v;
sprintf(set_name, "led_set%d", i);
led_set = of_get_property(node, set_name, &setlen);
if (!led_set || setlen != 16)
break;
v = be32_to_cpup(led_set) << 16 | be32_to_cpup(led_set + 1);
sw_w32(v, RTL930X_LED_SET0_0_CTRL - 4 - i * 8);
v = be32_to_cpup(led_set + 2) << 16 | be32_to_cpup(led_set + 3);
sw_w32(v, RTL930X_LED_SET0_0_CTRL - i * 8);
}
/* Set LED mode to serial (0x1) */
sw_w32_mask(0x3, 0x1, RTL930X_LED_GLB_CTRL);