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 <drivers/i2c.h>
15 #include <drivers/led.h>
16 #include <sys/util.h>
17 #include <zephyr.h>
18
19 #define LOG_LEVEL CONFIG_LED_LOG_LEVEL
20 #include <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_SLEEP 0x10 /* Sleep Mode */
41 /* PCA9633 mode register 2 */
42 #define PCA9633_MODE2_DMBLNK 0x20 /* Enable blinking */
43
44 #define PCA9633_MASK 0x03
45
46 struct pca9633_config {
47 struct i2c_dt_spec i2c;
48 };
49
50 struct pca9633_data {
51 struct led_data dev_data;
52 };
53
pca9633_led_blink(const struct device * dev,uint32_t led,uint32_t delay_on,uint32_t delay_off)54 static int pca9633_led_blink(const struct device *dev, uint32_t led,
55 uint32_t delay_on, uint32_t delay_off)
56 {
57 struct pca9633_data *data = dev->data;
58 const struct pca9633_config *config = dev->config;
59 struct led_data *dev_data = &data->dev_data;
60 uint8_t gdc, gfrq;
61 uint32_t period;
62
63 period = delay_on + delay_off;
64
65 if (period < dev_data->min_period || period > dev_data->max_period) {
66 return -EINVAL;
67 }
68
69 /*
70 * From manual:
71 * duty cycle = (GDC / 256) ->
72 * (time_on / period) = (GDC / 256) ->
73 * GDC = ((time_on * 256) / period)
74 */
75 gdc = delay_on * 256U / period;
76 if (i2c_reg_write_byte_dt(&config->i2c,
77 PCA9633_GRPPWM,
78 gdc)) {
79 LOG_ERR("LED reg write failed");
80 return -EIO;
81 }
82
83 /*
84 * From manual:
85 * period = ((GFRQ + 1) / 24) in seconds.
86 * So, period (in ms) = (((GFRQ + 1) / 24) * 1000) ->
87 * GFRQ = ((period * 24 / 1000) - 1)
88 */
89 gfrq = (period * 24U / 1000) - 1;
90 if (i2c_reg_write_byte_dt(&config->i2c,
91 PCA9633_GRPFREQ,
92 gfrq)) {
93 LOG_ERR("LED reg write failed");
94 return -EIO;
95 }
96
97 /* Enable blinking mode */
98 if (i2c_reg_update_byte_dt(&config->i2c,
99 PCA9633_MODE2,
100 PCA9633_MODE2_DMBLNK,
101 PCA9633_MODE2_DMBLNK)) {
102 LOG_ERR("LED reg update failed");
103 return -EIO;
104 }
105
106 /* Select the GRPPWM source to drive the LED outpout */
107 if (i2c_reg_update_byte_dt(&config->i2c,
108 PCA9633_LEDOUT,
109 PCA9633_MASK << (led << 1),
110 PCA9633_LED_GRP_PWM << (led << 1))) {
111 LOG_ERR("LED reg update failed");
112 return -EIO;
113 }
114
115 return 0;
116 }
117
pca9633_led_set_brightness(const struct device * dev,uint32_t led,uint8_t value)118 static int pca9633_led_set_brightness(const struct device *dev, uint32_t led,
119 uint8_t value)
120 {
121 const struct pca9633_config *config = dev->config;
122 struct pca9633_data *data = dev->data;
123 struct led_data *dev_data = &data->dev_data;
124 uint8_t val;
125
126 if (value < dev_data->min_brightness ||
127 value > dev_data->max_brightness) {
128 return -EINVAL;
129 }
130
131 /* Set the LED brightness value */
132 val = (value * 255U) / dev_data->max_brightness;
133 if (i2c_reg_write_byte_dt(&config->i2c,
134 PCA9633_PWM_BASE + led,
135 val)) {
136 LOG_ERR("LED reg write failed");
137 return -EIO;
138 }
139
140 /* Set the LED driver to be controlled through its PWMx register. */
141 if (i2c_reg_update_byte_dt(&config->i2c,
142 PCA9633_LEDOUT,
143 PCA9633_MASK << (led << 1),
144 PCA9633_LED_PWM << (led << 1))) {
145 LOG_ERR("LED reg update failed");
146 return -EIO;
147 }
148
149 return 0;
150 }
151
pca9633_led_on(const struct device * dev,uint32_t led)152 static inline int pca9633_led_on(const struct device *dev, uint32_t led)
153 {
154 const struct pca9633_config *config = dev->config;
155
156 /* Set LED state to ON */
157 if (i2c_reg_update_byte_dt(&config->i2c,
158 PCA9633_LEDOUT,
159 PCA9633_MASK << (led << 1),
160 PCA9633_LED_ON << (led << 1))) {
161 LOG_ERR("LED reg update failed");
162 return -EIO;
163 }
164
165 return 0;
166 }
167
pca9633_led_off(const struct device * dev,uint32_t led)168 static inline int pca9633_led_off(const struct device *dev, uint32_t led)
169 {
170 const struct pca9633_config *config = dev->config;
171
172 /* Set LED state to OFF */
173 if (i2c_reg_update_byte_dt(&config->i2c,
174 PCA9633_LEDOUT,
175 PCA9633_MASK << (led << 1),
176 PCA9633_LED_OFF)) {
177 LOG_ERR("LED reg update failed");
178 return -EIO;
179 }
180
181 return 0;
182 }
183
pca9633_led_init(const struct device * dev)184 static int pca9633_led_init(const struct device *dev)
185 {
186 const struct pca9633_config *config = dev->config;
187 struct pca9633_data *data = dev->data;
188 struct led_data *dev_data = &data->dev_data;
189
190 if (!device_is_ready(config->i2c.bus)) {
191 LOG_ERR("I2C bus is not ready");
192 return -ENODEV;
193 }
194
195 /* Take the LED driver out from Sleep mode. */
196 if (i2c_reg_update_byte_dt(&config->i2c,
197 PCA9633_MODE1,
198 PCA9633_MODE1_SLEEP,
199 ~PCA9633_MODE1_SLEEP)) {
200 LOG_ERR("LED reg update failed");
201 return -EIO;
202 }
203 /* Hardware specific limits */
204 dev_data->min_period = 41U;
205 dev_data->max_period = 10667U;
206 dev_data->min_brightness = 0U;
207 dev_data->max_brightness = 100U;
208
209 return 0;
210 }
211
212 static const struct led_driver_api pca9633_led_api = {
213 .blink = pca9633_led_blink,
214 .set_brightness = pca9633_led_set_brightness,
215 .on = pca9633_led_on,
216 .off = pca9633_led_off,
217 };
218
219 #define PCA9633_DEVICE(id) \
220 static const struct pca9633_config pca9633_##id##_cfg = { \
221 .i2c = I2C_DT_SPEC_INST_GET(id) \
222 }; \
223 static struct pca9633_data pca9633_##id##_data; \
224 \
225 DEVICE_DT_INST_DEFINE(id, &pca9633_led_init, NULL, \
226 &pca9633_##id##_data, \
227 &pca9633_##id##_cfg, POST_KERNEL, \
228 CONFIG_LED_INIT_PRIORITY, \
229 &pca9633_led_api);
230
231 DT_INST_FOREACH_STATUS_OKAY(PCA9633_DEVICE)
232