[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20240103103351.1188835-3-tobias@waldekranz.com>
Date: Wed, 3 Jan 2024 11:33:51 +0100
From: Tobias Waldekranz <tobias@...dekranz.com>
To: davem@...emloft.net,
kuba@...nel.org
Cc: andrew@...n.ch,
f.fainelli@...il.com,
olteanv@...il.com,
netdev@...r.kernel.org
Subject: [PATCH net-next 2/2] net: dsa: mv88e6xxx: Add LED support for 6393X
Trigger support:
- "none"
- "timer"
- "netdev"
Signed-off-by: Tobias Waldekranz <tobias@...dekranz.com>
---
drivers/net/dsa/mv88e6xxx/chip.c | 1 +
drivers/net/dsa/mv88e6xxx/leds.c | 227 +++++++++++++++++++++++++++++++
drivers/net/dsa/mv88e6xxx/leds.h | 2 +
drivers/net/dsa/mv88e6xxx/port.c | 33 +++++
drivers/net/dsa/mv88e6xxx/port.h | 7 +
5 files changed, 270 insertions(+)
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 8fab16badc9e..f43dc863999a 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -5495,6 +5495,7 @@ static const struct mv88e6xxx_ops mv88e6393x_ops = {
.gpio_ops = &mv88e6352_gpio_ops,
.avb_ops = &mv88e6390_avb_ops,
.ptp_ops = &mv88e6352_ptp_ops,
+ .led_ops = &mv88e6393x_led_ops,
.phylink_get_caps = mv88e6393x_phylink_get_caps,
.pcs_ops = &mv88e6393x_pcs_ops,
};
diff --git a/drivers/net/dsa/mv88e6xxx/leds.c b/drivers/net/dsa/mv88e6xxx/leds.c
index 1f331a632065..f964b3a7a3e7 100644
--- a/drivers/net/dsa/mv88e6xxx/leds.c
+++ b/drivers/net/dsa/mv88e6xxx/leds.c
@@ -4,6 +4,13 @@
#include "chip.h"
#include "port.h"
+#define FLAG_ACT (BIT(TRIGGER_NETDEV_RX) | BIT(TRIGGER_NETDEV_TX))
+#define FLAG_LINK BIT(TRIGGER_NETDEV_LINK)
+#define FLAG_LINK_10 BIT(TRIGGER_NETDEV_LINK_10)
+#define FLAG_LINK_100 BIT(TRIGGER_NETDEV_LINK_100)
+#define FLAG_LINK_1G BIT(TRIGGER_NETDEV_LINK_1000)
+#define FLAG_FULL BIT(TRIGGER_NETDEV_FULL_DUPLEX)
+
struct mv88e6xxx_led {
struct mv88e6xxx_chip *chip;
int port;
@@ -23,6 +30,226 @@ struct mv88e6xxx_led_ops {
int (*hw_control_get)(struct mv88e6xxx_led *led, unsigned long *flags);
};
+enum mv88e6393x_led_mode {
+ MV88E6393X_LED_MODE_BLINK = 0xd,
+ MV88E6393X_LED_MODE_OFF = 0xe,
+ MV88E6393X_LED_MODE_ON = 0xf,
+
+ MV88E6393X_LED_MODES = 0x10
+};
+
+static const unsigned long mv88e6393x_led_map_p1_p8[2][MV88E6393X_LED_MODES] = {
+ {
+ [0x1] = FLAG_ACT | FLAG_LINK_100 | FLAG_LINK_1G,
+ [0x2] = FLAG_ACT | FLAG_LINK_1G,
+ [0x3] = FLAG_ACT | FLAG_LINK,
+ [0x6] = FLAG_FULL,
+ [0x7] = FLAG_ACT | FLAG_LINK_10 | FLAG_LINK_1G,
+ [0x8] = FLAG_LINK,
+ [0x9] = FLAG_LINK_10,
+ [0xa] = FLAG_ACT | FLAG_LINK_10,
+ [0xb] = FLAG_LINK_100 | FLAG_LINK_1G,
+ },
+ {
+ [0x1] = FLAG_ACT,
+ [0x2] = FLAG_ACT | FLAG_LINK_10 | FLAG_LINK_100,
+ [0x3] = FLAG_LINK_1G,
+ [0x5] = FLAG_ACT | FLAG_LINK,
+ [0x6] = FLAG_ACT | FLAG_LINK_10 | FLAG_LINK_1G,
+ [0x7] = FLAG_LINK_10 | FLAG_LINK_1G,
+ [0x9] = FLAG_LINK_100,
+ [0xa] = FLAG_ACT | FLAG_LINK_100,
+ [0xb] = FLAG_LINK_10 | FLAG_LINK_100,
+ }
+};
+
+static const unsigned long mv88e6393x_led_map_p9_p10[2][MV88E6393X_LED_MODES] = {
+ {
+ [0x1] = FLAG_ACT | FLAG_LINK,
+ },
+ {
+ [0x6] = FLAG_FULL,
+ [0x7] = FLAG_ACT | FLAG_LINK,
+ [0x8] = FLAG_LINK,
+ }
+};
+
+const unsigned long *mv88e6393x_led_map(struct mv88e6xxx_led *led)
+{
+ switch (led->port) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ return mv88e6393x_led_map_p1_p8[led->index];
+ case 9:
+ case 10:
+ return mv88e6393x_led_map_p9_p10[led->index];
+ }
+
+ return NULL;
+}
+
+static int mv88e6393x_led_flags_to_mode(struct mv88e6xxx_led *led, unsigned long flags)
+{
+ const unsigned long *map = mv88e6393x_led_map(led);
+ int i;
+
+ if (!map)
+ return -ENODEV;
+
+ if (!flags)
+ return MV88E6393X_LED_MODE_OFF;
+
+ for (i = 0; i < MV88E6393X_LED_MODES; i++) {
+ if (map[i] == flags)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int mv88e6393x_led_mode_to_flags(struct mv88e6xxx_led *led, u8 mode,
+ unsigned long *flags)
+{
+ const unsigned long *map = mv88e6393x_led_map(led);
+
+ if (!map)
+ return -ENODEV;
+
+ if (mode == MV88E6393X_LED_MODE_OFF) {
+ *flags = 0;
+ return 0;
+ }
+
+ if (map[mode]) {
+ *flags = map[mode];
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int mv88e6393x_led_set(struct mv88e6xxx_led *led, int mode)
+{
+ u16 ctrl;
+ int err;
+
+ if (mode < 0)
+ return mode;
+
+ mv88e6xxx_reg_lock(led->chip);
+
+ err = mv88e6393x_port_led_read(led->chip, led->port, 0, &ctrl);
+ if (err)
+ goto out;
+
+ switch (led->index) {
+ case 0:
+ ctrl &= ~0x0f;
+ ctrl |= mode;
+ break;
+ case 1:
+ ctrl &= ~0xf0;
+ ctrl |= mode << 4;
+ }
+
+ err = mv88e6393x_port_led_write(led->chip, led->port, 0, ctrl);
+out:
+ mv88e6xxx_reg_unlock(led->chip);
+ return err;
+}
+
+static int mv88e6393x_led_get(struct mv88e6xxx_led *led)
+{
+ u16 ctrl;
+ int err;
+
+ mv88e6xxx_reg_lock(led->chip);
+ err = mv88e6393x_port_led_read(led->chip, led->port, 0, &ctrl);
+ mv88e6xxx_reg_unlock(led->chip);
+ if (err)
+ return err;
+
+ switch (led->index) {
+ case 0:
+ return ctrl & 0xf;
+ case 1:
+ return (ctrl >> 4) & 0xf;
+ }
+
+ return -EINVAL;
+}
+
+static int mv88e6393x_led_brightness_set(struct mv88e6xxx_led *led,
+ enum led_brightness brightness)
+{
+ if (brightness == LED_OFF)
+ return mv88e6393x_led_set(led, MV88E6393X_LED_MODE_OFF);
+
+ return mv88e6393x_led_set(led, MV88E6393X_LED_MODE_ON);
+}
+
+static int mv88e6393x_led_blink_set(struct mv88e6xxx_led *led,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ int err;
+
+ /* Defer anything other than 50% duty cycles to software */
+ if (*delay_on != *delay_off)
+ return -EINVAL;
+
+ /* Reject values outside ~20% of our default rate (84ms) */
+ if (*delay_on && ((*delay_on < 30) || (*delay_on > 50)))
+ return -EINVAL;
+
+ err = mv88e6393x_led_set(led, MV88E6393X_LED_MODE_BLINK);
+ if (!err)
+ *delay_on = *delay_off = 42;
+
+ return err;
+}
+
+static int mv88e6393x_led_hw_control_is_supported(struct mv88e6xxx_led *led,
+ unsigned long flags)
+{
+ int mode = mv88e6393x_led_flags_to_mode(led, flags);
+
+ return (mode < 0) ? mode : 0;
+}
+
+static int mv88e6393x_led_hw_control_set(struct mv88e6xxx_led *led,
+ unsigned long flags)
+{
+ int mode = mv88e6393x_led_flags_to_mode(led, flags);
+
+ return mv88e6393x_led_set(led, mode);
+}
+
+static int mv88e6393x_led_hw_control_get(struct mv88e6xxx_led *led,
+ unsigned long *flags)
+{
+ int mode = mv88e6393x_led_get(led);
+
+ if (mode < 0)
+ return mode;
+
+ return mv88e6393x_led_mode_to_flags(led, mode, flags);
+}
+
+const struct mv88e6xxx_led_ops mv88e6393x_led_ops = {
+ .brightness_set = mv88e6393x_led_brightness_set,
+ .blink_set = mv88e6393x_led_blink_set,
+ .hw_control_is_supported = mv88e6393x_led_hw_control_is_supported,
+ .hw_control_set = mv88e6393x_led_hw_control_set,
+ .hw_control_get = mv88e6393x_led_hw_control_get,
+};
+
static int mv88e6xxx_led_brightness_set(struct led_classdev *ldev,
enum led_brightness brightness)
{
diff --git a/drivers/net/dsa/mv88e6xxx/leds.h b/drivers/net/dsa/mv88e6xxx/leds.h
index a99d7a5ebc6d..c7de16ce4dde 100644
--- a/drivers/net/dsa/mv88e6xxx/leds.h
+++ b/drivers/net/dsa/mv88e6xxx/leds.h
@@ -7,6 +7,8 @@
#include "chip.h"
+extern const struct mv88e6xxx_led_ops mv88e6393x_led_ops;
+
int mv88e6xxx_port_setup_leds(struct dsa_switch *ds, int port);
#endif /* _MV88E6XXX_LEDS_H */
diff --git a/drivers/net/dsa/mv88e6xxx/port.c b/drivers/net/dsa/mv88e6xxx/port.c
index 5394a8cf7bf1..66073a1ef260 100644
--- a/drivers/net/dsa/mv88e6xxx/port.c
+++ b/drivers/net/dsa/mv88e6xxx/port.c
@@ -1539,6 +1539,39 @@ int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port,
return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_ETH_TYPE, etype);
}
+/* Offset 0x16: LED Control Register */
+
+int mv88e6393x_port_led_write(struct mv88e6xxx_chip *chip, int port,
+ unsigned int pointer, u16 data)
+{
+ u16 cmd = BIT(15) | ((pointer & 0x7) << 12) | (data & 0x7ff);
+ int err;
+
+ err = mv88e6xxx_port_write(chip, port, MV88E6393X_PORT_LED_CONTROL, cmd);
+ if (err)
+ return err;
+
+ return mv88e6xxx_port_wait_bit(chip, port, MV88E6393X_PORT_LED_CONTROL, 15, 0);
+}
+
+int mv88e6393x_port_led_read(struct mv88e6xxx_chip *chip, int port,
+ unsigned int pointer, u16 *data)
+{
+ u16 cmd = (pointer & 0x7) << 12;
+ int err;
+
+ err = mv88e6xxx_port_write(chip, port, MV88E6393X_PORT_LED_CONTROL, cmd);
+ if (err)
+ return err;
+
+ err = mv88e6xxx_port_read(chip, port, MV88E6393X_PORT_LED_CONTROL, &cmd);
+ if (err)
+ return err;
+
+ *data = cmd & 0x7ff;
+ return 0;
+}
+
/* Offset 0x18: Port IEEE Priority Remapping Registers [0-3]
* Offset 0x19: Port IEEE Priority Remapping Registers [4-7]
*/
diff --git a/drivers/net/dsa/mv88e6xxx/port.h b/drivers/net/dsa/mv88e6xxx/port.h
index 86deeb347cbc..3605a59d1399 100644
--- a/drivers/net/dsa/mv88e6xxx/port.h
+++ b/drivers/net/dsa/mv88e6xxx/port.h
@@ -294,6 +294,9 @@
/* Offset 0x13: OutFiltered Counter */
#define MV88E6XXX_PORT_OUT_FILTERED 0x13
+/* Offset 0x16: LED Control Register */
+#define MV88E6393X_PORT_LED_CONTROL 0x16
+
/* Offset 0x18: IEEE Priority Mapping Table */
#define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE 0x18
#define MV88E6390_PORT_IEEE_PRIO_MAP_TABLE_UPDATE 0x8000
@@ -410,6 +413,10 @@ int mv88e6393x_port_set_policy(struct mv88e6xxx_chip *chip, int port,
enum mv88e6xxx_policy_action action);
int mv88e6351_port_set_ether_type(struct mv88e6xxx_chip *chip, int port,
u16 etype);
+int mv88e6393x_port_led_write(struct mv88e6xxx_chip *chip, int port,
+ unsigned int pointer, u16 data);
+int mv88e6393x_port_led_read(struct mv88e6xxx_chip *chip, int port,
+ unsigned int pointer, u16 *data);
int mv88e6393x_set_egress_port(struct mv88e6xxx_chip *chip,
enum mv88e6xxx_egress_direction direction,
int port);
--
2.34.1
Powered by blists - more mailing lists