diff --git a/boards/arm/96b_neonkey/96b_neonkey.dts b/boards/arm/96b_neonkey/96b_neonkey.dts index 38654ddc4e..607044f3ef 100644 --- a/boards/arm/96b_neonkey/96b_neonkey.dts +++ b/boards/arm/96b_neonkey/96b_neonkey.dts @@ -72,6 +72,12 @@ &i2c3 { clock-frequency = ; + + lp3943@60 { + compatible = "ti,lp3943"; + reg = <0x60>; + label = "LP3943"; + }; }; &spi1 { diff --git a/boards/arm/96b_neonkey/Kconfig.board b/boards/arm/96b_neonkey/Kconfig.board index 0b8fb789e2..e84f64f573 100644 --- a/boards/arm/96b_neonkey/Kconfig.board +++ b/boards/arm/96b_neonkey/Kconfig.board @@ -7,3 +7,4 @@ config BOARD_96B_NEONKEY bool "96Boards Neonkey" depends on SOC_STM32F411XE + select HAS_DTS_I2C_DEVICE diff --git a/boards/arm/96b_neonkey/dts.fixup b/boards/arm/96b_neonkey/dts.fixup new file mode 100644 index 0000000000..e936c94bcf --- /dev/null +++ b/boards/arm/96b_neonkey/dts.fixup @@ -0,0 +1,9 @@ +/* This file is a temporary workaround for mapping of the generated information + * to the current driver definitions. This will be removed when the drivers + * are modified to handle the generated information, or the mapping of + * generated data matches the driver definitions. + */ + +#define CONFIG_LP3943_DEV_NAME ST_STM32_I2C_V1_40005C00_TI_LP3943_60_LABEL +#define CONFIG_LP3943_I2C_ADDRESS ST_STM32_I2C_V1_40005C00_TI_LP3943_60_BASE_ADDRESS +#define CONFIG_LP3943_I2C_MASTER_DEV_NAME ST_STM32_I2C_V1_40005C00_TI_LP3943_60_BUS_NAME diff --git a/drivers/led/CMakeLists.txt b/drivers/led/CMakeLists.txt index 762559438d..621bce910d 100644 --- a/drivers/led/CMakeLists.txt +++ b/drivers/led/CMakeLists.txt @@ -1 +1 @@ -# Nothing here (yet) +zephyr_sources_ifdef(CONFIG_LP3943 lp3943.c) diff --git a/drivers/led/Kconfig b/drivers/led/Kconfig index 1cf29ad680..0bd35c4742 100644 --- a/drivers/led/Kconfig +++ b/drivers/led/Kconfig @@ -10,7 +10,7 @@ menuconfig LED bool "LED drivers" default n help - Include LED drivers in the system configuration. + Include LED drivers in the system configuration. if LED @@ -20,14 +20,20 @@ config SYS_LOG_LED_LEVEL default 0 range 0 4 help - Sets the log level for LED drivers. You must have - system logging enabled. - Levels are: - 0 OFF, do not write - 1 ERROR, only write SYS_LOG_ERR - 2 WARNING, write SYS_LOG_WRN in addition to previous level - 3 INFO, write SYS_LOG_INF in addition to previous levels - 4 DEBUG, write SYS_LOG_DBG in addition to previous levels + Sets the log level for LED drivers. You must have + system logging enabled. + + Levels are: + + - 0 OFF, do not write + + - 1 ERROR, only write SYS_LOG_ERR + + - 2 WARNING, write SYS_LOG_WRN in addition to previous level + + - 3 INFO, write SYS_LOG_INF in addition to previous levels + + - 4 DEBUG, write SYS_LOG_DBG in addition to previous levels config LED_INIT_PRIORITY int "LED initialization priority" @@ -35,4 +41,6 @@ config LED_INIT_PRIORITY help System initialization priority for LED drivers. +source "drivers/led/Kconfig.lp3943" + endif # LED diff --git a/drivers/led/Kconfig.lp3943 b/drivers/led/Kconfig.lp3943 new file mode 100644 index 0000000000..54585d0d31 --- /dev/null +++ b/drivers/led/Kconfig.lp3943 @@ -0,0 +1,48 @@ +# +# Copyright (c) 2018 Linaro Limited +# +# SPDX-License-Identifier: Apache-2.0 +# + +menuconfig LP3943 + bool "LP3943 LED driver" + depends on I2C + default n + help + Enable LED driver for LP3943. + + LP3943 LED driver has 16 channels each with multi-programmable + states at a specified rate. Each channel can drive upto 25 mA + per LED. + +if !HAS_DTS_I2C_DEVICE + +config LP3943_DEV_NAME + string "LP3943 device name" + default "LP3943" + help + Device name for LP3943 LED driver. + +config LP3943_I2C_ADDRESS + hex "LP3943 I2C slave address" + range 0x60 0x67 + default 0x60 + help + Specify the I2C slave address for the LP3943 LED driver. + 0x60: ADR0 = 0, ADR1 = 0, ADR2 = 0 + 0x61: ADR0 = 0, ADR1 = 0, ADR2 = 1 + 0x62: ADR0 = 0, ADR1 = 1, ADR2 = 0 + 0x63: ADR0 = 0, ADR1 = 1, ADR2 = 1 + 0x64: ADR0 = 1, ADR1 = 0, ADR2 = 0 + 0x65: ADR0 = 1, ADR1 = 0, ADR2 = 1 + 0x66: ADR0 = 1, ADR1 = 1, ADR2 = 0 + 0x67: ADR0 = 1, ADR1 = 1, ADR2 = 1 + +config LP3943_I2C_MASTER_DEV_NAME + string "I2C master where LP3943 is connected" + default "I2C_0" + help + Specify the device name of the I2C master device to which + LP3943 is connected. + +endif diff --git a/drivers/led/lp3943.c b/drivers/led/lp3943.c new file mode 100644 index 0000000000..8e833376b8 --- /dev/null +++ b/drivers/led/lp3943.c @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2018 Linaro Ltd. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @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 +#include +#include +#include + +#define SYS_LOG_LEVEL CONFIG_SYS_LOG_LED_LEVEL +#include + +#include "led_context.h" + +/* 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 + +enum lp3943_modes { + LP3943_OFF, + LP3943_ON, + LP3943_DIM0, + LP3943_DIM1, +}; + +struct lp3943_data { + struct device *i2c; + struct led_data dev_data; +}; + +static int lp3943_get_led_reg(u32_t *led, u8_t *reg) +{ + switch (*led) { + case 0 ... 3: + *reg = LP3943_LS0; + break; + case 4 ... 7: + *reg = LP3943_LS1; + *led -= 4; + break; + case 8 ... 11: + *reg = LP3943_LS2; + *led -= 8; + break; + case 12 ... 15: + *reg = LP3943_LS3; + *led -= 12; + break; + default: + SYS_LOG_ERR("Invalid LED specified"); + return -EINVAL; + } + + return 0; +} + +static int lp3943_set_dim_states(struct lp3943_data *data, u32_t led, u8_t mode) +{ + int ret; + u8_t reg; + + ret = lp3943_get_led_reg(&led, ®); + if (ret) { + return ret; + } + + /* Set DIMx states for the LEDs */ + if (i2c_reg_update_byte(data->i2c, CONFIG_LP3943_I2C_ADDRESS, reg, + LP3943_MASK << (led << 1), + mode << (led << 1))) { + SYS_LOG_ERR("LED reg update failed"); + return -EIO; + } + + return 0; +} + +static int lp3943_led_blink(struct device *dev, u32_t led, + u32_t delay_on, u32_t delay_off) +{ + struct lp3943_data *data = dev->driver_data; + struct led_data *dev_data = &data->dev_data; + int ret; + u16_t period; + u8_t reg, val, mode; + + period = delay_on + delay_off; + + if (period < dev_data->min_period || period > dev_data->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 * 255) / dev_data->max_period; + if (i2c_reg_write_byte(data->i2c, CONFIG_LP3943_I2C_ADDRESS, + reg, val)) { + SYS_LOG_ERR("LED write failed"); + return -EIO; + } + + ret = lp3943_set_dim_states(data, led, mode); + if (ret) { + return ret; + } + + return 0; +} + +static int lp3943_led_set_brightness(struct device *dev, u32_t led, + u8_t value) +{ + struct lp3943_data *data = dev->driver_data; + struct led_data *dev_data = &data->dev_data; + int ret; + u8_t reg, val, mode; + + if (value < dev_data->min_brightness || + value > dev_data->max_brightness) { + 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_PWM0; + } else { + reg = LP3943_PWM1; + } + + val = (value * 255) / dev_data->max_brightness; + if (i2c_reg_write_byte(data->i2c, CONFIG_LP3943_I2C_ADDRESS, + reg, val)) { + SYS_LOG_ERR("LED write failed"); + return -EIO; + } + + ret = lp3943_set_dim_states(data, led, mode); + if (ret) { + return ret; + } + + return 0; +} + +static inline int lp3943_led_on(struct device *dev, u32_t led) +{ + struct lp3943_data *data = dev->driver_data; + int ret; + u8_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(data->i2c, CONFIG_LP3943_I2C_ADDRESS, reg, + LP3943_MASK << (led << 1), + mode << (led << 1))) { + SYS_LOG_ERR("LED reg update failed"); + return -EIO; + } + + return 0; +} + +static inline int lp3943_led_off(struct device *dev, u32_t led) +{ + struct lp3943_data *data = dev->driver_data; + int ret; + u8_t reg; + + ret = lp3943_get_led_reg(&led, ®); + if (ret) { + return ret; + } + + /* Set LED state to OFF */ + if (i2c_reg_update_byte(data->i2c, CONFIG_LP3943_I2C_ADDRESS, reg, + LP3943_MASK << (led << 1), 0)) { + SYS_LOG_ERR("LED reg update failed"); + return -EIO; + } + + return 0; +} + +static int lp3943_led_init(struct device *dev) +{ + struct lp3943_data *data = dev->driver_data; + struct led_data *dev_data = &data->dev_data; + + data->i2c = device_get_binding(CONFIG_LP3943_I2C_MASTER_DEV_NAME); + if (data->i2c == NULL) { + SYS_LOG_DBG("Failed to get I2C device"); + return -EINVAL; + } + + /* Hardware specific limits */ + dev_data->min_period = 0; + dev_data->max_period = 1600; + dev_data->min_brightness = 0; + dev_data->max_brightness = 100; + + return 0; +} + +static struct lp3943_data lp3943_led_data; + +static const struct led_driver_api lp3943_led_api = { + .blink = lp3943_led_blink, + .set_brightness = lp3943_led_set_brightness, + .on = lp3943_led_on, + .off = lp3943_led_off, +}; + +DEVICE_AND_API_INIT(lp3943_led, CONFIG_LP3943_DEV_NAME, + &lp3943_led_init, &lp3943_led_data, + NULL, POST_KERNEL, CONFIG_LED_INIT_PRIORITY, + &lp3943_led_api); diff --git a/dts/bindings/led/ti,lp3943.yaml b/dts/bindings/led/ti,lp3943.yaml new file mode 100644 index 0000000000..57b8145cdb --- /dev/null +++ b/dts/bindings/led/ti,lp3943.yaml @@ -0,0 +1,17 @@ +--- +title: TI LP3943 LED Driver +id: ti,lp3943 +version: 0.1 + +description: TI LP3943 LED binding + +inherits: + !include i2c-device.yaml + +properties: + compatible: + type: string + category: required + description: compatible strings + constraint: "ti,lp3943" +...