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