mirror of
https://github.com/kernelkit/infix.git
synced 2026-08-06 23:43:20 +02:00
- Add missing locking around standard counter groups - Fix reference counting issue with IGMPv3/MLDv2 reports Found as part of debugging the regression test suite on hardware.
316 lines
8.5 KiB
Diff
316 lines
8.5 KiB
Diff
From 7750d0a5bfae46ab783e8e7087b70175860f808c Mon Sep 17 00:00:00 2001
|
|
From: Tobias Waldekranz <tobias@waldekranz.com>
|
|
Date: Thu, 16 Nov 2023 19:44:32 +0100
|
|
Subject: [PATCH 09/30] net: dsa: mv88e6xxx: Add LED infrastructure
|
|
Organization: Addiva Elektronik
|
|
|
|
Parse DT for LEDs and register them for devices that support it,
|
|
though no actual implementations exist yet.
|
|
|
|
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
|
|
---
|
|
drivers/net/dsa/mv88e6xxx/Makefile | 1 +
|
|
drivers/net/dsa/mv88e6xxx/chip.c | 5 +-
|
|
drivers/net/dsa/mv88e6xxx/chip.h | 16 +++
|
|
drivers/net/dsa/mv88e6xxx/leds.c | 184 +++++++++++++++++++++++++++++
|
|
drivers/net/dsa/mv88e6xxx/leds.h | 12 ++
|
|
5 files changed, 217 insertions(+), 1 deletion(-)
|
|
create mode 100644 drivers/net/dsa/mv88e6xxx/leds.c
|
|
create mode 100644 drivers/net/dsa/mv88e6xxx/leds.h
|
|
|
|
diff --git a/drivers/net/dsa/mv88e6xxx/Makefile b/drivers/net/dsa/mv88e6xxx/Makefile
|
|
index a9a9651187db..6720d9303914 100644
|
|
--- a/drivers/net/dsa/mv88e6xxx/Makefile
|
|
+++ b/drivers/net/dsa/mv88e6xxx/Makefile
|
|
@@ -9,6 +9,7 @@ mv88e6xxx-objs += global2.o
|
|
mv88e6xxx-objs += global2_avb.o
|
|
mv88e6xxx-objs += global2_scratch.o
|
|
mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += hwtstamp.o
|
|
+mv88e6xxx-objs += leds.o
|
|
mv88e6xxx-objs += pcs-6185.o
|
|
mv88e6xxx-objs += pcs-6352.o
|
|
mv88e6xxx-objs += pcs-639x.o
|
|
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
|
|
index f4b61fcd4541..a3016d5e005d 100644
|
|
--- a/drivers/net/dsa/mv88e6xxx/chip.c
|
|
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
|
|
@@ -37,6 +37,7 @@
|
|
#include "global1.h"
|
|
#include "global2.h"
|
|
#include "hwtstamp.h"
|
|
+#include "leds.h"
|
|
#include "phy.h"
|
|
#include "port.h"
|
|
#include "ptp.h"
|
|
@@ -4012,7 +4013,9 @@ static int mv88e6xxx_port_setup(struct dsa_switch *ds, int port)
|
|
return err;
|
|
}
|
|
|
|
- return mv88e6xxx_setup_devlink_regions_port(ds, port);
|
|
+ err = err ? : mv88e6xxx_setup_devlink_regions_port(ds, port);
|
|
+ err = err ? : mv88e6xxx_port_setup_leds(ds, port);
|
|
+ return err;
|
|
}
|
|
|
|
static void mv88e6xxx_port_teardown(struct dsa_switch *ds, int port)
|
|
diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h
|
|
index 85eb293381a7..d0dca152e714 100644
|
|
--- a/drivers/net/dsa/mv88e6xxx/chip.h
|
|
+++ b/drivers/net/dsa/mv88e6xxx/chip.h
|
|
@@ -206,6 +206,7 @@ struct mv88e6xxx_gpio_ops;
|
|
struct mv88e6xxx_avb_ops;
|
|
struct mv88e6xxx_ptp_ops;
|
|
struct mv88e6xxx_pcs_ops;
|
|
+struct mv88e6xxx_led_ops;
|
|
|
|
struct mv88e6xxx_irq {
|
|
u16 masked;
|
|
@@ -329,6 +330,8 @@ struct mv88e6xxx_hw_stat {
|
|
int type;
|
|
};
|
|
|
|
+struct mv88e6xxx_led;
|
|
+
|
|
struct mv88e6xxx_chip {
|
|
const struct mv88e6xxx_info *info;
|
|
|
|
@@ -653,6 +656,9 @@ struct mv88e6xxx_ops {
|
|
/* Precision Time Protocol operations */
|
|
const struct mv88e6xxx_ptp_ops *ptp_ops;
|
|
|
|
+ /* LED operations */
|
|
+ const struct mv88e6xxx_led_ops *led_ops;
|
|
+
|
|
/* Phylink */
|
|
void (*phylink_get_caps)(struct mv88e6xxx_chip *chip, int port,
|
|
struct phylink_config *config);
|
|
@@ -736,7 +742,17 @@ struct mv88e6xxx_pcs_ops {
|
|
void (*pcs_teardown)(struct mv88e6xxx_chip *chip, int port);
|
|
struct phylink_pcs *(*pcs_select)(struct mv88e6xxx_chip *chip, int port,
|
|
phy_interface_t mode);
|
|
+};
|
|
|
|
+struct mv88e6xxx_led_ops {
|
|
+ int (*brightness_set)(struct mv88e6xxx_led *led,
|
|
+ enum led_brightness brightness);
|
|
+ int (*blink_set)(struct mv88e6xxx_led *led,
|
|
+ unsigned long *delay_on, unsigned long *delay_off);
|
|
+ int (*hw_control_is_supported)(struct mv88e6xxx_led *led,
|
|
+ unsigned long flags);
|
|
+ int (*hw_control_set)(struct mv88e6xxx_led *led, unsigned long flags);
|
|
+ int (*hw_control_get)(struct mv88e6xxx_led *led, unsigned long *flags);
|
|
};
|
|
|
|
static inline bool mv88e6xxx_has_stu(struct mv88e6xxx_chip *chip)
|
|
diff --git a/drivers/net/dsa/mv88e6xxx/leds.c b/drivers/net/dsa/mv88e6xxx/leds.c
|
|
new file mode 100644
|
|
index 000000000000..42e1661e1f47
|
|
--- /dev/null
|
|
+++ b/drivers/net/dsa/mv88e6xxx/leds.c
|
|
@@ -0,0 +1,184 @@
|
|
+// SPDX-License-Identifier: GPL-2.0-or-later
|
|
+#include <net/dsa.h>
|
|
+
|
|
+#include "chip.h"
|
|
+#include "port.h"
|
|
+
|
|
+struct mv88e6xxx_led {
|
|
+ struct mv88e6xxx_chip *chip;
|
|
+ int port;
|
|
+ u8 index;
|
|
+
|
|
+ struct led_classdev ldev;
|
|
+};
|
|
+
|
|
+static int mv88e6xxx_led_brightness_set(struct led_classdev *ldev,
|
|
+ enum led_brightness brightness)
|
|
+{
|
|
+ const struct mv88e6xxx_led_ops *ops;
|
|
+ struct mv88e6xxx_led *led;
|
|
+
|
|
+ led = container_of(ldev, struct mv88e6xxx_led, ldev);
|
|
+ ops = led->chip->info->ops->led_ops;
|
|
+
|
|
+ if (!ops->brightness_set)
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ return ops->brightness_set(led, brightness);
|
|
+}
|
|
+
|
|
+static int mv88e6xxx_led_blink_set(struct led_classdev *ldev,
|
|
+ unsigned long *delay_on,
|
|
+ unsigned long *delay_off)
|
|
+{
|
|
+ const struct mv88e6xxx_led_ops *ops;
|
|
+ struct mv88e6xxx_led *led;
|
|
+
|
|
+ led = container_of(ldev, struct mv88e6xxx_led, ldev);
|
|
+ ops = led->chip->info->ops->led_ops;
|
|
+
|
|
+ if (!ops->blink_set)
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ return ops->blink_set(led, delay_on, delay_off);
|
|
+}
|
|
+
|
|
+static int mv88e6xxx_led_hw_control_is_supported(struct led_classdev *ldev,
|
|
+ unsigned long flags)
|
|
+{
|
|
+ const struct mv88e6xxx_led_ops *ops;
|
|
+ struct mv88e6xxx_led *led;
|
|
+
|
|
+ led = container_of(ldev, struct mv88e6xxx_led, ldev);
|
|
+ ops = led->chip->info->ops->led_ops;
|
|
+
|
|
+ if (!ops->hw_control_is_supported)
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ return ops->hw_control_is_supported(led, flags);
|
|
+}
|
|
+
|
|
+static int mv88e6xxx_led_hw_control_set(struct led_classdev *ldev,
|
|
+ unsigned long flags)
|
|
+{
|
|
+ const struct mv88e6xxx_led_ops *ops;
|
|
+ struct mv88e6xxx_led *led;
|
|
+
|
|
+ led = container_of(ldev, struct mv88e6xxx_led, ldev);
|
|
+ ops = led->chip->info->ops->led_ops;
|
|
+
|
|
+ if (!ops->hw_control_set)
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ return ops->hw_control_set(led, flags);
|
|
+}
|
|
+
|
|
+static int mv88e6xxx_led_hw_control_get(struct led_classdev *ldev,
|
|
+ unsigned long *flags)
|
|
+{
|
|
+ const struct mv88e6xxx_led_ops *ops;
|
|
+ struct mv88e6xxx_led *led;
|
|
+
|
|
+ led = container_of(ldev, struct mv88e6xxx_led, ldev);
|
|
+ ops = led->chip->info->ops->led_ops;
|
|
+
|
|
+ if (!ops->hw_control_get)
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ return ops->hw_control_get(led, flags);
|
|
+}
|
|
+
|
|
+static struct device *mv88e6xxx_led_hw_control_get_device(struct led_classdev *ldev)
|
|
+{
|
|
+ struct mv88e6xxx_led *led;
|
|
+ struct dsa_port *dp;
|
|
+
|
|
+ led = container_of(ldev, struct mv88e6xxx_led, ldev);
|
|
+ dp = dsa_to_port(led->chip->ds, led->port);
|
|
+
|
|
+ if (dp && dp->slave)
|
|
+ return &dp->slave->dev;
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static int mv88e6xxx_port_setup_led(struct mv88e6xxx_chip *chip, int port,
|
|
+ struct device_node *np)
|
|
+{
|
|
+ struct led_init_data init_data = {};
|
|
+ struct mv88e6xxx_led *led;
|
|
+ char *devname;
|
|
+ u32 index;
|
|
+ int err;
|
|
+
|
|
+ err = of_property_read_u32(np, "reg", &index);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ if (index >= 2)
|
|
+ return -EINVAL;
|
|
+
|
|
+ led = devm_kzalloc(chip->dev, sizeof(*led), GFP_KERNEL);
|
|
+ if (!led)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ *led = (struct mv88e6xxx_led) {
|
|
+ .chip = chip,
|
|
+ .port = port,
|
|
+ .index = index,
|
|
+
|
|
+ .ldev = {
|
|
+ .max_brightness = 1,
|
|
+ .brightness_set_blocking = mv88e6xxx_led_brightness_set,
|
|
+ .blink_set = mv88e6xxx_led_blink_set,
|
|
+
|
|
+#ifdef CONFIG_LEDS_TRIGGERS
|
|
+ .hw_control_trigger = "netdev",
|
|
+ .hw_control_get_device = mv88e6xxx_led_hw_control_get_device,
|
|
+
|
|
+ .hw_control_is_supported = mv88e6xxx_led_hw_control_is_supported,
|
|
+ .hw_control_set = mv88e6xxx_led_hw_control_set,
|
|
+ .hw_control_get = mv88e6xxx_led_hw_control_get,
|
|
+#endif
|
|
+ },
|
|
+ };
|
|
+
|
|
+ devname = devm_kasprintf(chip->dev, GFP_KERNEL, "%s.%d",
|
|
+ dev_name(chip->dev), port);
|
|
+ if (!devname)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ init_data = (struct led_init_data) {
|
|
+ .fwnode = of_fwnode_handle(np),
|
|
+ .devname_mandatory = true,
|
|
+ .devicename = devname,
|
|
+ };
|
|
+
|
|
+ return devm_led_classdev_register_ext(chip->dev, &led->ldev, &init_data);
|
|
+}
|
|
+
|
|
+int mv88e6xxx_port_setup_leds(struct dsa_switch *ds, int port)
|
|
+{
|
|
+ struct dsa_port *dp = dsa_to_port(ds, port);
|
|
+ struct mv88e6xxx_chip *chip = ds->priv;
|
|
+ struct device_node *pnp, *np;
|
|
+ int err;
|
|
+
|
|
+ if (!chip->info->ops->led_ops)
|
|
+ return 0;
|
|
+
|
|
+ if (!dp->dn)
|
|
+ return 0;
|
|
+
|
|
+ pnp = of_get_child_by_name(dp->dn, "leds");
|
|
+ if (!pnp)
|
|
+ return 0;
|
|
+
|
|
+ for_each_available_child_of_node(pnp, np) {
|
|
+ err = mv88e6xxx_port_setup_led(chip, port, np);
|
|
+ if (err)
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff --git a/drivers/net/dsa/mv88e6xxx/leds.h b/drivers/net/dsa/mv88e6xxx/leds.h
|
|
new file mode 100644
|
|
index 000000000000..8afa2a0c0527
|
|
--- /dev/null
|
|
+++ b/drivers/net/dsa/mv88e6xxx/leds.h
|
|
@@ -0,0 +1,12 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
+
|
|
+/* Marvell 88E6xxx Switch leds support. */
|
|
+
|
|
+#ifndef _MV88E6XXX_LEDS_H
|
|
+#define _MV88E6XXX_LEDS_H
|
|
+
|
|
+#include "chip.h"
|
|
+
|
|
+int mv88e6xxx_port_setup_leds(struct dsa_switch *ds, int port);
|
|
+
|
|
+#endif /* _MV88E6XXX_LEDS_H */
|
|
--
|
|
2.34.1
|
|
|