realtek: add base register field LED support
authorSander Vanheule <sander@svanheule.net>
Wed, 28 Sep 2022 19:08:18 +0000 (21:08 +0200)
committerSander Vanheule <sander@svanheule.net>
Mon, 31 Oct 2022 17:52:26 +0000 (18:52 +0100)
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>
target/linux/realtek/files-5.10/drivers/leds/realtek/Kconfig [new file with mode: 0644]
target/linux/realtek/files-5.10/drivers/leds/realtek/Makefile [new file with mode: 0644]
target/linux/realtek/files-5.10/drivers/leds/realtek/led-regfield.c [new file with mode: 0644]
target/linux/realtek/files-5.10/drivers/leds/realtek/led-regfield.h [new file with mode: 0644]
target/linux/realtek/patches-5.10/202-leds-add-Realtek-LED-hardware-directory.patch [new file with mode: 0644]
target/linux/realtek/rtl838x/config-5.10
target/linux/realtek/rtl839x/config-5.10
target/linux/realtek/rtl930x/config-5.10
target/linux/realtek/rtl931x/config-5.10

diff --git a/target/linux/realtek/files-5.10/drivers/leds/realtek/Kconfig b/target/linux/realtek/files-5.10/drivers/leds/realtek/Kconfig
new file mode 100644 (file)
index 0000000..687be63
--- /dev/null
@@ -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
diff --git a/target/linux/realtek/files-5.10/drivers/leds/realtek/Makefile b/target/linux/realtek/files-5.10/drivers/leds/realtek/Makefile
new file mode 100644 (file)
index 0000000..125dc45
--- /dev/null
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_LEDS_RTL)                         += led-regfield.o
diff --git a/target/linux/realtek/files-5.10/drivers/leds/realtek/led-regfield.c b/target/linux/realtek/files-5.10/drivers/leds/realtek/led-regfield.c
new file mode 100644 (file)
index 0000000..dd669c9
--- /dev/null
@@ -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;
+}
diff --git a/target/linux/realtek/files-5.10/drivers/leds/realtek/led-regfield.h b/target/linux/realtek/files-5.10/drivers/leds/realtek/led-regfield.h
new file mode 100644 (file)
index 0000000..f0a7c02
--- /dev/null
@@ -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
diff --git a/target/linux/realtek/patches-5.10/202-leds-add-Realtek-LED-hardware-directory.patch b/target/linux/realtek/patches-5.10/202-leds-add-Realtek-LED-hardware-directory.patch
new file mode 100644 (file)
index 0000000..6eb2b1b
--- /dev/null
@@ -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: [PATCH] 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/
index fdfbc2461c1bbec589323a1ff52806b8e374dbca..574405e05f06896b9760b3a8a88b23afa39cecc8 100644 (file)
@@ -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
index 344e9f9882093f3a74ee8d798a9a548701f8d769..5bb76371b58e0e6c2e735bc045450578f2680eef 100644 (file)
@@ -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
index ab31186ae1341d9365ce7898fea18185863874a4..e153caadd368a05209dd313642cf130cb2b24697 100644 (file)
@@ -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
index 00f751ce855a0286992f0ba2468e603ff07ea963..19ae6c2dce89f7538401a0d52f8fe4405feec51a 100644 (file)
@@ -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