From fd6fd106a897bb50421ed9575ef1ac81e4f86f2a Mon Sep 17 00:00:00 2001 From: GuEe-GUI <2991707448@qq.com> Date: Tue, 2 Dec 2025 14:09:55 +0800 Subject: [PATCH 1/4] [DM][LED] Fixup LED check status string match Signed-off-by: GuEe-GUI <2991707448@qq.com> --- components/drivers/led/led.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/drivers/led/led.c b/components/drivers/led/led.c index f041148cd3a..c6bd68a5cec 100644 --- a/components/drivers/led/led.c +++ b/components/drivers/led/led.c @@ -70,7 +70,7 @@ static rt_ssize_t _led_write(rt_device_t dev, rt_off_t pos, const void *buffer, for (int i = 0; i < RT_ARRAY_SIZE(_led_states); ++i) { - if (!rt_strncpy((char *)_led_states[i], buffer, size)) + if (!rt_strncmp((char *)_led_states[i], buffer, size)) { return rt_led_set_state(led, i) ? : size; } From 2000566b6ed5dbf661b70c64549675e806181fa0 Mon Sep 17 00:00:00 2001 From: GuEe-GUI <2991707448@qq.com> Date: Tue, 2 Dec 2025 14:10:58 +0800 Subject: [PATCH 2/4] [DM][LED] Update LED blink cycle to heartbeat Signed-off-by: GuEe-GUI <2991707448@qq.com> --- components/drivers/led/led.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/components/drivers/led/led.c b/components/drivers/led/led.c index c6bd68a5cec..7c0640aa907 100644 --- a/components/drivers/led/led.c +++ b/components/drivers/led/led.c @@ -21,6 +21,7 @@ struct blink_timer { rt_bool_t toggle; rt_bool_t enabled; + rt_uint32_t count; struct rt_timer timer; }; @@ -104,19 +105,42 @@ const static struct rt_device_ops _led_ops = static void _led_blink_timerout(void *param) { + rt_tick_t tick; struct rt_led_device *led = param; struct blink_timer *btimer = led->sysdata; if (btimer->toggle) { led->ops->set_state(led, RT_LED_S_OFF); + + if (btimer->count++ & 1) + { + btimer->count = 0; + tick = rt_tick_from_millisecond(1000); + goto _set_timeout; + } } else { led->ops->set_state(led, RT_LED_S_ON); + + if (!btimer->count) + { + tick = rt_tick_from_millisecond(80); + goto _set_timeout; + } } +_toggle: btimer->toggle = !btimer->toggle; + return; + +_set_timeout: + rt_timer_stop(&btimer->timer); + rt_timer_control(&btimer->timer, RT_TIMER_CTRL_SET_TIME, &tick); + rt_timer_start(&btimer->timer); + + goto _toggle; } rt_err_t rt_led_register(struct rt_led_device *led) @@ -158,8 +182,9 @@ rt_err_t rt_led_register(struct rt_led_device *led) btimer->toggle = RT_FALSE; btimer->enabled = RT_FALSE; + btimer->count = 0; rt_timer_init(&btimer->timer, dev_name, _led_blink_timerout, led, - rt_tick_from_millisecond(500), RT_TIMER_FLAG_PERIODIC); + rt_tick_from_millisecond(1000), RT_TIMER_FLAG_PERIODIC); } led->parent.type = RT_Device_Class_Char; From f3647fccec0f9fb6f30247aa4cb1504095f52231 Mon Sep 17 00:00:00 2001 From: GuEe-GUI <2991707448@qq.com> Date: Tue, 2 Dec 2025 14:12:30 +0800 Subject: [PATCH 3/4] [DM][LED] Fixup gpio-led memory alloc to zero Signed-off-by: GuEe-GUI <2991707448@qq.com> --- components/drivers/led/led-gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/drivers/led/led-gpio.c b/components/drivers/led/led-gpio.c index fb60f32fbee..e1d0cfe65f4 100644 --- a/components/drivers/led/led-gpio.c +++ b/components/drivers/led/led-gpio.c @@ -90,7 +90,7 @@ static rt_err_t ofw_append_gpio_led(struct rt_ofw_node *np) rt_err_t err; enum rt_led_state led_state = RT_LED_S_OFF; const char *propname, *state, *trigger; - struct gpio_led *gled = rt_malloc(sizeof(*gled)); + struct gpio_led *gled = rt_calloc(1, sizeof(*gled)); if (!gled) { From 5f8dc42ec45bf8295883c68e7dfb3c67c51ec707 Mon Sep 17 00:00:00 2001 From: GuEe-GUI <2991707448@qq.com> Date: Tue, 2 Dec 2025 14:13:31 +0800 Subject: [PATCH 4/4] [DM][LED] Update LED common drivers 1. PWM-LED 2. Syscon-LED Signed-off-by: GuEe-GUI <2991707448@qq.com> --- components/drivers/led/Kconfig | 13 ++ components/drivers/led/SConscript | 6 + components/drivers/led/led-pwm.c | 303 ++++++++++++++++++++++++++++ components/drivers/led/led-syscon.c | 186 +++++++++++++++++ 4 files changed, 508 insertions(+) create mode 100644 components/drivers/led/led-pwm.c create mode 100644 components/drivers/led/led-syscon.c diff --git a/components/drivers/led/Kconfig b/components/drivers/led/Kconfig index 462aa0bac04..c1f5145f3b4 100644 --- a/components/drivers/led/Kconfig +++ b/components/drivers/led/Kconfig @@ -10,6 +10,19 @@ config RT_LED_GPIO depends on RT_USING_OFW default n +config RT_LED_PWM + bool "PWM driven LEDs Support" + depends on RT_USING_LED + depends on RT_USING_PWM + depends on RT_USING_OFW + default n + +config RT_LED_SYSCON + bool "System controllers connected LEDs Support" + depends on RT_USING_LED + depends on RT_MFD_SYSCON + default n + if RT_USING_LED osource "$(SOC_DM_LED_DIR)/Kconfig" endif diff --git a/components/drivers/led/SConscript b/components/drivers/led/SConscript index 276ca0bd8b8..51459e0df6a 100644 --- a/components/drivers/led/SConscript +++ b/components/drivers/led/SConscript @@ -13,6 +13,12 @@ src = ['led.c'] if GetDepend(['RT_LED_GPIO']): src += ['led-gpio.c'] +if GetDepend(['RT_LED_PWM']): + src += ['led-pwm.c'] + +if GetDepend(['RT_LED_SYSCON']): + src += ['led-syscon.c'] + group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) Return('group') diff --git a/components/drivers/led/led-pwm.c b/components/drivers/led/led-pwm.c new file mode 100644 index 00000000000..ac56e452fcb --- /dev/null +++ b/components/drivers/led/led-pwm.c @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include +#include + +#define DBG_TAG "led.pwm" +#define DBG_LVL DBG_INFO +#include + +struct pwm_led +{ + struct rt_led_device parent; + + struct rt_device_pwm *pwm_dev; + struct rt_pwm_configuration pwm_conf; + + rt_uint32_t max_brightness; + rt_uint32_t brightness; + rt_bool_t active_low; + rt_bool_t enabled; +}; + +#define raw_to_pwm_led(raw) rt_container_of(raw, struct pwm_led, parent) + +static rt_err_t pwm_led_set_state(struct rt_led_device *led, enum rt_led_state state) +{ + rt_err_t err = RT_EOK; + struct rt_pwm_configuration pwm_conf = {}; + struct pwm_led *pled = raw_to_pwm_led(led); + + switch (state) + { + case RT_LED_S_OFF: + if ((err = rt_pwm_set(pled->pwm_dev, + pled->pwm_conf.channel, pled->pwm_conf.period, 0))) + { + break; + } + + if (!(err = rt_pwm_disable(pled->pwm_dev, pled->pwm_conf.channel))) + { + pled->enabled = RT_FALSE; + } + break; + + case RT_LED_S_ON: + pwm_conf.channel = pled->pwm_conf.channel; + pwm_conf.period = pled->pwm_conf.period; + pwm_conf.pulse = pled->pwm_conf.pulse; + pwm_conf.complementary = pled->active_low; + + if ((err = rt_device_control(&pled->pwm_dev->parent, PWM_CMD_SET, &pwm_conf))) + { + break; + } + + if (!(err = rt_pwm_enable(pled->pwm_dev, pled->pwm_conf.channel))) + { + pled->enabled = RT_TRUE; + } + break; + + case RT_LED_S_TOGGLE: + err = led->ops->get_state(led, &state); + + if (!err) + { + err = led->ops->set_state(led, state == RT_LED_S_OFF ? RT_LED_S_ON : RT_LED_S_OFF); + } + break; + + default: + return -RT_ENOSYS; + } + + return err; +} + +static rt_err_t pwm_led_get_state(struct rt_led_device *led, enum rt_led_state *out_state) +{ + struct pwm_led *pled = raw_to_pwm_led(led); + + *out_state = pled->enabled ? RT_LED_S_ON : RT_LED_S_OFF; + + return RT_EOK; +} + +static rt_err_t pwm_led_set_brightness(struct rt_led_device *led, rt_uint32_t brightness) +{ + struct pwm_led *pled = raw_to_pwm_led(led); + rt_uint64_t duty = pled->pwm_conf.period; + + duty *= brightness; + rt_do_div(duty, pled->max_brightness); + + if (pled->active_low) + { + duty = pled->pwm_conf.period - duty; + } + + pled->pwm_conf.pulse = duty; + + return rt_pwm_set(pled->pwm_dev, + pled->pwm_conf.channel, pled->pwm_conf.period, pled->pwm_conf.pulse); +} + +const static struct rt_led_ops pwm_led_ops = +{ + .set_state = pwm_led_set_state, + .get_state = pwm_led_get_state, + .set_brightness = pwm_led_set_brightness, +}; + +static rt_err_t ofw_append_pwm_led(struct rt_ofw_node *np) +{ + rt_err_t err; + rt_bool_t need_set_brightness = RT_TRUE; + enum rt_led_state led_state = RT_LED_S_OFF; + const char *propname, *state, *trigger; + struct rt_ofw_node *pwm_np; + struct rt_ofw_cell_args pwm_args; + struct pwm_led *pled = rt_calloc(1, sizeof(*pled)); + + if (!pled) + { + return -RT_ENOMEM; + } + + pled->active_low = rt_ofw_prop_read_bool(np, "active-low"); + + if (rt_ofw_prop_read_u32(np, "max-brightness", &pled->max_brightness)) + { + err = -RT_EINVAL; + goto _fail; + } + + if (rt_ofw_parse_phandle_cells(np, "pwms", "#pwm-cells", 0, &pwm_args)) + { + err = -RT_EINVAL; + goto _fail; + } + + pwm_np = pwm_args.data; + + if (!rt_ofw_data(pwm_np)) + { + rt_platform_ofw_request(pwm_np); + } + + pled->pwm_dev = rt_ofw_data(pwm_np); + rt_ofw_node_put(pwm_np); + + if (!pled->pwm_dev) + { + err = -RT_EINVAL; + goto _fail; + } + + pled->pwm_conf.channel = pwm_args.args[0]; + pled->pwm_conf.period = pwm_args.args[1]; + + pled->parent.ops = &pwm_led_ops; + + if ((err = rt_led_register(&pled->parent))) + { + goto _fail; + } + + if (!rt_ofw_prop_read_string(np, "default-state", &state)) + { + rt_pwm_get(pled->pwm_dev, &pled->pwm_conf); + + if (!rt_strcmp(state, "on")) + { + led_state = RT_LED_S_ON; + pled->brightness = pled->max_brightness; + } + else if (!rt_strcmp(state, "keep")) + { + if (pled->pwm_conf.period) + { + rt_uint64_t brightness; + + brightness = pled->max_brightness; + brightness *= pled->pwm_conf.pulse; + rt_do_div(brightness, pled->pwm_conf.period); + + pled->brightness = brightness; + + need_set_brightness = RT_FALSE; + } + else + { + goto _out_state_check; + } + } + + pled->pwm_conf.period = pwm_args.args[1]; + } + +_out_state_check: + if ((propname = rt_ofw_get_prop_fuzzy_name(np, "default-trigger$"))) + { + if (!rt_ofw_prop_read_string(np, propname, &trigger)) + { + if (!rt_strcmp(trigger, "heartbeat") || + !rt_strcmp(trigger, "timer")) + { + led_state = RT_LED_S_BLINK; + } + } + } + + rt_led_set_state(&pled->parent, led_state); + + if (need_set_brightness) + { + pwm_led_set_brightness(&pled->parent, pled->brightness); + } + + rt_ofw_data(np) = &pled->parent; + + return RT_EOK; + +_fail: + rt_free(pled); + + return err; +} + +static rt_err_t pwm_led_probe(struct rt_platform_device *pdev) +{ + struct rt_ofw_node *led_np, *np = pdev->parent.ofw_node; + + rt_ofw_foreach_available_child_node(np, led_np) + { + rt_err_t err = ofw_append_pwm_led(led_np); + + if (err == -RT_ENOMEM) + { + rt_ofw_node_put(led_np); + + return err; + } + else if (err) + { + LOG_E("%s: create LED fail", rt_ofw_node_full_name(led_np)); + } + } + + return RT_EOK; +} + +static rt_err_t pwm_led_remove(struct rt_platform_device *pdev) +{ + struct pwm_led *pled; + struct rt_led_device *led_dev; + struct rt_ofw_node *led_np, *np = pdev->parent.ofw_node; + + rt_ofw_foreach_available_child_node(np, led_np) + { + led_dev = rt_ofw_data(led_np); + + if (!led_dev) + { + continue; + } + + pled = rt_container_of(led_dev, struct pwm_led, parent); + + rt_ofw_data(led_np) = RT_NULL; + + rt_led_unregister(&pled->parent); + + rt_free(pled); + } + + return RT_EOK; +} + +static const struct rt_ofw_node_id pwm_led_ofw_ids[] = +{ + { .compatible = "pwm-leds" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver pwm_led_driver = +{ + .name = "led-pwm", + .ids = pwm_led_ofw_ids, + + .probe = pwm_led_probe, + .remove = pwm_led_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(pwm_led_driver); diff --git a/components/drivers/led/led-syscon.c b/components/drivers/led/led-syscon.c new file mode 100644 index 00000000000..97464929dc0 --- /dev/null +++ b/components/drivers/led/led-syscon.c @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-3-08 GuEe-GUI the first version + */ + +#include +#include + +#define DBG_TAG "led.syscon" +#define DBG_LVL DBG_INFO +#include + +struct syscon_led +{ + struct rt_led_device parent; + + struct rt_syscon *map; + rt_uint32_t offset; + rt_uint32_t mask; +}; + +#define raw_to_syscon_led(raw) rt_container_of(raw, struct syscon_led, parent) + +static rt_err_t syscon_led_set_state(struct rt_led_device *led, enum rt_led_state state) +{ + rt_err_t err = RT_EOK; + struct syscon_led *sled = raw_to_syscon_led(led); + + switch (state) + { + case RT_LED_S_OFF: + err = rt_syscon_update_bits(sled->map, sled->offset, sled->mask, 0); + break; + + case RT_LED_S_ON: + err = rt_syscon_update_bits(sled->map, sled->offset, sled->mask, sled->mask); + break; + + case RT_LED_S_TOGGLE: + err = led->ops->get_state(led, &state); + + if (!err) + { + err = led->ops->set_state(led, state == RT_LED_S_OFF ? RT_LED_S_ON : RT_LED_S_OFF); + } + break; + + default: + return -RT_ENOSYS; + } + + return err; +} + +static rt_err_t syscon_led_get_state(struct rt_led_device *led, enum rt_led_state *out_state) +{ + rt_err_t err; + rt_uint32_t val; + struct syscon_led *sled = raw_to_syscon_led(led); + + if (!(err = rt_syscon_read(sled->map, sled->offset, &val))) + { + if ((val & ~sled->mask)) + { + *out_state = RT_LED_S_ON; + } + else + { + *out_state = RT_LED_S_OFF; + } + } + + return err; +} + +const static struct rt_led_ops syscon_led_ops = +{ + .set_state = syscon_led_set_state, + .get_state = syscon_led_get_state, +}; + +static rt_err_t syscon_led_probe(struct rt_platform_device *pdev) +{ + rt_err_t err; + const char *propname, *state, *trigger; + enum rt_led_state led_state = RT_LED_S_OFF; + struct rt_ofw_node *np = pdev->parent.ofw_node, *pnp; + struct syscon_led *sled = rt_calloc(1, sizeof(*sled)); + + if (!sled) + { + return -RT_ENOMEM; + } + + if (!(pnp = rt_ofw_get_parent(np))) + { + err = -RT_EINVAL; + goto _fail; + } + + if ((err = rt_ofw_prop_read_u32(np, "offset", &sled->offset)) || + (err = rt_ofw_prop_read_u32(np, "mask", &sled->mask))) + { + rt_ofw_node_put(pnp); + goto _fail; + } + + sled->map = rt_syscon_find_by_ofw_node(pnp); + rt_ofw_node_put(pnp); + + if (!sled->map) + { + err = -RT_EINVAL; + goto _fail; + } + + pdev->parent.user_data = sled; + + sled->parent.ops = &syscon_led_ops; + + if ((err = rt_led_register(&sled->parent))) + { + goto _fail; + } + + if (!rt_ofw_prop_read_string(np, "default-state", &state)) + { + if (!rt_strcmp(state, "on")) + { + led_state = RT_LED_S_ON; + } + } + + if ((propname = rt_ofw_get_prop_fuzzy_name(np, "default-trigger$"))) + { + if (!rt_ofw_prop_read_string(np, propname, &trigger)) + { + if (!rt_strcmp(trigger, "heartbeat") || + !rt_strcmp(trigger, "timer")) + { + led_state = RT_LED_S_BLINK; + } + } + } + + rt_led_set_state(&sled->parent, led_state); + + return RT_EOK; + +_fail: + rt_free(sled); + + return err; +} + +static rt_err_t syscon_led_remove(struct rt_platform_device *pdev) +{ + struct syscon_led *sled = pdev->parent.user_data; + + rt_led_unregister(&sled->parent); + + rt_free(sled); + + return RT_EOK; +} + +static const struct rt_ofw_node_id syscon_led_ofw_ids[] = +{ + { .compatible = "register-bit-led" }, + { /* sentinel */ } +}; + +static struct rt_platform_driver syscon_led_driver = +{ + .name = "leds-syscon", + .ids = syscon_led_ofw_ids, + + .probe = syscon_led_probe, + .remove = syscon_led_remove, +}; +RT_PLATFORM_DRIVER_EXPORT(syscon_led_driver);