drivers: led: add driver for PWM LEDs
This driver supports the PWM driven LEDs. The devices are created from the DT nodes with a compatible property matching "pwm-leds". For each child node a LED is created and its "pwms" phandle's node is used to retrieve the PWM configuration: channel, period and flags. If some of this properties are missing (it is the case for some PWM controllers), then reasonable default values are used. This driver implements the following LED API methods: - led_on - led_off - led_blink - led_set_brightness Signed-off-by: Simon Guinot <simon.guinot@seagate.com>
This commit is contained in:
parent
74b0b41167
commit
525a588db4
4 changed files with 174 additions and 0 deletions
|
@ -1,6 +1,7 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_sources_ifdef(CONFIG_HT16K33 ht16k33.c)
|
||||
zephyr_sources_ifdef(CONFIG_LED_PWM led_pwm.c)
|
||||
zephyr_sources_ifdef(CONFIG_LP3943 lp3943.c)
|
||||
zephyr_sources_ifdef(CONFIG_LP503X lp503x.c)
|
||||
zephyr_sources_ifdef(CONFIG_LP5562 lp5562.c)
|
||||
|
|
|
@ -31,5 +31,6 @@ source "drivers/led/Kconfig.lp3943"
|
|||
source "drivers/led/Kconfig.lp503x"
|
||||
source "drivers/led/Kconfig.lp5562"
|
||||
source "drivers/led/Kconfig.pca9633"
|
||||
source "drivers/led/Kconfig.pwm"
|
||||
|
||||
endif # LED
|
||||
|
|
8
drivers/led/Kconfig.pwm
Normal file
8
drivers/led/Kconfig.pwm
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Copyright (c) 2020 Seagate Technology LLC
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config LED_PWM
|
||||
bool "PWM LED driver"
|
||||
depends on PWM
|
||||
help
|
||||
Enable driver for PWM LEDs.
|
164
drivers/led/led_pwm.c
Normal file
164
drivers/led/led_pwm.c
Normal file
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Seagate Technology LLC
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT pwm_leds
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief PWM driven LEDs
|
||||
*/
|
||||
|
||||
#include <drivers/led.h>
|
||||
#include <drivers/pwm.h>
|
||||
#include <device.h>
|
||||
#include <zephyr.h>
|
||||
#include <sys/math_extras.h>
|
||||
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_REGISTER(led_pwm, CONFIG_LED_LOG_LEVEL);
|
||||
|
||||
#define DEV_CFG(dev) ((const struct led_pwm_config *) ((dev)->config))
|
||||
#define DEV_DATA(dev) ((struct led_pwm_data *) ((dev)->data))
|
||||
|
||||
struct led_pwm {
|
||||
char *pwm_label;
|
||||
uint32_t channel;
|
||||
uint32_t period;
|
||||
pwm_flags_t flags;
|
||||
};
|
||||
|
||||
struct led_pwm_config {
|
||||
int num_leds;
|
||||
const struct led_pwm *led;
|
||||
};
|
||||
|
||||
struct led_pwm_data {
|
||||
const struct device *pwm;
|
||||
};
|
||||
|
||||
static int led_pwm_blink(const struct device *dev, uint32_t led,
|
||||
uint32_t delay_on, uint32_t delay_off)
|
||||
{
|
||||
const struct led_pwm_config *config = DEV_CFG(dev);
|
||||
struct led_pwm_data *data = DEV_DATA(dev);
|
||||
const struct led_pwm *led_pwm;
|
||||
uint32_t period_usec, pulse_usec;
|
||||
|
||||
if (led >= config->num_leds) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert delays (in ms) into PWM period and pulse (in us) and check
|
||||
* for overflows.
|
||||
*/
|
||||
if (u32_add_overflow(delay_on, delay_off, &period_usec) ||
|
||||
u32_mul_overflow(period_usec, 1000, &period_usec) ||
|
||||
u32_mul_overflow(delay_on, 1000, &pulse_usec)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
led_pwm = &config->led[led];
|
||||
|
||||
return pwm_pin_set_usec(data[led].pwm, led_pwm->channel,
|
||||
period_usec, pulse_usec, led_pwm->flags);
|
||||
}
|
||||
|
||||
static int led_pwm_set_brightness(const struct device *dev,
|
||||
uint32_t led, uint8_t value)
|
||||
{
|
||||
const struct led_pwm_config *config = DEV_CFG(dev);
|
||||
struct led_pwm_data *data = DEV_DATA(dev);
|
||||
const struct led_pwm *led_pwm;
|
||||
uint32_t pulse;
|
||||
|
||||
if (led >= config->num_leds || value > 100) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
led_pwm = &config->led[led];
|
||||
|
||||
pulse = led_pwm->period * value / 100;
|
||||
|
||||
return pwm_pin_set_cycles(data[led].pwm, led_pwm->channel,
|
||||
led_pwm->period, pulse, led_pwm->flags);
|
||||
}
|
||||
|
||||
static int led_pwm_on(const struct device *dev, uint32_t led)
|
||||
{
|
||||
return led_pwm_set_brightness(dev, led, 100);
|
||||
}
|
||||
|
||||
static int led_pwm_off(const struct device *dev, uint32_t led)
|
||||
{
|
||||
return led_pwm_set_brightness(dev, led, 0);
|
||||
}
|
||||
|
||||
static int led_pwm_init(const struct device *dev)
|
||||
{
|
||||
const struct led_pwm_config *config = DEV_CFG(dev);
|
||||
struct led_pwm_data *data = DEV_DATA(dev);
|
||||
int i;
|
||||
|
||||
if (!config->num_leds) {
|
||||
LOG_ERR("%s: no LEDs found (DT child nodes missing)",
|
||||
dev->name);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
for (i = 0; i < config->num_leds; i++) {
|
||||
const struct led_pwm *led = &config->led[i];
|
||||
|
||||
data[i].pwm = device_get_binding(led->pwm_label);
|
||||
if (data->pwm == NULL) {
|
||||
LOG_ERR("%s: device %s not found",
|
||||
dev->name, led->pwm_label);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct led_driver_api led_pwm_api = {
|
||||
.on = led_pwm_on,
|
||||
.off = led_pwm_off,
|
||||
.blink = led_pwm_blink,
|
||||
.set_brightness = led_pwm_set_brightness,
|
||||
};
|
||||
|
||||
#define LED_PWM(led_node_id) \
|
||||
{ \
|
||||
.pwm_label = DT_PWMS_LABEL(led_node_id), \
|
||||
.channel = DT_PWMS_CHANNEL(led_node_id), \
|
||||
.period = DT_PHA_OR(led_node_id, pwms, period, 100), \
|
||||
.flags = DT_PHA_OR(led_node_id, pwms, flags, \
|
||||
PWM_POLARITY_NORMAL), \
|
||||
},
|
||||
|
||||
#define LED_PWM_DEVICE(id) \
|
||||
\
|
||||
const struct led_pwm led_pwm_##id[] = { \
|
||||
DT_INST_FOREACH_CHILD(id, LED_PWM) \
|
||||
}; \
|
||||
\
|
||||
const struct led_pwm_config led_pwm_config_##id = { \
|
||||
.num_leds = ARRAY_SIZE(led_pwm_##id), \
|
||||
.led = led_pwm_##id, \
|
||||
}; \
|
||||
\
|
||||
static struct led_pwm_data \
|
||||
led_pwm_data_##id[ARRAY_SIZE(led_pwm_##id)]; \
|
||||
\
|
||||
DEVICE_AND_API_INIT(led_pwm_##id, \
|
||||
DT_INST_PROP_OR(id, label, "LED_PWM_"#id), \
|
||||
&led_pwm_init, \
|
||||
&led_pwm_data_##id, \
|
||||
&led_pwm_config_##id, \
|
||||
POST_KERNEL, CONFIG_LED_INIT_PRIORITY, \
|
||||
&led_pwm_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(LED_PWM_DEVICE)
|
Loading…
Add table
Add a link
Reference in a new issue