diff mbox series

[v3,03/13] realtek: add base register field LED support

Message ID 7467168728bb6733c30cac4e231fa4dbfbbd7f20.1667154408.git.sander@svanheule.net
State Under Review
Delegated to: Sander Vanheule
Headers show
Series realtek: pinctrl and LED drivers | expand

Commit Message

Sander Vanheule Oct. 30, 2022, 6:30 p.m. UTC
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
diff mbox series

Patch

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
index 000000000000..687be63f739c
--- /dev/null
+++ b/target/linux/realtek/files-5.10/drivers/leds/realtek/Kconfig
@@ -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
index 000000000000..125dc45ff2ce
--- /dev/null
+++ b/target/linux/realtek/files-5.10/drivers/leds/realtek/Makefile
@@ -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
index 000000000000..dd669c94c62e
--- /dev/null
+++ b/target/linux/realtek/files-5.10/drivers/leds/realtek/led-regfield.c
@@ -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
index 000000000000..f0a7c02514ad
--- /dev/null
+++ b/target/linux/realtek/files-5.10/drivers/leds/realtek/led-regfield.h
@@ -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
index 000000000000..4866a09f5783
--- /dev/null
+++ b/target/linux/realtek/patches-5.10/202-leds-add-Realtek-LED-hardware-directory.patch
@@ -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/
diff --git a/target/linux/realtek/rtl838x/config-5.10 b/target/linux/realtek/rtl838x/config-5.10
index fdfbc2461c1b..574405e05f06 100644
--- a/target/linux/realtek/rtl838x/config-5.10
+++ b/target/linux/realtek/rtl838x/config-5.10
@@ -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
diff --git a/target/linux/realtek/rtl839x/config-5.10 b/target/linux/realtek/rtl839x/config-5.10
index 344e9f988209..5bb76371b58e 100644
--- a/target/linux/realtek/rtl839x/config-5.10
+++ b/target/linux/realtek/rtl839x/config-5.10
@@ -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
diff --git a/target/linux/realtek/rtl930x/config-5.10 b/target/linux/realtek/rtl930x/config-5.10
index ab31186ae134..e153caadd368 100644
--- a/target/linux/realtek/rtl930x/config-5.10
+++ b/target/linux/realtek/rtl930x/config-5.10
@@ -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
diff --git a/target/linux/realtek/rtl931x/config-5.10 b/target/linux/realtek/rtl931x/config-5.10
index 00f751ce855a..19ae6c2dce89 100644
--- a/target/linux/realtek/rtl931x/config-5.10
+++ b/target/linux/realtek/rtl931x/config-5.10
@@ -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