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