/* * Copyright (c) 2023 Phytec Messtechnik GmbH. * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT ti_lp5569 /** * @file * @brief LP5569 LED controller * * The LP5569 is a 9-channel LED driver that communicates over I2C. */ #include #include #include #include #include #include #include LOG_MODULE_REGISTER(lp5569, CONFIG_LED_LOG_LEVEL); #define LP5569_NUM_LEDS 9 /* General Registers */ #define LP5569_CONFIG 0x00 #define LP5569_CHIP_EN BIT(6) #define LP5569_MISC 0x2F #define LP5569_POWERSAVE_EN BIT(5) #define LP5569_CP_MODE_SHIFT 3 /* PWM base Register for controlling the duty-cycle */ #define LP5569_LED0_PWM 0x16 struct lp5569_config { struct i2c_dt_spec bus; struct gpio_dt_spec enable_gpio; const uint8_t cp_mode; }; static int lp5569_led_set_brightness(const struct device *dev, uint32_t led, uint8_t brightness) { const struct lp5569_config *config = dev->config; uint8_t val; int ret; if (led >= LP5569_NUM_LEDS || brightness > 100) { return -EINVAL; } /* Map 0-100 % to 0-255 pwm register value */ val = brightness * 255 / 100; ret = i2c_reg_write_byte_dt(&config->bus, LP5569_LED0_PWM + led, val); if (ret < 0) { LOG_ERR("LED reg update failed"); return ret; } return 0; } static inline int lp5569_led_on(const struct device *dev, uint32_t led) { /* Set LED brightness to 100 % */ return lp5569_led_set_brightness(dev, led, 100); } static inline int lp5569_led_off(const struct device *dev, uint32_t led) { /* Set LED brightness to 0 % */ return lp5569_led_set_brightness(dev, led, 0); } static int lp5569_enable(const struct device *dev) { const struct lp5569_config *config = dev->config; int ret; if (!i2c_is_ready_dt(&config->bus)) { LOG_ERR("I2C device not ready"); return -ENODEV; } /* flip the enable pin if specified */ if (config->enable_gpio.port) { if (!gpio_is_ready_dt(&config->enable_gpio)) { LOG_ERR("Enable GPIO not ready"); return -ENODEV; } ret = gpio_pin_configure_dt(&config->enable_gpio, GPIO_OUTPUT_ACTIVE); if (ret < 0) { LOG_ERR("Failed to configure enable_gpio, err: %d", ret); return ret; } /* datasheet 7.9: t_en max 3 ms for chip initialization */ k_msleep(3); } ret = i2c_reg_write_byte_dt(&config->bus, LP5569_CONFIG, LP5569_CHIP_EN); if (ret < 0) { LOG_ERR("Enable LP5569 failed"); return ret; } ret = i2c_reg_write_byte_dt(&config->bus, LP5569_MISC, LP5569_POWERSAVE_EN | (config->cp_mode << LP5569_CP_MODE_SHIFT)); if (ret < 0) { LOG_ERR("LED reg update failed"); return ret; } return 0; } static int lp5569_init(const struct device *dev) { /* If the device is behind a power domain, it will start in * PM_DEVICE_STATE_OFF. */ if (pm_device_on_power_domain(dev)) { pm_device_init_off(dev); LOG_INF("Init %s as PM_DEVICE_STATE_OFF", dev->name); return 0; } return lp5569_enable(dev); } #ifdef CONFIG_PM_DEVICE static int lp5569_pm_action(const struct device *dev, enum pm_device_action action) { const struct lp5569_config *config = dev->config; int ret; switch (action) { case PM_DEVICE_ACTION_TURN_ON: case PM_DEVICE_ACTION_RESUME: ret = lp5569_enable(dev); if (ret < 0) { LOG_ERR("Enable LP5569 failed"); return ret; } break; case PM_DEVICE_ACTION_TURN_OFF: case PM_DEVICE_ACTION_SUSPEND: ret = i2c_reg_update_byte_dt(&config->bus, LP5569_CONFIG, LP5569_CHIP_EN, 0); if (ret < 0) { LOG_ERR("Disable LP5569 failed"); return ret; } break; default: return -ENOTSUP; } return 0; } #endif /* CONFIG_PM_DEVICE */ static const struct led_driver_api lp5569_led_api = { .set_brightness = lp5569_led_set_brightness, .on = lp5569_led_on, .off = lp5569_led_off, }; #define LP5569_DEFINE(id) \ static const struct lp5569_config lp5569_config_##id = { \ .bus = I2C_DT_SPEC_INST_GET(id), \ .enable_gpio = GPIO_DT_SPEC_INST_GET_OR(id, enable_gpios, {0}), \ .cp_mode = DT_ENUM_IDX(DT_DRV_INST(id), charge_pump_mode), \ }; \ \ PM_DEVICE_DT_INST_DEFINE(id, lp5569_pm_action); \ \ DEVICE_DT_INST_DEFINE(id, &lp5569_init, PM_DEVICE_DT_INST_GET(id), NULL, \ &lp5569_config_##id, POST_KERNEL, CONFIG_LED_INIT_PRIORITY, \ &lp5569_led_api); DT_INST_FOREACH_STATUS_OKAY(LP5569_DEFINE)