From a095bd7328c2793ceef178de5802d430c9d59653 Mon Sep 17 00:00:00 2001 From: Peter van der Perk Date: Fri, 21 Jul 2023 11:32:44 -0400 Subject: [PATCH] drivers: led: Add Onsemi ncp5623c driver The controller and the driver support two hardware configurations: - one three-channel (RGB) LED - or three single-channel LEDs Signed-off-by: Peter van der Perk --- drivers/led/CMakeLists.txt | 1 + drivers/led/Kconfig | 1 + drivers/led/Kconfig.ncp5623 | 14 ++ drivers/led/ncp5623.c | 219 ++++++++++++++++++++++++ dts/bindings/led/onnn,ncp5623.yaml | 68 ++++++++ tests/drivers/build_all/led/app.overlay | 5 + 6 files changed, 308 insertions(+) create mode 100644 drivers/led/Kconfig.ncp5623 create mode 100644 drivers/led/ncp5623.c create mode 100644 dts/bindings/led/onnn,ncp5623.yaml diff --git a/drivers/led/CMakeLists.txt b/drivers/led/CMakeLists.txt index 6bd4e672bf9..c3d90d0ba06 100644 --- a/drivers/led/CMakeLists.txt +++ b/drivers/led/CMakeLists.txt @@ -14,6 +14,7 @@ zephyr_library_sources_ifdef(CONFIG_LP3943 lp3943.c) zephyr_library_sources_ifdef(CONFIG_LP50XX lp50xx.c) zephyr_library_sources_ifdef(CONFIG_LP5562 lp5562.c) zephyr_library_sources_ifdef(CONFIG_LP5569 lp5569.c) +zephyr_library_sources_ifdef(CONFIG_NCP5623 ncp5623.c) zephyr_library_sources_ifdef(CONFIG_PCA9633 pca9633.c) zephyr_library_sources_ifdef(CONFIG_TLC59108 tlc59108.c) zephyr_library_sources_ifdef(CONFIG_IS31FL3733 is31fl3733.c) diff --git a/drivers/led/Kconfig b/drivers/led/Kconfig index e1290174d89..7808a1013ae 100644 --- a/drivers/led/Kconfig +++ b/drivers/led/Kconfig @@ -33,6 +33,7 @@ source "drivers/led/Kconfig.lp3943" source "drivers/led/Kconfig.lp50xx" source "drivers/led/Kconfig.lp5562" source "drivers/led/Kconfig.lp5569" +source "drivers/led/Kconfig.ncp5623" source "drivers/led/Kconfig.npm1300" source "drivers/led/Kconfig.pca9633" source "drivers/led/Kconfig.pwm" diff --git a/drivers/led/Kconfig.ncp5623 b/drivers/led/Kconfig.ncp5623 new file mode 100644 index 00000000000..817f93ab7ff --- /dev/null +++ b/drivers/led/Kconfig.ncp5623 @@ -0,0 +1,14 @@ +# Copyright (c) 2023 NXP Semiconductors +# Copyright (c) 2023 Cognipilot Foundation +# SPDX-License-Identifier: Apache-2.0 + +config NCP5623 + bool "NCP5623 LED driver" + default y + depends on DT_HAS_ONNN_NCP5623_ENABLED + select I2C + help + Enable driver for the Onsemi NCP5623 I2C LED controller. + The controller and the driver support two hardware configurations: + - one three-channel (RGB) LED + - or three single-channel LEDs diff --git a/drivers/led/ncp5623.c b/drivers/led/ncp5623.c new file mode 100644 index 00000000000..3cb58d4bc48 --- /dev/null +++ b/drivers/led/ncp5623.c @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2023 NXP Semiconductors + * Copyright (c) 2023 Cognipilot Foundation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT onnn_ncp5623 + +/** + * @file + * @brief NCP5623 LED driver + * + * The NCP5623 is a 3-channel LED driver that communicates over I2C. + */ + +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(ncp5623, CONFIG_LED_LOG_LEVEL); + +#define NCP5623_LED_CURRENT 0x20 +#define NCP5623_LED_PWM0 0x40 +#define NCP5623_LED_PWM1 0x60 +#define NCP5623_LED_PWM2 0x80 + +#define NCP5623_CHANNEL_COUNT 3 + +/* Brightness limits */ +#define NCP5623_MIN_BRIGHTNESS 0 +#define NCP5623_MAX_BRIGHTNESS 0x1f + +static const uint8_t led_channels[] = {NCP5623_LED_PWM0, NCP5623_LED_PWM1, NCP5623_LED_PWM2}; + +struct ncp5623_config { + struct i2c_dt_spec bus; + uint8_t num_leds; + const struct led_info *leds_info; +}; + +static const struct led_info *ncp5623_led_to_info(const struct ncp5623_config *config, uint32_t led) +{ + if (led < config->num_leds) { + return &config->leds_info[led]; + } + + return NULL; +} + +static int ncp5623_get_info(const struct device *dev, uint32_t led, const struct led_info **info) +{ + const struct ncp5623_config *config = dev->config; + const struct led_info *led_info = ncp5623_led_to_info(config, led); + + if (!led_info) { + return -EINVAL; + } + + *info = led_info; + + return 0; +} + +static int ncp5623_set_color(const struct device *dev, uint32_t led, uint8_t num_colors, + const uint8_t *color) +{ + const struct ncp5623_config *config = dev->config; + const struct led_info *led_info = ncp5623_led_to_info(config, led); + uint8_t buf[6] = {0x70, NCP5623_LED_PWM0, 0x70, NCP5623_LED_PWM1, 0x70, NCP5623_LED_PWM2}; + + if (!led_info) { + return -ENODEV; + } + + if (led_info->num_colors != 3) { + return -ENOTSUP; + } + if (num_colors != 3) { + return -EINVAL; + } + + buf[1] = buf[1] | color[0] / 8; + buf[3] = buf[3] | color[1] / 8; + buf[5] = buf[5] | color[2] / 8; + + return i2c_burst_write_dt(&config->bus, NCP5623_LED_CURRENT | NCP5623_MAX_BRIGHTNESS, buf, + sizeof(buf)); +} + +static int ncp5623_set_brightness(const struct device *dev, uint32_t led, uint8_t value) +{ + const struct ncp5623_config *config = dev->config; + const struct led_info *led_info = ncp5623_led_to_info(config, led); + int ret = 0; + + if (!led_info) { + return -ENODEV; + } + + if (value > 100) { + return -EINVAL; + } + + if (led_info->num_colors != 1) { + return -ENOTSUP; + } + + /* Rescale 0..100 to 0..31 */ + value = value * NCP5623_MAX_BRIGHTNESS / 100; + + ret = i2c_reg_write_byte_dt(&config->bus, led_channels[led] | value, 0x70); + + if (ret < 0) { + LOG_ERR("%s: LED write failed", dev->name); + } + + return ret; +} + +static inline int ncp5623_led_on(const struct device *dev, uint32_t led) +{ + return ncp5623_set_brightness(dev, led, 100); +} + +static inline int ncp5623_led_off(const struct device *dev, uint32_t led) +{ + return ncp5623_set_brightness(dev, led, 0); +} + +static int ncp5623_led_init(const struct device *dev) +{ + const struct ncp5623_config *config = dev->config; + const struct led_info *led_info = NULL; + int i; + uint8_t buf[6] = {0x70, NCP5623_LED_PWM0, 0x70, NCP5623_LED_PWM1, 0x70, NCP5623_LED_PWM2}; + + if (!i2c_is_ready_dt(&config->bus)) { + LOG_ERR("%s: I2C device not ready", dev->name); + return -ENODEV; + } + + if (config->num_leds == 1) { /* one three-channel (RGB) LED */ + led_info = ncp5623_led_to_info(config, 0); + + if (!led_info) { + return -ENODEV; + } + + if (led_info->num_colors != 3 || led_info->num_colors != 1) { + LOG_ERR("%s: invalid number of colors %d (must be %d or 1)", dev->name, + led_info->num_colors, NCP5623_CHANNEL_COUNT); + } + } else if (config->num_leds <= 3) { /* three single-channel LEDs */ + for (i = 0; i < config->num_leds; i++) { + led_info = ncp5623_led_to_info(config, 0); + + if (!led_info) { + return -ENODEV; + } + + if (led_info->num_colors > 1) { + LOG_ERR("%s: invalid number of colors %d (must be 1 when defining " + "multiple leds)", + dev->name, led_info->num_colors); + return -EINVAL; + } + } + } else { + LOG_ERR("%s: invalid number of leds %d (max %d)", dev->name, config->num_leds, + NCP5623_CHANNEL_COUNT); + return -EINVAL; + } + + if (i2c_burst_write_dt(&config->bus, NCP5623_LED_CURRENT | NCP5623_MAX_BRIGHTNESS, buf, + 6)) { + LOG_ERR("%s: LED write failed", dev->name); + return -EIO; + } + + return 0; +} + +static const struct led_driver_api ncp5623_led_api = { + .set_brightness = ncp5623_set_brightness, + .on = ncp5623_led_on, + .off = ncp5623_led_off, + .get_info = ncp5623_get_info, + .set_color = ncp5623_set_color, +}; + +#define COLOR_MAPPING(led_node_id) \ + static const uint8_t color_mapping_##led_node_id[] = DT_PROP(led_node_id, color_mapping); + +#define LED_INFO(led_node_id) \ + { \ + .label = DT_PROP(led_node_id, label), \ + .index = DT_PROP(led_node_id, index), \ + .num_colors = DT_PROP_LEN(led_node_id, color_mapping), \ + .color_mapping = color_mapping_##led_node_id, \ + }, + +#define NCP5623_DEFINE(id) \ + \ + DT_INST_FOREACH_CHILD(id, COLOR_MAPPING) \ + \ + static const struct led_info ncp5623_leds_##id[] = {DT_INST_FOREACH_CHILD(id, LED_INFO)}; \ + \ + static const struct ncp5623_config ncp5623_config_##id = { \ + .bus = I2C_DT_SPEC_INST_GET(id), \ + .num_leds = ARRAY_SIZE(ncp5623_leds_##id), \ + .leds_info = ncp5623_leds_##id, \ + }; \ + DEVICE_DT_INST_DEFINE(id, &ncp5623_led_init, NULL, NULL, &ncp5623_config_##id, \ + POST_KERNEL, CONFIG_LED_INIT_PRIORITY, &ncp5623_led_api); + +DT_INST_FOREACH_STATUS_OKAY(NCP5623_DEFINE) diff --git a/dts/bindings/led/onnn,ncp5623.yaml b/dts/bindings/led/onnn,ncp5623.yaml new file mode 100644 index 00000000000..b272d00f0ba --- /dev/null +++ b/dts/bindings/led/onnn,ncp5623.yaml @@ -0,0 +1,68 @@ +# Copyright (c) 2023 NXP Semiconductors +# Copyright (c) 2023 Congipilot Foundation +# SPDX-License-Identifier: Apache-2.0 + +description: | + NCP5623 Triple Output I2C Controlled RGB LED driver + + The LED_SHELL application can be used for testing + + The following example defines a single RGB LED in the ncp5623 DT node + + ncp5623c@39 { + compatible = "onnn,ncp5623"; + reg = <0x39>; + + led_0 { + label = "RGB LED"; + index = <0>; + color-mapping = + , + , + ; + }; + }; + + The following example defines three single-channel LEDs in the ncp5623 DT node + + ncp5623c@39 { + compatible = "onnn,ncp5623"; + reg = <0x39>; + + led_0 { + label = "RED LED"; + index = <0>; + color-mapping = + ; + }; + + led_1 { + label = "GREEN LED"; + index = <1>; + color-mapping = + ; + }; + + led_2 { + label = "BLUE LED"; + index = <2>; + color-mapping = + ; + }; + }; + + +compatible: "onnn,ncp5623" + +include: ["i2c-device.yaml", "led-controller.yaml"] + +child-binding: + properties: + label: + required: true + + index: + required: true + + color-mapping: + required: true diff --git a/tests/drivers/build_all/led/app.overlay b/tests/drivers/build_all/led/app.overlay index b0f41045d68..d61abc83dd8 100644 --- a/tests/drivers/build_all/led/app.overlay +++ b/tests/drivers/build_all/led/app.overlay @@ -99,6 +99,11 @@ compatible = "ti,tlc59108"; reg = <0xc>; }; + + ncp5623@d { + compatible = "onnn,ncp5623"; + reg = <0xd>; + }; }; }; };