1 /*
2 * Copyright (c) 2022 Chromium OS Authors
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #define DT_DRV_COMPAT ti_tca6424a
7
8 #include <zephyr/drivers/gpio/gpio_utils.h>
9 #include <zephyr/drivers/gpio.h>
10 #include <zephyr/drivers/i2c.h>
11 #include <zephyr/logging/log.h>
12 #include <zephyr/sys/byteorder.h>
13 #include <zephyr/sys/util.h>
14 LOG_MODULE_REGISTER(gpio_tca6424a, CONFIG_GPIO_LOG_LEVEL);
15
16 /* TCA6424A auto increment register addresses */
17 #define TCA6424A_REG_INPUT 0x80
18 #define TCA6424A_REG_OUTPUT 0x84
19 #define TCA6424A_REG_POLARITY_INVERSION 0x88
20 #define TCA6424A_REG_CONFIGURATION 0x8C
21
22 /** Cache of the output configuration and data of the pins. */
23 struct tca6424a_pins_state {
24 uint32_t input;
25 uint32_t output;
26 uint32_t config;
27 };
28
29 struct tca6424a_irq_state {
30 uint32_t rising;
31 uint32_t falling;
32 };
33
34 /** Configuration data */
35 struct tca6424a_drv_cfg {
36 /* gpio_driver_config needs to be first */
37 struct gpio_driver_config common;
38
39 struct i2c_dt_spec i2c_spec;
40 struct gpio_dt_spec int_gpio;
41 struct gpio_dt_spec reset_gpio;
42 };
43
44 /** Runtime driver data */
45 struct tca6424a_drv_data {
46 /* gpio_driver_data needs to be first */
47 struct gpio_driver_data common;
48
49 sys_slist_t callbacks;
50 struct k_sem lock;
51 struct k_work work;
52 const struct device *dev;
53 struct gpio_callback int_gpio_cb;
54 struct tca6424a_pins_state pins_state;
55 struct tca6424a_irq_state irq_state;
56 };
57
58 /**
59 * @brief Gets the state of a specified block of 3 registers from the TCA6424A
60 * and stores it in specified cache and buffer variables.
61 *
62 * @param dev Pointer to the device structure for the driver instance.
63 * @param reg Address of the first of 3 registers to be read.
64 * @param cache Pointer to the location to cache the pins states.
65 * @param buf Pointer to the buffer to output the register.
66 *
67 * @retval 0 if successful.
68 * @retval Negative value for error code.
69 */
read_port_regs(const struct device * dev,uint8_t reg,uint32_t * buf)70 static int read_port_regs(const struct device *dev, uint8_t reg, uint32_t *buf)
71 {
72 const struct tca6424a_drv_cfg *const config = dev->config;
73 uint32_t port_data, value;
74 int ret;
75
76 ret = i2c_burst_read_dt(&config->i2c_spec, reg, (uint8_t *)&port_data, 3);
77 if (ret != 0) {
78 LOG_ERR("%s: error reading register 0x%X (%d)", dev->name,
79 reg, ret);
80 return ret;
81 }
82
83 value = sys_le24_to_cpu(port_data);
84 *buf = value;
85 LOG_DBG("%s: Read: REG[0x%X] = 0x%X, REG[0x%X] = 0x%X, "
86 "REG[0x%X] = 0x%X",
87 dev->name, reg, (*buf & 0xFF), (reg + 1), ((*buf >> 8) & 0xFF),
88 (reg + 2), ((*buf >> 16) & 0xFF));
89
90 return 0;
91 }
92
93 /**
94 * @brief writes to a specified block of 3 registers from the TCA6424A
95 * and stores it in a specified cache variable.
96 *
97 * @param dev Pointer to the device structure for the driver instance.
98 * @param reg Address of the first of 3 registers to be written.
99 * @param cache Pointer to the location to cache the pins states.
100 * @param value The pin value to be written into the registers.
101 *
102 * @retval 0 if successful.
103 * @retval Negative value for error code.
104 */
write_port_regs(const struct device * dev,uint8_t reg,uint32_t value)105 static int write_port_regs(const struct device *dev, uint8_t reg, uint32_t value)
106 {
107 const struct tca6424a_drv_cfg *const config = dev->config;
108 uint8_t buf[4];
109 int ret;
110
111 LOG_DBG("%s: Write: REG[0x%X] = 0x%X, REG[0x%X] = 0x%X, "
112 "REG[0x%X] = 0x%X",
113 dev->name, reg, (value & 0xFF), (reg + 1), ((value >> 8) & 0xFF),
114 (reg + 2), ((value >> 16) & 0xFF));
115
116 buf[0] = reg;
117 sys_put_le24(value, &buf[1]);
118 ret = i2c_write_dt(&config->i2c_spec, buf, sizeof(buf));
119 if (ret != 0) {
120 LOG_ERR("%s: error writing to register 0x%X "
121 "(%d)",
122 dev->name, reg, ret);
123 }
124 return ret;
125 }
126
update_input_regs(const struct device * dev,uint32_t * buf)127 static inline int update_input_regs(const struct device *dev, uint32_t *buf)
128 {
129 int ret;
130 struct tca6424a_drv_data *const drv_data = dev->data;
131
132 ret = read_port_regs(dev, TCA6424A_REG_INPUT, buf);
133 if (ret == 0) {
134 drv_data->pins_state.input = *buf;
135 }
136
137 return ret;
138 }
139
update_output_regs(const struct device * dev,uint32_t value)140 static inline int update_output_regs(const struct device *dev, uint32_t value)
141 {
142 int ret;
143 struct tca6424a_drv_data *const drv_data = dev->data;
144
145 ret = write_port_regs(dev, TCA6424A_REG_OUTPUT, value);
146 if (ret == 0) {
147 drv_data->pins_state.output = value;
148 }
149
150 return ret;
151 }
152
update_invers_regs(const struct device * dev,uint32_t value)153 static inline int update_invers_regs(const struct device *dev, uint32_t value)
154 {
155 return write_port_regs(dev, TCA6424A_REG_POLARITY_INVERSION, value);
156 }
157
update_config_regs(const struct device * dev,uint32_t value)158 static inline int update_config_regs(const struct device *dev, uint32_t value)
159 {
160 int ret;
161 struct tca6424a_drv_data *const drv_data = dev->data;
162
163 ret = write_port_regs(dev, TCA6424A_REG_CONFIGURATION, value);
164 if (ret == 0) {
165 drv_data->pins_state.config = value;
166 }
167
168 return ret;
169 }
170
171 /**
172 * @brief Handles interrupt triggered by the interrupt pin of TCA6424A.
173 *
174 * If int_gpios is configured in device tree then this will be triggered each
175 * time a gpio configured as an input changes state. The gpio input states are
176 * read in this function which clears the interrupt.
177 *
178 * @param dev Pointer to the device structure for the driver instance.
179 */
tca6424a_handle_interrupt(const struct device * dev)180 static void tca6424a_handle_interrupt(const struct device *dev)
181 {
182 struct tca6424a_drv_data *drv_data = dev->data;
183 struct tca6424a_irq_state *irq_state = &drv_data->irq_state;
184 int ret;
185 uint32_t previous_state;
186 uint32_t current_state;
187 uint32_t transitioned_pins;
188 uint32_t interrupt_status;
189
190 k_sem_take(&drv_data->lock, K_FOREVER);
191
192 /* Any interrupts enabled? */
193 if (!irq_state->rising && !irq_state->falling) {
194 k_sem_give(&drv_data->lock);
195 return;
196 }
197
198 /* Store previous input state then read new value */
199 previous_state = drv_data->pins_state.input;
200 ret = update_input_regs(dev, ¤t_state);
201 if (ret != 0) {
202 k_sem_give(&drv_data->lock);
203 return;
204 }
205
206 /* Find out which input pins have changed state */
207 transitioned_pins = previous_state ^ current_state;
208
209 /* Mask gpio transactions with rising/falling edge interrupt config */
210 interrupt_status = (irq_state->rising & transitioned_pins & current_state);
211 interrupt_status |= (irq_state->falling & transitioned_pins & previous_state);
212 k_sem_give(&drv_data->lock);
213
214 if (interrupt_status) {
215 gpio_fire_callbacks(&drv_data->callbacks, dev, interrupt_status);
216 }
217 }
218
219 /**
220 * @brief Work handler for TCA6424A interrupt
221 *
222 * @param work Work struct that contains pointer to interrupt handler function
223 */
tca6424a_work_handler(struct k_work * work)224 static void tca6424a_work_handler(struct k_work *work)
225 {
226 struct tca6424a_drv_data *drv_data = CONTAINER_OF(work, struct tca6424a_drv_data, work);
227
228 tca6424a_handle_interrupt(drv_data->dev);
229 }
230
231 /**
232 * @brief ISR for interrupt pin of TCA6424A
233 *
234 * @param dev Pointer to the device structure for the driver instance.
235 * @param gpio_cb Pointer to callback function struct
236 * @param pins Bitmask of pins that triggered interrupt
237 */
tca6424a_int_gpio_handler(const struct device * dev,struct gpio_callback * gpio_cb,uint32_t pins)238 static void tca6424a_int_gpio_handler(const struct device *dev, struct gpio_callback *gpio_cb,
239 uint32_t pins)
240 {
241 ARG_UNUSED(dev);
242 ARG_UNUSED(pins);
243
244 struct tca6424a_drv_data *drv_data =
245 CONTAINER_OF(gpio_cb, struct tca6424a_drv_data, int_gpio_cb);
246
247 k_work_submit(&drv_data->work);
248 }
249
tca6424a_setup_pin(const struct device * dev,gpio_pin_t pin,gpio_flags_t flags)250 static int tca6424a_setup_pin(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags)
251 {
252 struct tca6424a_drv_data *const drv_data = dev->data;
253 uint32_t reg_cfg = drv_data->pins_state.config;
254 uint32_t reg_out = drv_data->pins_state.output;
255 int ret;
256
257 /* For each pin, 0 == output, 1 == input */
258 if ((flags & GPIO_OUTPUT) != 0) {
259 if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0) {
260 reg_out |= BIT(pin);
261 } else if ((flags & GPIO_OUTPUT_INIT_LOW) != 0) {
262 reg_out &= ~BIT(pin);
263 }
264 ret = update_output_regs(dev, reg_out);
265 if (ret != 0) {
266 return ret;
267 }
268 reg_cfg &= ~BIT(pin);
269 } else {
270 reg_cfg |= BIT(pin);
271 }
272
273 ret = update_config_regs(dev, reg_cfg);
274
275 return ret;
276 }
277
tca6424a_pin_config(const struct device * dev,gpio_pin_t pin,gpio_flags_t flags)278 static int tca6424a_pin_config(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags)
279 {
280 int ret;
281 struct tca6424a_drv_data *const drv_data = dev->data;
282
283 /* Does not support disconnected pin */
284 if ((flags & (GPIO_INPUT | GPIO_OUTPUT)) == GPIO_DISCONNECTED) {
285 return -ENOTSUP;
286 }
287
288 /* Single Ended lines (Open drain and open source) not supported */
289 if ((flags & GPIO_SINGLE_ENDED) != 0) {
290 return -ENOTSUP;
291 }
292
293 /* The TCA6424A has no internal pull up support */
294 if (((flags & GPIO_PULL_UP) != 0) || ((flags & GPIO_PULL_DOWN) != 0)) {
295 return -ENOTSUP;
296 }
297
298 /* Simultaneous input & output mode not supported */
299 if (((flags & GPIO_INPUT) != 0) && ((flags & GPIO_OUTPUT) != 0)) {
300 return -ENOTSUP;
301 }
302
303 /* Can't do I2C bus operations from an ISR */
304 if (k_is_in_isr()) {
305 return -EWOULDBLOCK;
306 }
307
308 k_sem_take(&drv_data->lock, K_FOREVER);
309
310 ret = tca6424a_setup_pin(dev, pin, flags);
311 if (ret != 0) {
312 LOG_ERR("%s: error setting pin direction (%d)", dev->name, ret);
313 }
314
315 k_sem_give(&drv_data->lock);
316 return ret;
317 }
318
tca6424a_port_get_raw(const struct device * dev,gpio_port_value_t * value)319 static int tca6424a_port_get_raw(const struct device *dev, gpio_port_value_t *value)
320 {
321 struct tca6424a_drv_data *const drv_data = dev->data;
322 uint32_t buf;
323 int ret;
324
325 /* Can't do I2C bus operations from an ISR */
326 if (k_is_in_isr()) {
327 return -EWOULDBLOCK;
328 }
329
330 k_sem_take(&drv_data->lock, K_FOREVER);
331
332 ret = update_input_regs(dev, &buf);
333 if (ret == 0) {
334 *value = buf;
335 }
336
337 k_sem_give(&drv_data->lock);
338 return ret;
339 }
340
tca6424a_port_set_masked_raw(const struct device * dev,gpio_port_pins_t mask,gpio_port_value_t value)341 static int tca6424a_port_set_masked_raw(const struct device *dev, gpio_port_pins_t mask,
342 gpio_port_value_t value)
343 {
344 struct tca6424a_drv_data *const drv_data = dev->data;
345 uint32_t reg_out;
346 int ret;
347
348 /* Can't do I2C bus operations from an ISR */
349 if (k_is_in_isr()) {
350 return -EWOULDBLOCK;
351 }
352
353 k_sem_take(&drv_data->lock, K_FOREVER);
354
355 reg_out = drv_data->pins_state.output;
356 reg_out = (reg_out & ~mask) | (mask & value);
357
358 ret = update_output_regs(dev, reg_out);
359
360 k_sem_give(&drv_data->lock);
361
362 return ret;
363 }
364
tca6424a_port_set_bits_raw(const struct device * dev,gpio_port_pins_t mask)365 static int tca6424a_port_set_bits_raw(const struct device *dev, gpio_port_pins_t mask)
366 {
367 return tca6424a_port_set_masked_raw(dev, mask, mask);
368 }
369
tca6424a_port_clear_bits_raw(const struct device * dev,gpio_port_pins_t mask)370 static int tca6424a_port_clear_bits_raw(const struct device *dev, gpio_port_pins_t mask)
371 {
372 return tca6424a_port_set_masked_raw(dev, mask, 0);
373 }
374
tca6424a_port_toggle_bits(const struct device * dev,gpio_port_pins_t mask)375 static int tca6424a_port_toggle_bits(const struct device *dev, gpio_port_pins_t mask)
376 {
377 struct tca6424a_drv_data *const drv_data = dev->data;
378 uint32_t reg_out;
379 int ret;
380
381 /* Can't do I2C bus operations from an ISR */
382 if (k_is_in_isr()) {
383 return -EWOULDBLOCK;
384 }
385
386 k_sem_take(&drv_data->lock, K_FOREVER);
387
388 reg_out = drv_data->pins_state.output;
389 reg_out ^= mask;
390
391 ret = update_output_regs(dev, reg_out);
392
393 k_sem_give(&drv_data->lock);
394
395 return ret;
396 }
397
tca6424a_pin_interrupt_configure(const struct device * dev,gpio_pin_t pin,enum gpio_int_mode mode,enum gpio_int_trig trig)398 static int tca6424a_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin,
399 enum gpio_int_mode mode, enum gpio_int_trig trig)
400 {
401 struct tca6424a_drv_data *drv_data = dev->data;
402 struct tca6424a_irq_state *irq = &drv_data->irq_state;
403
404 /* Device does not support level-triggered interrupts. */
405 if (mode == GPIO_INT_MODE_LEVEL) {
406 return -ENOTSUP;
407 }
408
409 k_sem_take(&drv_data->lock, K_FOREVER);
410
411 if (mode == GPIO_INT_MODE_DISABLED) {
412 irq->falling &= ~BIT(pin);
413 irq->rising &= ~BIT(pin);
414 } else { /* GPIO_INT_MODE_EDGE */
415 if (trig == GPIO_INT_TRIG_BOTH) {
416 irq->falling |= BIT(pin);
417 irq->rising |= BIT(pin);
418 } else if (trig == GPIO_INT_TRIG_LOW) {
419 irq->falling |= BIT(pin);
420 irq->rising &= ~BIT(pin);
421 } else if (trig == GPIO_INT_TRIG_HIGH) {
422 irq->falling &= ~BIT(pin);
423 irq->rising |= BIT(pin);
424 }
425 }
426
427 k_sem_give(&drv_data->lock);
428
429 return 0;
430 }
431
tca6424a_manage_callback(const struct device * dev,struct gpio_callback * callback,bool set)432 static int tca6424a_manage_callback(const struct device *dev, struct gpio_callback *callback,
433 bool set)
434 {
435 struct tca6424a_drv_data *drv_data = dev->data;
436
437 return gpio_manage_callback(&drv_data->callbacks, callback, set);
438 }
439
440 static const struct gpio_driver_api tca6424a_drv_api = {
441 .pin_configure = tca6424a_pin_config,
442 .port_get_raw = tca6424a_port_get_raw,
443 .port_set_masked_raw = tca6424a_port_set_masked_raw,
444 .port_set_bits_raw = tca6424a_port_set_bits_raw,
445 .port_clear_bits_raw = tca6424a_port_clear_bits_raw,
446 .port_toggle_bits = tca6424a_port_toggle_bits,
447 .pin_interrupt_configure = tca6424a_pin_interrupt_configure,
448 .manage_callback = tca6424a_manage_callback,
449 };
450
451 /**
452 * @brief Initialization function of TCA6424A
453 *
454 * This sets initial input/output configuration and output states.
455 * The interrupt is configured if it is enabled.
456 *
457 * @param dev Device struct
458 *
459 * @retval 0 if successful.
460 * @retval Negative value for error code.
461 */
tca6424a_init(const struct device * dev)462 static int tca6424a_init(const struct device *dev)
463 {
464 const struct tca6424a_drv_cfg *drv_cfg = dev->config;
465 struct tca6424a_drv_data *drv_data = dev->data;
466 int ret;
467
468 if (!device_is_ready(drv_cfg->i2c_spec.bus)) {
469 LOG_ERR("I2C device not found");
470 return -ENODEV;
471 }
472 /* If the RESET line is available, use it to reset the expander.
473 * Otherwise, write reset values to registers that are not used by
474 * this driver.
475 */
476 if (drv_cfg->reset_gpio.port) {
477 if (!gpio_is_ready_dt(&drv_cfg->reset_gpio)) {
478 LOG_ERR("%s is not ready", drv_cfg->reset_gpio.port->name);
479 return -ENODEV;
480 }
481 ret = gpio_pin_configure_dt(&drv_cfg->reset_gpio, GPIO_OUTPUT_ACTIVE);
482 if (ret != 0) {
483 LOG_ERR("%s: failed to configure RESET line: %d", dev->name, ret);
484 return ret;
485 }
486 /* RESET signal needs to be active for a minimum of 30 ns. */
487 k_busy_wait(1);
488
489 ret = gpio_pin_set_dt(&drv_cfg->reset_gpio, 0);
490 if (ret != 0) {
491 LOG_ERR("%s: failed to deactivate RESET line: %d", dev->name, ret);
492 return ret;
493 }
494 /* Give the expander at least 200 ns to recover after reset. */
495 k_busy_wait(1);
496 } else {
497 ret = update_invers_regs(dev, 0x0);
498 if (ret != 0) {
499 LOG_ERR("%s: failed to reset inversion register: %d", dev->name, ret);
500 return ret;
501 }
502 }
503 /* Set initial configuration of the pins. */
504 ret = update_config_regs(dev, 0xFFFFFF);
505 if (ret != 0) {
506 return ret;
507 }
508
509 ret = update_output_regs(dev, 0x0);
510 if (ret != 0) {
511 return ret;
512 }
513
514 /* Read initial state of the input port register. */
515 ret = update_input_regs(dev, &drv_data->pins_state.input);
516 if (ret != 0) {
517 LOG_ERR("%s: failed to initially read input port: %d", dev->name, ret);
518 return ret;
519 }
520
521 /* If the INT line is available, configure the callback for it. */
522 if (drv_cfg->int_gpio.port) {
523 if (!gpio_is_ready_dt(&drv_cfg->int_gpio)) {
524 LOG_ERR("Cannot get pointer to gpio interrupt device "
525 "%s init failed", dev->name);
526 return -EINVAL;
527 }
528
529 drv_data->dev = dev;
530
531 k_work_init(&drv_data->work, tca6424a_work_handler);
532
533 ret = gpio_pin_configure_dt(&drv_cfg->int_gpio, GPIO_INPUT);
534 if (ret != 0) {
535 LOG_ERR("%s init failed: %d", dev->name, ret);
536 return ret;
537 }
538
539 ret = gpio_pin_interrupt_configure_dt(&drv_cfg->int_gpio, GPIO_INT_EDGE_TO_ACTIVE);
540 if (ret != 0) {
541 LOG_ERR("%s init failed: %d", dev->name, ret);
542 return ret;
543 }
544
545 gpio_init_callback(&drv_data->int_gpio_cb, tca6424a_int_gpio_handler,
546 BIT(drv_cfg->int_gpio.pin));
547
548 ret = gpio_add_callback(drv_cfg->int_gpio.port, &drv_data->int_gpio_cb);
549 if (ret != 0) {
550 LOG_ERR("%s init failed: %d", dev->name, ret);
551 return ret;
552 }
553 }
554
555 LOG_DBG("%s init ok", dev->name);
556 return ret;
557 }
558
559 #define TCA6424A_INST(idx) \
560 static const struct tca6424a_drv_cfg tca6424a_cfg##idx = { \
561 .common = { \
562 .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(idx), \
563 }, \
564 .i2c_spec = I2C_DT_SPEC_INST_GET(idx), \
565 .int_gpio = GPIO_DT_SPEC_INST_GET_OR(idx, int_gpios, {0}), \
566 .reset_gpio = GPIO_DT_SPEC_INST_GET_OR(idx, reset_gpios, {0}), \
567 }; \
568 static struct tca6424a_drv_data tca6424a_data##idx = { \
569 .lock = Z_SEM_INITIALIZER(tca6424a_data##idx.lock, 1, 1), \
570 .work = Z_WORK_INITIALIZER(tca6424a_work_handler), \
571 .dev = DEVICE_DT_INST_GET(idx), \
572 }; \
573 DEVICE_DT_INST_DEFINE(idx, tca6424a_init, NULL, &tca6424a_data##idx, &tca6424a_cfg##idx, \
574 POST_KERNEL, CONFIG_GPIO_TCA6424A_INIT_PRIORITY, &tca6424a_drv_api);
575
576 DT_INST_FOREACH_STATUS_OKAY(TCA6424A_INST)
577