You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
263 lines
5.0 KiB
263 lines
5.0 KiB
/* |
|
* Copyright (c) 2018 Linaro Ltd. |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#define DT_DRV_COMPAT ti_lp3943 |
|
|
|
/** |
|
* @file |
|
* @brief LP3943 LED driver |
|
* |
|
* Limitations: |
|
* - Blink period and brightness value are controlled by two sets of PSCx/PWMx |
|
* registers. This driver partitions the available LEDs into two groups as |
|
* 0 to 7 and 8 to 15 and assigns PSC0/PWM0 to LEDs from 0 to 7 and PSC1/PWM1 |
|
* to LEDs from 8 to 15. So, it is not possible to set unique blink period |
|
* and brightness value for LEDs in a group, changing either of these |
|
* values for a LED will affect other LEDs also. |
|
*/ |
|
|
|
#include <zephyr/drivers/i2c.h> |
|
#include <zephyr/drivers/led.h> |
|
#include <zephyr/sys/util.h> |
|
#include <zephyr/kernel.h> |
|
|
|
#define LOG_LEVEL CONFIG_LED_LOG_LEVEL |
|
#include <zephyr/logging/log.h> |
|
LOG_MODULE_REGISTER(lp3943); |
|
|
|
/* LP3943 Registers */ |
|
#define LP3943_INPUT_1 0x00 |
|
#define LP3943_INPUT_2 0x01 |
|
#define LP3943_PSC0 0x02 |
|
#define LP3943_PWM0 0x03 |
|
#define LP3943_PSC1 0x04 |
|
#define LP3943_PWM1 0x05 |
|
#define LP3943_LS0 0x06 |
|
#define LP3943_LS1 0x07 |
|
#define LP3943_LS2 0x08 |
|
#define LP3943_LS3 0x09 |
|
|
|
#define LP3943_MASK 0x03 |
|
|
|
#define LP3943_MAX_PERIOD 1600U |
|
|
|
enum lp3943_modes { |
|
LP3943_OFF, |
|
LP3943_ON, |
|
LP3943_DIM0, |
|
LP3943_DIM1, |
|
}; |
|
|
|
struct lp3943_config { |
|
struct i2c_dt_spec bus; |
|
}; |
|
|
|
static int lp3943_get_led_reg(uint32_t *led, uint8_t *reg) |
|
{ |
|
switch (*led) { |
|
case 0: |
|
case 1: |
|
case 2: |
|
__fallthrough; |
|
case 3: |
|
*reg = LP3943_LS0; |
|
break; |
|
case 4: |
|
case 5: |
|
case 6: |
|
__fallthrough; |
|
case 7: |
|
*reg = LP3943_LS1; |
|
*led -= 4U; |
|
break; |
|
case 8: |
|
case 9: |
|
case 10: |
|
__fallthrough; |
|
case 11: |
|
*reg = LP3943_LS2; |
|
*led -= 8U; |
|
break; |
|
case 12: |
|
case 13: |
|
case 14: |
|
__fallthrough; |
|
case 15: |
|
*reg = LP3943_LS3; |
|
*led -= 12U; |
|
break; |
|
default: |
|
LOG_ERR("Invalid LED specified"); |
|
return -EINVAL; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int lp3943_set_dim_states(const struct lp3943_config *config, |
|
uint32_t led, uint8_t mode) |
|
{ |
|
int ret; |
|
uint8_t reg; |
|
|
|
ret = lp3943_get_led_reg(&led, ®); |
|
if (ret) { |
|
return ret; |
|
} |
|
|
|
/* Set DIMx states for the LEDs */ |
|
if (i2c_reg_update_byte_dt(&config->bus, reg, LP3943_MASK << (led << 1), |
|
mode << (led << 1))) { |
|
LOG_ERR("LED reg update failed"); |
|
return -EIO; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int lp3943_led_blink(const struct device *dev, uint32_t led, |
|
uint32_t delay_on, uint32_t delay_off) |
|
{ |
|
const struct lp3943_config *config = dev->config; |
|
int ret; |
|
uint16_t period; |
|
uint8_t reg, val, mode; |
|
|
|
period = delay_on + delay_off; |
|
if (period > LP3943_MAX_PERIOD) { |
|
return -EINVAL; |
|
} |
|
|
|
/* Use DIM0 for LEDs 0 to 7 and DIM1 for LEDs 8 to 15 */ |
|
if (led < 8) { |
|
mode = LP3943_DIM0; |
|
} else { |
|
mode = LP3943_DIM1; |
|
} |
|
|
|
if (mode == LP3943_DIM0) { |
|
reg = LP3943_PSC0; |
|
} else { |
|
reg = LP3943_PSC1; |
|
} |
|
|
|
val = period * 255U / LP3943_MAX_PERIOD; |
|
if (i2c_reg_write_byte_dt(&config->bus, reg, val)) { |
|
LOG_ERR("LED write failed"); |
|
return -EIO; |
|
} |
|
|
|
ret = lp3943_set_dim_states(config, led, mode); |
|
if (ret) { |
|
return ret; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int lp3943_led_set_brightness(const struct device *dev, uint32_t led, |
|
uint8_t value) |
|
{ |
|
const struct lp3943_config *config = dev->config; |
|
int ret; |
|
uint8_t reg, val, mode; |
|
|
|
/* Use DIM0 for LEDs 0 to 7 and DIM1 for LEDs 8 to 15 */ |
|
if (led < 8) { |
|
mode = LP3943_DIM0; |
|
} else { |
|
mode = LP3943_DIM1; |
|
} |
|
|
|
if (mode == LP3943_DIM0) { |
|
reg = LP3943_PWM0; |
|
} else { |
|
reg = LP3943_PWM1; |
|
} |
|
|
|
val = (value * 255U) / LED_BRIGHTNESS_MAX; |
|
if (i2c_reg_write_byte_dt(&config->bus, reg, val)) { |
|
LOG_ERR("LED write failed"); |
|
return -EIO; |
|
} |
|
|
|
ret = lp3943_set_dim_states(config, led, mode); |
|
if (ret) { |
|
return ret; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static inline int lp3943_led_on(const struct device *dev, uint32_t led) |
|
{ |
|
const struct lp3943_config *config = dev->config; |
|
int ret; |
|
uint8_t reg, mode; |
|
|
|
ret = lp3943_get_led_reg(&led, ®); |
|
if (ret) { |
|
return ret; |
|
} |
|
|
|
/* Set LED state to ON */ |
|
mode = LP3943_ON; |
|
if (i2c_reg_update_byte_dt(&config->bus, reg, LP3943_MASK << (led << 1), |
|
mode << (led << 1))) { |
|
LOG_ERR("LED reg update failed"); |
|
return -EIO; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static inline int lp3943_led_off(const struct device *dev, uint32_t led) |
|
{ |
|
const struct lp3943_config *config = dev->config; |
|
int ret; |
|
uint8_t reg; |
|
|
|
ret = lp3943_get_led_reg(&led, ®); |
|
if (ret) { |
|
return ret; |
|
} |
|
|
|
/* Set LED state to OFF */ |
|
if (i2c_reg_update_byte_dt(&config->bus, reg, LP3943_MASK << (led << 1), |
|
0)) { |
|
LOG_ERR("LED reg update failed"); |
|
return -EIO; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int lp3943_led_init(const struct device *dev) |
|
{ |
|
const struct lp3943_config *config = dev->config; |
|
|
|
if (!device_is_ready(config->bus.bus)) { |
|
LOG_ERR("I2C device not ready"); |
|
return -ENODEV; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static const struct lp3943_config lp3943_led_config = { |
|
.bus = I2C_DT_SPEC_INST_GET(0), |
|
}; |
|
|
|
static DEVICE_API(led, lp3943_led_api) = { |
|
.blink = lp3943_led_blink, |
|
.set_brightness = lp3943_led_set_brightness, |
|
.on = lp3943_led_on, |
|
.off = lp3943_led_off, |
|
}; |
|
|
|
DEVICE_DT_INST_DEFINE(0, &lp3943_led_init, NULL, NULL, |
|
&lp3943_led_config, POST_KERNEL, CONFIG_LED_INIT_PRIORITY, |
|
&lp3943_led_api);
|
|
|