1 /*
2 * Copyright (c) 2023 Phytec Messtechnik GmbH.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT ti_lp5569
8
9 /**
10 * @file
11 * @brief LP5569 LED controller
12 *
13 * The LP5569 is a 9-channel LED driver that communicates over I2C.
14 */
15
16 #include <zephyr/drivers/i2c.h>
17 #include <zephyr/drivers/led.h>
18 #include <zephyr/device.h>
19 #include <zephyr/pm/device.h>
20 #include <zephyr/kernel.h>
21
22 #include <zephyr/logging/log.h>
23 LOG_MODULE_REGISTER(lp5569, CONFIG_LED_LOG_LEVEL);
24
25 #define LP5569_NUM_LEDS 9
26
27 /* General Registers */
28 #define LP5569_CONFIG 0x00
29 #define LP5569_CHIP_EN BIT(6)
30
31 #define LP5569_MISC 0x2F
32 #define LP5569_POWERSAVE_EN BIT(5)
33
34 /* PWM base Register for controlling the duty-cycle */
35 #define LP5569_LED0_PWM 0x16
36
37 struct lp5569_config {
38 struct i2c_dt_spec bus;
39 };
40
lp5569_led_set_brightness(const struct device * dev,uint32_t led,uint8_t brightness)41 static int lp5569_led_set_brightness(const struct device *dev, uint32_t led,
42 uint8_t brightness)
43 {
44 const struct lp5569_config *config = dev->config;
45 uint8_t val;
46 int ret;
47
48 if (led >= LP5569_NUM_LEDS || brightness > 100) {
49 return -EINVAL;
50 }
51
52 /* Map 0-100 % to 0-255 pwm register value */
53 val = brightness * 255 / 100;
54
55 ret = i2c_reg_write_byte_dt(&config->bus, LP5569_LED0_PWM + led, val);
56 if (ret < 0) {
57 LOG_ERR("LED reg update failed");
58 return ret;
59 }
60
61 return 0;
62 }
63
lp5569_led_on(const struct device * dev,uint32_t led)64 static inline int lp5569_led_on(const struct device *dev, uint32_t led)
65 {
66 /* Set LED brightness to 100 % */
67 return lp5569_led_set_brightness(dev, led, 100);
68 }
69
lp5569_led_off(const struct device * dev,uint32_t led)70 static inline int lp5569_led_off(const struct device *dev, uint32_t led)
71 {
72 /* Set LED brightness to 0 % */
73 return lp5569_led_set_brightness(dev, led, 0);
74 }
75
lp5569_enable(const struct device * dev)76 static int lp5569_enable(const struct device *dev)
77 {
78 const struct lp5569_config *config = dev->config;
79 int ret;
80
81 if (!i2c_is_ready_dt(&config->bus)) {
82 LOG_ERR("I2C device not ready");
83 return -ENODEV;
84 }
85
86 ret = i2c_reg_write_byte_dt(&config->bus, LP5569_CONFIG,
87 LP5569_CHIP_EN);
88 if (ret < 0) {
89 LOG_ERR("Enable LP5569 failed");
90 return ret;
91 }
92
93 ret = i2c_reg_write_byte_dt(&config->bus, LP5569_MISC,
94 LP5569_POWERSAVE_EN);
95 if (ret < 0) {
96 LOG_ERR("LED reg update failed");
97 return ret;
98 }
99
100 return 0;
101 }
102
lp5569_init(const struct device * dev)103 static int lp5569_init(const struct device *dev)
104 {
105 /* If the device is behind a power domain, it will start in
106 * PM_DEVICE_STATE_OFF.
107 */
108 if (pm_device_on_power_domain(dev)) {
109 pm_device_init_off(dev);
110 LOG_INF("Init %s as PM_DEVICE_STATE_OFF", dev->name);
111 return 0;
112 }
113
114 return lp5569_enable(dev);
115 }
116
117 #ifdef CONFIG_PM_DEVICE
lp5569_pm_action(const struct device * dev,enum pm_device_action action)118 static int lp5569_pm_action(const struct device *dev,
119 enum pm_device_action action)
120 {
121 const struct lp5569_config *config = dev->config;
122 int ret;
123
124 switch (action) {
125 case PM_DEVICE_ACTION_TURN_ON:
126 case PM_DEVICE_ACTION_RESUME:
127 ret = lp5569_enable(dev);
128 if (ret < 0) {
129 LOG_ERR("Enable LP5569 failed");
130 return ret;
131 }
132 break;
133 case PM_DEVICE_ACTION_TURN_OFF:
134 case PM_DEVICE_ACTION_SUSPEND:
135 ret = i2c_reg_update_byte_dt(&config->bus, LP5569_CONFIG,
136 LP5569_CHIP_EN, 0);
137 if (ret < 0) {
138 LOG_ERR("Disable LP5569 failed");
139 return ret;
140 }
141 break;
142 default:
143 return -ENOTSUP;
144 }
145
146 return 0;
147 }
148 #endif /* CONFIG_PM_DEVICE */
149
150 static const struct led_driver_api lp5569_led_api = {
151 .set_brightness = lp5569_led_set_brightness,
152 .on = lp5569_led_on,
153 .off = lp5569_led_off,
154 };
155
156 #define LP5569_DEFINE(id) \
157 static const struct lp5569_config lp5569_config_##id = { \
158 .bus = I2C_DT_SPEC_INST_GET(id), \
159 }; \
160 \
161 PM_DEVICE_DT_INST_DEFINE(id, lp5569_pm_action); \
162 \
163 DEVICE_DT_INST_DEFINE(id, &lp5569_init, \
164 PM_DEVICE_DT_INST_GET(id), \
165 NULL, \
166 &lp5569_config_##id, POST_KERNEL, \
167 CONFIG_LED_INIT_PRIORITY, \
168 &lp5569_led_api);
169
170 DT_INST_FOREACH_STATUS_OKAY(LP5569_DEFINE)
171