1 /*
2 * Copyright (c) 2018 Savoir-Faire Linux.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT nxp_pca9633
8
9 /**
10 * @file
11 * @brief LED driver for the PCA9633 I2C LED driver (7-bit slave address 0x62)
12 */
13
14 #include <zephyr/drivers/i2c.h>
15 #include <zephyr/drivers/led.h>
16 #include <zephyr/sys/util.h>
17 #include <zephyr/kernel.h>
18
19 #define LOG_LEVEL CONFIG_LED_LOG_LEVEL
20 #include <zephyr/logging/log.h>
21 LOG_MODULE_REGISTER(pca9633);
22
23 #include "led_context.h"
24
25 /* PCA9633 select registers determine the source that drives LED outputs */
26 #define PCA9633_LED_OFF 0x0 /* LED driver off */
27 #define PCA9633_LED_ON 0x1 /* LED driver on */
28 #define PCA9633_LED_PWM 0x2 /* Controlled through PWM */
29 #define PCA9633_LED_GRP_PWM 0x3 /* Controlled through PWM/GRPPWM */
30
31 /* PCA9633 control register */
32 #define PCA9633_MODE1 0x00
33 #define PCA9633_MODE2 0x01
34 #define PCA9633_PWM_BASE 0x02 /* Reg 0x02-0x05 for brightness control LED01-04 */
35 #define PCA9633_GRPPWM 0x06
36 #define PCA9633_GRPFREQ 0x07
37 #define PCA9633_LEDOUT 0x08
38
39 /* PCA9633 mode register 1 */
40 #define PCA9633_MODE1_ALLCAL 0x01 /* All Call Address enabled */
41 #define PCA9633_MODE1_SLEEP 0x10 /* Sleep Mode */
42 /* PCA9633 mode register 2 */
43 #define PCA9633_MODE2_DMBLNK 0x20 /* Enable blinking */
44
45 #define PCA9633_MASK 0x03
46
47 struct pca9633_config {
48 struct i2c_dt_spec i2c;
49 bool disable_allcall;
50 };
51
52 struct pca9633_data {
53 struct led_data dev_data;
54 };
55
pca9633_led_blink(const struct device * dev,uint32_t led,uint32_t delay_on,uint32_t delay_off)56 static int pca9633_led_blink(const struct device *dev, uint32_t led,
57 uint32_t delay_on, uint32_t delay_off)
58 {
59 struct pca9633_data *data = dev->data;
60 const struct pca9633_config *config = dev->config;
61 struct led_data *dev_data = &data->dev_data;
62 uint8_t gdc, gfrq;
63 uint32_t period;
64
65 period = delay_on + delay_off;
66
67 if (period < dev_data->min_period || period > dev_data->max_period) {
68 return -EINVAL;
69 }
70
71 /*
72 * From manual:
73 * duty cycle = (GDC / 256) ->
74 * (time_on / period) = (GDC / 256) ->
75 * GDC = ((time_on * 256) / period)
76 */
77 gdc = delay_on * 256U / period;
78 if (i2c_reg_write_byte_dt(&config->i2c,
79 PCA9633_GRPPWM,
80 gdc)) {
81 LOG_ERR("LED reg write failed");
82 return -EIO;
83 }
84
85 /*
86 * From manual:
87 * period = ((GFRQ + 1) / 24) in seconds.
88 * So, period (in ms) = (((GFRQ + 1) / 24) * 1000) ->
89 * GFRQ = ((period * 24 / 1000) - 1)
90 */
91 gfrq = (period * 24U / 1000) - 1;
92 if (i2c_reg_write_byte_dt(&config->i2c,
93 PCA9633_GRPFREQ,
94 gfrq)) {
95 LOG_ERR("LED reg write failed");
96 return -EIO;
97 }
98
99 /* Enable blinking mode */
100 if (i2c_reg_update_byte_dt(&config->i2c,
101 PCA9633_MODE2,
102 PCA9633_MODE2_DMBLNK,
103 PCA9633_MODE2_DMBLNK)) {
104 LOG_ERR("LED reg update failed");
105 return -EIO;
106 }
107
108 /* Select the GRPPWM source to drive the LED output */
109 if (i2c_reg_update_byte_dt(&config->i2c,
110 PCA9633_LEDOUT,
111 PCA9633_MASK << (led << 1),
112 PCA9633_LED_GRP_PWM << (led << 1))) {
113 LOG_ERR("LED reg update failed");
114 return -EIO;
115 }
116
117 return 0;
118 }
119
pca9633_led_set_brightness(const struct device * dev,uint32_t led,uint8_t value)120 static int pca9633_led_set_brightness(const struct device *dev, uint32_t led,
121 uint8_t value)
122 {
123 const struct pca9633_config *config = dev->config;
124 struct pca9633_data *data = dev->data;
125 struct led_data *dev_data = &data->dev_data;
126 uint8_t val;
127
128 if (value < dev_data->min_brightness ||
129 value > dev_data->max_brightness) {
130 return -EINVAL;
131 }
132
133 /* Set the LED brightness value */
134 val = (value * 255U) / dev_data->max_brightness;
135 if (i2c_reg_write_byte_dt(&config->i2c,
136 PCA9633_PWM_BASE + led,
137 val)) {
138 LOG_ERR("LED reg write failed");
139 return -EIO;
140 }
141
142 /* Set the LED driver to be controlled through its PWMx register. */
143 if (i2c_reg_update_byte_dt(&config->i2c,
144 PCA9633_LEDOUT,
145 PCA9633_MASK << (led << 1),
146 PCA9633_LED_PWM << (led << 1))) {
147 LOG_ERR("LED reg update failed");
148 return -EIO;
149 }
150
151 return 0;
152 }
153
pca9633_led_on(const struct device * dev,uint32_t led)154 static inline int pca9633_led_on(const struct device *dev, uint32_t led)
155 {
156 const struct pca9633_config *config = dev->config;
157
158 /* Set LED state to ON */
159 if (i2c_reg_update_byte_dt(&config->i2c,
160 PCA9633_LEDOUT,
161 PCA9633_MASK << (led << 1),
162 PCA9633_LED_ON << (led << 1))) {
163 LOG_ERR("LED reg update failed");
164 return -EIO;
165 }
166
167 return 0;
168 }
169
pca9633_led_off(const struct device * dev,uint32_t led)170 static inline int pca9633_led_off(const struct device *dev, uint32_t led)
171 {
172 const struct pca9633_config *config = dev->config;
173
174 /* Set LED state to OFF */
175 if (i2c_reg_update_byte_dt(&config->i2c,
176 PCA9633_LEDOUT,
177 PCA9633_MASK << (led << 1),
178 PCA9633_LED_OFF)) {
179 LOG_ERR("LED reg update failed");
180 return -EIO;
181 }
182
183 return 0;
184 }
185
pca9633_led_init(const struct device * dev)186 static int pca9633_led_init(const struct device *dev)
187 {
188 const struct pca9633_config *config = dev->config;
189 struct pca9633_data *data = dev->data;
190 struct led_data *dev_data = &data->dev_data;
191
192 if (!device_is_ready(config->i2c.bus)) {
193 LOG_ERR("I2C bus is not ready");
194 return -ENODEV;
195 }
196
197 /*
198 * Take the LED driver out from Sleep mode and disable All Call Address
199 * if specified in DT.
200 */
201 if (i2c_reg_update_byte_dt(
202 &config->i2c, PCA9633_MODE1,
203 config->disable_allcall ? PCA9633_MODE1_SLEEP | PCA9633_MODE1_ALLCAL
204 : PCA9633_MODE1_SLEEP,
205 config->disable_allcall ? ~(PCA9633_MODE1_SLEEP | PCA9633_MODE1_ALLCAL)
206 : ~PCA9633_MODE1_SLEEP)) {
207 LOG_ERR("LED reg update failed");
208 return -EIO;
209 }
210 /* Hardware specific limits */
211 dev_data->min_period = 41U;
212 dev_data->max_period = 10667U;
213 dev_data->min_brightness = 0U;
214 dev_data->max_brightness = 100U;
215
216 return 0;
217 }
218
219 static DEVICE_API(led, pca9633_led_api) = {
220 .blink = pca9633_led_blink,
221 .set_brightness = pca9633_led_set_brightness,
222 .on = pca9633_led_on,
223 .off = pca9633_led_off,
224 };
225
226 #define PCA9633_DEVICE(id) \
227 static const struct pca9633_config pca9633_##id##_cfg = { \
228 .i2c = I2C_DT_SPEC_INST_GET(id), \
229 .disable_allcall = DT_INST_PROP(id, disable_allcall), \
230 }; \
231 static struct pca9633_data pca9633_##id##_data; \
232 \
233 DEVICE_DT_INST_DEFINE(id, &pca9633_led_init, NULL, \
234 &pca9633_##id##_data, \
235 &pca9633_##id##_cfg, POST_KERNEL, \
236 CONFIG_LED_INIT_PRIORITY, \
237 &pca9633_led_api);
238
239 DT_INST_FOREACH_STATUS_OKAY(PCA9633_DEVICE)
240