new file mode 100644
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menuconfig LEDS_RTL
+ bool "Realtek LED support"
+ help
+ Say Y to enable support for LED peripherals found on Realtek switch
+ SoCs.
+
+if LEDS_RTL
+
+endif # LED_RTL
new file mode 100644
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_LEDS_RTL) += led-regfield.o
new file mode 100644
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Base support for simple, monochromatic LEDs, configured by a short register
+ * field. The register field should allow the LED to be turned on or off, or
+ * toggled at a predetermined rate with a 50% duty cycle.
+ */
+
+#include <linux/leds.h>
+#include <linux/property.h>
+
+#include "led-regfield.h"
+
+static int regfield_led_set_mode(struct regfield_led *led, unsigned int mode)
+{
+ int err;
+
+ err = regmap_field_write(led->field, mode);
+ if (!err && led->commit)
+ led->commit(led);
+
+ return err;
+}
+
+static void regfield_led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct regfield_led *led = to_regfield_led(led_cdev);
+ bool turn_off = brightness == 0;
+
+ if ((!led->active_low && turn_off) || (led->active_low && !turn_off))
+ regfield_led_set_mode(led, led->modes->off);
+ else
+ regfield_led_set_mode(led, led->modes->on);
+}
+
+static enum led_brightness regfield_led_brightness_get(struct led_classdev *led_cdev)
+{
+ struct regfield_led *led = to_regfield_led(led_cdev);
+ u32 val = 0;
+
+ regmap_field_read(led->field, &val);
+
+ if ((!led->active_low && (val == led->modes->off)) ||
+ (led->active_low && (val == led->modes->on)))
+ return 0;
+ else
+ return 1;
+}
+
+static int regfield_led_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ struct regfield_led *led = to_regfield_led(led_cdev);
+ const struct regfield_led_blink_mode *blink = led->modes->blink;
+ u32 cycle_ms = *delay_on + *delay_off;
+ int err;
+
+ if (cycle_ms == 0)
+ cycle_ms = 500;
+
+ while (blink->toggle_ms && (blink + 1)->toggle_ms) {
+ /*
+ * Split at the arithmetic mean of intervals, which compares
+ * the half cycle interval (cycle_ms / 2) to the mean toggle
+ * interval ((blink->toggle_ms + (blink + 1)->toggle_ms) / 2).
+ * Since the (/ 2) is common on both sides, it can be dropped.
+ */
+ if (cycle_ms < (blink->toggle_ms + (blink + 1)->toggle_ms))
+ break;
+ blink++;
+ }
+
+ err = regfield_led_set_mode(led, blink->mode);
+ if (err)
+ return err;
+
+ *delay_on = blink->toggle_ms;
+ *delay_off = blink->toggle_ms;
+
+ return 0;
+}
+
+int regfield_led_init(struct regfield_led *led, struct regmap_field *field,
+ struct fwnode_handle *fwnode, const struct regfield_led_modes *modes)
+{
+ if (IS_ERR_OR_NULL(field) || !modes)
+ return -EINVAL;
+
+ led->field = field;
+ led->modes = modes;
+ led->active_low = fwnode_property_read_bool(fwnode, "active-low");
+
+ led->cdev.max_brightness = 1;
+ led->cdev.brightness_set = regfield_led_brightness_set;
+ led->cdev.brightness_get = regfield_led_brightness_get;
+ led->cdev.blink_set = regfield_led_blink_set;
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef LEDS_REALTEK_LED_REGFIELD_H
+#define LEDS_REALTEK_LED_REGFIELD_H
+
+#include <linux/device.h>
+#include <linux/fwnode.h>
+#include <linux/leds.h>
+#include <linux/regmap.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 16, 0)
+#include <linux/container_of.h>
+#else
+#include <linux/kernel.h>
+#endif
+
+/*
+ * Register field LED
+ *
+ * Next to being able to turn an LED on or off, Realtek provides LED management
+ * peripherals with hardware accelerated blinking modes with 50% duty cycle.
+ */
+struct regfield_led_blink_mode {
+ u16 toggle_ms; /* Toggle interval in ms */
+ u8 mode; /* ASIC mode bits */
+};
+
+struct regfield_led_modes {
+ u8 off;
+ u8 on;
+ /*
+ * List of blink modes. Must be sorted by interval and terminated by an
+ * entry where regfield_led_blink_mode::toggle_ms equals zero.
+ */
+ struct regfield_led_blink_mode blink[];
+};
+
+struct regfield_led {
+ struct led_classdev cdev;
+ const struct regfield_led_modes *modes;
+ struct regmap_field *field;
+ void (*commit)(struct regfield_led *led);
+ bool active_low;
+};
+
+static inline struct regfield_led *to_regfield_led(struct led_classdev *cdev)
+{
+ return container_of(cdev, struct regfield_led, cdev);
+}
+
+int regfield_led_init(struct regfield_led *led, struct regmap_field *field,
+ struct fwnode_handle *led_node, const struct regfield_led_modes *modes);
+
+#endif
new file mode 100644
@@ -0,0 +1,37 @@
+From d71ec8184236356c50088b00b2417fb142e72bd9 Mon Sep 17 00:00:00 2001
+From: Sander Vanheule <sander@svanheule.net>
+Date: Sun, 10 Jul 2022 11:31:53 +0200
+Subject: leds: add Realtek LED hardware directory
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Realtek LED hardware peripherals share some common features. They are
+typically controlled through a register field, a few bits in size. These
+allow the LED to be turned off or on, or toggled at one of a number of
+intervals.
+
+Signed-off-by: Sander Vanheule <sander@svanheule.net>
+---
+
+--- a/drivers/leds/Kconfig
++++ b/drivers/leds/Kconfig
+@@ -944,4 +944,7 @@ config LEDS_UBNT_LEDBAR
+ comment "LED Triggers"
+ source "drivers/leds/trigger/Kconfig"
+
++comment "Realtek LED drivers"
++source "drivers/leds/realtek/Kconfig"
++
+ endif # NEW_LEDS
+--- a/drivers/leds/Makefile
++++ b/drivers/leds/Makefile
+@@ -107,5 +107,8 @@ obj-$(CONFIG_LEDS_USER) += uleds.o
+ # LED Triggers
+ obj-$(CONFIG_LEDS_TRIGGERS) += trigger/
+
++# Realtek LED drivers
++obj-y += realtek/
++
+ # LED Blink
+ obj-y += blink/
@@ -116,6 +116,7 @@ CONFIG_IRQ_MIPS_CPU=y
CONFIG_IRQ_WORK=y
CONFIG_JFFS2_ZLIB=y
CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_RTL=y
CONFIG_LEGACY_PTYS=y
CONFIG_LEGACY_PTY_COUNT=256
CONFIG_LIBFDT=y
@@ -109,6 +109,7 @@ CONFIG_IRQ_MIPS_CPU=y
CONFIG_IRQ_WORK=y
CONFIG_JFFS2_ZLIB=y
CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_RTL=y
CONFIG_LEGACY_PTYS=y
CONFIG_LEGACY_PTY_COUNT=256
CONFIG_LIBFDT=y
@@ -100,6 +100,7 @@ CONFIG_IRQ_MIPS_CPU=y
CONFIG_IRQ_WORK=y
CONFIG_JFFS2_ZLIB=y
CONFIG_LEDS_GPIO=y
+# CONFIG_LEDS_RTL is not set
CONFIG_LEGACY_PTYS=y
CONFIG_LEGACY_PTY_COUNT=256
CONFIG_LIBFDT=y
@@ -101,6 +101,7 @@ CONFIG_IRQ_MIPS_CPU=y
CONFIG_IRQ_WORK=y
CONFIG_JFFS2_ZLIB=y
CONFIG_LEDS_GPIO=y
+# CONFIG_LEDS_RTL is not set
CONFIG_LEGACY_PTYS=y
CONFIG_LEGACY_PTY_COUNT=256
CONFIG_LIBFDT=y
Realtek LED hardware peripherals share some common features. They are typically controlled through a register field, a few bits in size. These allow the LED to be turned off or on, or toggled at one of a number of intervals. Support for register field LEDs is added in a new directory, so Realtek LEDs drivers can be grouped together. Signed-off-by: Sander Vanheule <sander@svanheule.net> --- Changes in v3: - Merge symbol selection updates to ease bisecting --- .../files-5.10/drivers/leds/realtek/Kconfig | 10 ++ .../files-5.10/drivers/leds/realtek/Makefile | 2 + .../drivers/leds/realtek/led-regfield.c | 99 +++++++++++++++++++ .../drivers/leds/realtek/led-regfield.h | 55 +++++++++++ ...s-add-Realtek-LED-hardware-directory.patch | 37 +++++++ target/linux/realtek/rtl838x/config-5.10 | 1 + target/linux/realtek/rtl839x/config-5.10 | 1 + target/linux/realtek/rtl930x/config-5.10 | 1 + target/linux/realtek/rtl931x/config-5.10 | 1 + 9 files changed, 207 insertions(+) create mode 100644 target/linux/realtek/files-5.10/drivers/leds/realtek/Kconfig create mode 100644 target/linux/realtek/files-5.10/drivers/leds/realtek/Makefile create mode 100644 target/linux/realtek/files-5.10/drivers/leds/realtek/led-regfield.c create mode 100644 target/linux/realtek/files-5.10/drivers/leds/realtek/led-regfield.h create mode 100644 target/linux/realtek/patches-5.10/202-leds-add-Realtek-LED-hardware-directory.patch