From 038df5762a21cf6e64ceb87491e32c62d86f2e56 Mon Sep 17 00:00:00 2001 From: Sander Vanheule Date: Wed, 28 Sep 2022 21:08:18 +0200 Subject: [PATCH] realtek: add base register field LED support 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 --- .../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 --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 0000000000..687be63f73 --- /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 0000000000..125dc45ff2 --- /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 0000000000..dd669c94c6 --- /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 +#include + +#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 0000000000..f0a7c02514 --- /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 +#include +#include +#include +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 16, 0) +#include +#else +#include +#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 0000000000..6eb2b1b507 --- /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 +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 +--- + +--- 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 fdfbc2461c..574405e05f 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 344e9f9882..5bb76371b5 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 ab31186ae1..e153caadd3 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 00f751ce85..19ae6c2dce 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 -- 2.30.2