1 /*
2 * Copyright (c) 2021 Nordic Semiconductor ASA
3 * Copyright (c) 2023 SILA Embedded Solutions GmbH
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <zephyr/kernel.h>
9 #include <zephyr/drivers/gpio.h>
10 #include <zephyr/drivers/gpio/gpio_utils.h>
11 #include <zephyr/drivers/i2c.h>
12 #include <zephyr/logging/log.h>
13
14 LOG_MODULE_REGISTER(pcal64xxa, CONFIG_GPIO_LOG_LEVEL);
15
16 enum pcal6408a_register {
17 PCAL6408A_REG_INPUT_PORT = 0x00,
18 PCAL6408A_REG_OUTPUT_PORT = 0x01,
19 PCAL6408A_REG_POLARITY_INVERSION = 0x02,
20 PCAL6408A_REG_CONFIGURATION = 0x03,
21 PCAL6408A_REG_OUTPUT_DRIVE_STRENGTH_0 = 0x40,
22 PCAL6408A_REG_OUTPUT_DRIVE_STRENGTH_1 = 0x41,
23 PCAL6408A_REG_INPUT_LATCH = 0x42,
24 PCAL6408A_REG_PULL_UP_DOWN_ENABLE = 0x43,
25 PCAL6408A_REG_PULL_UP_DOWN_SELECT = 0x44,
26 PCAL6408A_REG_INTERRUPT_MASK = 0x45,
27 PCAL6408A_REG_INTERRUPT_STATUS = 0x46,
28 PCAL6408A_REG_OUTPUT_PORT_CONFIGURATION = 0x4f,
29 };
30
31 enum pcal6416a_register {
32 PCAL6416A_REG_INPUT_PORT_0 = 0x00,
33 PCAL6416A_REG_INPUT_PORT_1 = 0x01,
34 PCAL6416A_REG_OUTPUT_PORT_0 = 0x02,
35 PCAL6416A_REG_OUTPUT_PORT_1 = 0x03,
36 PCAL6416A_REG_POLARITY_INVERSION_0 = 0x04,
37 PCAL6416A_REG_POLARITY_INVERSION_1 = 0x05,
38 PCAL6416A_REG_CONFIGURATION_0 = 0x06,
39 PCAL6416A_REG_CONFIGURATION_1 = 0x07,
40 PCAL6416A_REG_OUTPUT_DRIVE_STRENGTH_0_0 = 0x40,
41 PCAL6416A_REG_OUTPUT_DRIVE_STRENGTH_0_1 = 0x41,
42 PCAL6416A_REG_OUTPUT_DRIVE_STRENGTH_1_0 = 0x42,
43 PCAL6416A_REG_OUTPUT_DRIVE_STRENGTH_1_1 = 0x43,
44 PCAL6416A_REG_INPUT_LATCH_0 = 0x44,
45 PCAL6416A_REG_INPUT_LATCH_1 = 0x45,
46 PCAL6416A_REG_PULL_UP_DOWN_ENABLE_0 = 0x46,
47 PCAL6416A_REG_PULL_UP_DOWN_ENABLE_1 = 0x47,
48 PCAL6416A_REG_PULL_UP_DOWN_SELECT_0 = 0x48,
49 PCAL6416A_REG_PULL_UP_DOWN_SELECT_1 = 0x49,
50 PCAL6416A_REG_INTERRUPT_MASK_0 = 0x4A,
51 PCAL6416A_REG_INTERRUPT_MASK_1 = 0x4B,
52 PCAL6416A_REG_INTERRUPT_STATUS_0 = 0x4C,
53 PCAL6416A_REG_INTERRUPT_STATUS_1 = 0x4D,
54 PCAL6416A_REG_OUTPUT_PORT_CONFIGURATION = 0x4F,
55 };
56
57 struct pcal64xxa_pins_cfg {
58 uint16_t configured_as_inputs;
59 uint16_t outputs_high;
60 uint16_t pull_ups_selected;
61 uint16_t pulls_enabled;
62 };
63
64 struct pcal64xxa_triggers {
65 uint16_t masked;
66 uint16_t dual_edge;
67 uint16_t on_low;
68 };
69
70 struct pcal64xxa_drv_data {
71 /* gpio_driver_data needs to be first */
72 struct gpio_driver_data common;
73
74 sys_slist_t callbacks;
75 struct k_sem lock;
76 struct k_work work;
77 const struct device *dev;
78 struct gpio_callback int_gpio_cb;
79 struct pcal64xxa_pins_cfg pins_cfg;
80 struct pcal64xxa_triggers triggers;
81 uint16_t input_port_last;
82 };
83
84 typedef int (*pcal64xxa_pins_cfg_apply)(const struct i2c_dt_spec *i2c,
85 const struct pcal64xxa_pins_cfg *pins_cfg);
86 typedef int (*pcal64xxa_triggers_apply)(const struct i2c_dt_spec *i2c,
87 const struct pcal64xxa_triggers *triggers);
88 typedef int (*pcal64xxa_reset_state_apply)(const struct i2c_dt_spec *i2c);
89 typedef int (*pcal64xxa_inputs_read)(const struct i2c_dt_spec *i2c, uint16_t *int_sources,
90 uint16_t *input_port);
91 typedef int (*pcal64xxa_outputs_write)(const struct i2c_dt_spec *i2c, uint16_t outputs);
92
93 struct pcal64xxa_chip_api {
94 pcal64xxa_pins_cfg_apply pins_cfg_apply;
95 pcal64xxa_triggers_apply triggers_apply;
96 pcal64xxa_inputs_read inputs_read;
97 pcal64xxa_outputs_write outputs_write;
98 pcal64xxa_reset_state_apply reset_state_apply;
99 };
100
101 struct pcal64xxa_drv_cfg {
102 /* gpio_driver_config needs to be first */
103 struct gpio_driver_config common;
104
105 struct i2c_dt_spec i2c;
106 uint8_t ngpios;
107 const struct gpio_dt_spec gpio_reset;
108 const struct gpio_dt_spec gpio_interrupt;
109 const struct pcal64xxa_chip_api *chip_api;
110 };
111
pcal64xxa_pin_configure(const struct device * dev,gpio_pin_t pin,gpio_flags_t flags)112 static int pcal64xxa_pin_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags)
113 {
114 struct pcal64xxa_drv_data *drv_data = dev->data;
115 const struct pcal64xxa_drv_cfg *drv_cfg = dev->config;
116 struct pcal64xxa_pins_cfg pins_cfg;
117 gpio_flags_t flags_io;
118 int rc;
119
120 LOG_DBG("configure pin %i with flags 0x%08X", pin, flags);
121
122 /* This device does not support open-source outputs, and open-drain
123 * outputs can be only configured port-wise.
124 */
125 if ((flags & GPIO_SINGLE_ENDED) != 0) {
126 return -ENOTSUP;
127 }
128
129 /* Pins in this device can be either inputs or outputs and cannot be
130 * completely disconnected.
131 */
132 flags_io = (flags & (GPIO_INPUT | GPIO_OUTPUT));
133 if (flags_io == (GPIO_INPUT | GPIO_OUTPUT) || flags_io == GPIO_DISCONNECTED) {
134 return -ENOTSUP;
135 }
136
137 if (k_is_in_isr()) {
138 return -EWOULDBLOCK;
139 }
140
141 k_sem_take(&drv_data->lock, K_FOREVER);
142
143 pins_cfg = drv_data->pins_cfg;
144
145 if ((flags & (GPIO_PULL_UP | GPIO_PULL_DOWN)) != 0) {
146 if ((flags & GPIO_PULL_UP) != 0) {
147 pins_cfg.pull_ups_selected |= BIT(pin);
148 } else {
149 pins_cfg.pull_ups_selected &= ~BIT(pin);
150 }
151
152 pins_cfg.pulls_enabled |= BIT(pin);
153 } else {
154 pins_cfg.pulls_enabled &= ~BIT(pin);
155 }
156
157 if ((flags & GPIO_OUTPUT) != 0) {
158 if ((flags & GPIO_OUTPUT_INIT_LOW) != 0) {
159 pins_cfg.outputs_high &= ~BIT(pin);
160 } else if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0) {
161 pins_cfg.outputs_high |= BIT(pin);
162 }
163
164 pins_cfg.configured_as_inputs &= ~BIT(pin);
165 } else {
166 pins_cfg.configured_as_inputs |= BIT(pin);
167 }
168
169 rc = drv_cfg->chip_api->pins_cfg_apply(&drv_cfg->i2c, &pins_cfg);
170 if (rc == 0) {
171 drv_data->pins_cfg = pins_cfg;
172 } else {
173 LOG_ERR("failed to apply pin config for device %s", dev->name);
174 }
175
176 k_sem_give(&drv_data->lock);
177
178 return rc;
179 }
180
pcal64xxa_process_input(const struct device * dev,gpio_port_value_t * value)181 static int pcal64xxa_process_input(const struct device *dev, gpio_port_value_t *value)
182 {
183 const struct pcal64xxa_drv_cfg *drv_cfg = dev->config;
184 struct pcal64xxa_drv_data *drv_data = dev->data;
185 int rc;
186 uint16_t int_sources;
187 uint16_t input_port;
188
189 k_sem_take(&drv_data->lock, K_FOREVER);
190
191 rc = drv_cfg->chip_api->inputs_read(&drv_cfg->i2c, &int_sources, &input_port);
192
193 if (rc != 0) {
194 LOG_ERR("failed to read inputs from device %s", dev->name);
195 return rc;
196 }
197
198 if (value) {
199 *value = input_port;
200 }
201
202 /* It may happen that some inputs change their states between above
203 * reads of the interrupt status and input port registers. Such changes
204 * will not be noted in `int_sources`, thus to correctly detect them,
205 * the current state of inputs needs to be additionally compared with
206 * the one read last time, and any differences need to be added to
207 * `int_sources`.
208 */
209 int_sources |= ((input_port ^ drv_data->input_port_last) & ~drv_data->triggers.masked);
210
211 drv_data->input_port_last = input_port;
212
213 if (int_sources) {
214 uint16_t dual_edge_triggers = drv_data->triggers.dual_edge;
215 uint16_t falling_edge_triggers = (~dual_edge_triggers & drv_data->triggers.on_low);
216 uint16_t fired_triggers = 0;
217
218 /* For dual edge triggers, react to all state changes. */
219 fired_triggers |= (int_sources & dual_edge_triggers);
220 /* For single edge triggers, fire callbacks only for the pins
221 * that transitioned to their configured target state (0 for
222 * falling edges, 1 otherwise, hence the XOR operation below).
223 */
224 fired_triggers |= ((input_port & int_sources) ^ falling_edge_triggers);
225
226 /* Give back semaphore before the callback to make the same
227 * driver available again for the callback.
228 */
229 k_sem_give(&drv_data->lock);
230
231 gpio_fire_callbacks(&drv_data->callbacks, dev, fired_triggers);
232 } else {
233 k_sem_give(&drv_data->lock);
234 }
235
236 return 0;
237 }
238
pcal64xxa_work_handler(struct k_work * work)239 static void pcal64xxa_work_handler(struct k_work *work)
240 {
241 struct pcal64xxa_drv_data *drv_data = CONTAINER_OF(work, struct pcal64xxa_drv_data, work);
242
243 (void)pcal64xxa_process_input(drv_data->dev, NULL);
244 }
245
pcal64xxa_int_gpio_handler(const struct device * dev,struct gpio_callback * gpio_cb,uint32_t pins)246 static void pcal64xxa_int_gpio_handler(const struct device *dev, struct gpio_callback *gpio_cb,
247 uint32_t pins)
248 {
249 ARG_UNUSED(dev);
250 ARG_UNUSED(pins);
251
252 struct pcal64xxa_drv_data *drv_data =
253 CONTAINER_OF(gpio_cb, struct pcal64xxa_drv_data, int_gpio_cb);
254
255 k_work_submit(&drv_data->work);
256 }
257
pcal64xxa_port_get_raw(const struct device * dev,gpio_port_value_t * value)258 static int pcal64xxa_port_get_raw(const struct device *dev, gpio_port_value_t *value)
259 {
260 int rc;
261
262 if (k_is_in_isr()) {
263 return -EWOULDBLOCK;
264 }
265
266 /* Reading of the input port also clears the generated interrupt,
267 * thus the configured callbacks must be fired also here if needed.
268 */
269 rc = pcal64xxa_process_input(dev, value);
270
271 return rc;
272 }
273
pcal64xxa_port_set_raw(const struct device * dev,uint16_t mask,uint16_t value,uint16_t toggle)274 static int pcal64xxa_port_set_raw(const struct device *dev, uint16_t mask, uint16_t value,
275 uint16_t toggle)
276 {
277 const struct pcal64xxa_drv_cfg *drv_cfg = dev->config;
278 struct pcal64xxa_drv_data *drv_data = dev->data;
279 int rc;
280 uint16_t output;
281
282 LOG_DBG("setting port with mask 0x%04X with value 0x%04X and toggle 0x%04X", mask, value,
283 toggle);
284
285 if (k_is_in_isr()) {
286 return -EWOULDBLOCK;
287 }
288
289 k_sem_take(&drv_data->lock, K_FOREVER);
290
291 output = (drv_data->pins_cfg.outputs_high & ~mask);
292 output |= (value & mask);
293 output ^= toggle;
294 /*
295 * No need to limit `out` to only pins configured as outputs,
296 * as the chip anyway ignores all other bits in the register.
297 */
298 rc = drv_cfg->chip_api->outputs_write(&drv_cfg->i2c, output);
299 if (rc == 0) {
300 drv_data->pins_cfg.outputs_high = output;
301 }
302
303 k_sem_give(&drv_data->lock);
304
305 if (rc != 0) {
306 LOG_ERR("%s: failed to write output port: %d", dev->name, rc);
307 return -EIO;
308 }
309
310 return 0;
311 }
312
pcal64xxa_port_set_masked_raw(const struct device * dev,gpio_port_pins_t mask,gpio_port_value_t value)313 static int pcal64xxa_port_set_masked_raw(const struct device *dev, gpio_port_pins_t mask,
314 gpio_port_value_t value)
315 {
316 return pcal64xxa_port_set_raw(dev, (uint16_t)mask, (uint16_t)value, 0);
317 }
318
pcal64xxa_port_set_bits_raw(const struct device * dev,gpio_port_pins_t pins)319 static int pcal64xxa_port_set_bits_raw(const struct device *dev, gpio_port_pins_t pins)
320 {
321 return pcal64xxa_port_set_raw(dev, (uint16_t)pins, (uint16_t)pins, 0);
322 }
323
pcal64xxa_port_clear_bits_raw(const struct device * dev,gpio_port_pins_t pins)324 static int pcal64xxa_port_clear_bits_raw(const struct device *dev, gpio_port_pins_t pins)
325 {
326 return pcal64xxa_port_set_raw(dev, (uint16_t)pins, 0, 0);
327 }
328
pcal64xxa_port_toggle_bits(const struct device * dev,gpio_port_pins_t pins)329 static int pcal64xxa_port_toggle_bits(const struct device *dev, gpio_port_pins_t pins)
330 {
331 return pcal64xxa_port_set_raw(dev, 0, 0, (uint16_t)pins);
332 }
333
pcal64xxa_pin_interrupt_configure(const struct device * dev,gpio_pin_t pin,enum gpio_int_mode mode,enum gpio_int_trig trig)334 static int pcal64xxa_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin,
335 enum gpio_int_mode mode, enum gpio_int_trig trig)
336 {
337 const struct pcal64xxa_drv_cfg *drv_cfg = dev->config;
338 struct pcal64xxa_drv_data *drv_data = dev->data;
339 struct pcal64xxa_triggers triggers;
340 int rc;
341
342 LOG_DBG("configure interrupt for pin %i", pin);
343
344 if (drv_cfg->gpio_interrupt.port == NULL) {
345 return -ENOTSUP;
346 }
347
348 /* This device supports only edge-triggered interrupts. */
349 if (mode == GPIO_INT_MODE_LEVEL) {
350 return -ENOTSUP;
351 }
352
353 if (k_is_in_isr()) {
354 return -EWOULDBLOCK;
355 }
356
357 k_sem_take(&drv_data->lock, K_FOREVER);
358
359 triggers = drv_data->triggers;
360
361 if (mode == GPIO_INT_MODE_DISABLED) {
362 triggers.masked |= BIT(pin);
363 } else {
364 triggers.masked &= ~BIT(pin);
365 }
366
367 if (trig == GPIO_INT_TRIG_BOTH) {
368 triggers.dual_edge |= BIT(pin);
369 } else {
370 triggers.dual_edge &= ~BIT(pin);
371
372 if (trig == GPIO_INT_TRIG_LOW) {
373 triggers.on_low |= BIT(pin);
374 } else {
375 triggers.on_low &= ~BIT(pin);
376 }
377 }
378
379 rc = drv_cfg->chip_api->triggers_apply(&drv_cfg->i2c, &triggers);
380 if (rc == 0) {
381 drv_data->triggers = triggers;
382 } else {
383 LOG_ERR("failed to apply triggers for device %s", dev->name);
384 }
385
386 k_sem_give(&drv_data->lock);
387
388 return rc;
389 }
390
pcal64xxa_manage_callback(const struct device * dev,struct gpio_callback * callback,bool set)391 static int pcal64xxa_manage_callback(const struct device *dev,
392 struct gpio_callback *callback, bool set)
393 {
394 struct pcal64xxa_drv_data *drv_data = dev->data;
395
396 return gpio_manage_callback(&drv_data->callbacks, callback, set);
397 }
398
pcal64xxa_i2c_write(const struct i2c_dt_spec * i2c,uint8_t register_address,uint8_t value)399 static int pcal64xxa_i2c_write(const struct i2c_dt_spec *i2c, uint8_t register_address,
400 uint8_t value)
401 {
402 int rc;
403
404 LOG_DBG("writing to register 0x%02X value 0x%02X", register_address, value);
405 rc = i2c_reg_write_byte_dt(i2c, register_address, value);
406
407 if (rc != 0) {
408 LOG_ERR("unable to write to register 0x%02X, error %i", register_address, rc);
409 }
410
411 return rc;
412 }
413
pcal64xxa_i2c_read(const struct i2c_dt_spec * i2c,uint8_t register_address,uint8_t * value)414 static int pcal64xxa_i2c_read(const struct i2c_dt_spec *i2c, uint8_t register_address,
415 uint8_t *value)
416 {
417 int rc;
418
419 rc = i2c_reg_read_byte_dt(i2c, register_address, value);
420 LOG_DBG("reading from register 0x%02X value 0x%02X", register_address, *value);
421
422 if (rc != 0) {
423 LOG_ERR("unable to read from register 0x%02X, error %i", register_address, rc);
424 }
425
426 return rc;
427 }
428
429 #if DT_HAS_COMPAT_STATUS_OKAY(nxp_pcal6408a)
pcal6408a_pins_cfg_apply(const struct i2c_dt_spec * i2c,const struct pcal64xxa_pins_cfg * pins_cfg)430 static int pcal6408a_pins_cfg_apply(const struct i2c_dt_spec *i2c,
431 const struct pcal64xxa_pins_cfg *pins_cfg)
432 {
433 int rc;
434
435 rc = pcal64xxa_i2c_write(i2c, PCAL6408A_REG_PULL_UP_DOWN_SELECT,
436 (uint8_t)pins_cfg->pull_ups_selected);
437 if (rc != 0) {
438 return -EIO;
439 }
440
441 rc = pcal64xxa_i2c_write(i2c, PCAL6408A_REG_PULL_UP_DOWN_ENABLE,
442 (uint8_t)pins_cfg->pulls_enabled);
443 if (rc != 0) {
444 return -EIO;
445 }
446
447 rc = pcal64xxa_i2c_write(i2c, PCAL6408A_REG_OUTPUT_PORT, (uint8_t)pins_cfg->outputs_high);
448 if (rc != 0) {
449 return -EIO;
450 }
451
452 rc = pcal64xxa_i2c_write(i2c, PCAL6408A_REG_CONFIGURATION,
453 (uint8_t)pins_cfg->configured_as_inputs);
454 if (rc != 0) {
455 return -EIO;
456 }
457
458 return 0;
459 }
460
pcal6408a_inputs_read(const struct i2c_dt_spec * i2c,uint16_t * int_sources,uint16_t * input_port)461 static int pcal6408a_inputs_read(const struct i2c_dt_spec *i2c, uint16_t *int_sources,
462 uint16_t *input_port)
463 {
464 int rc;
465 uint8_t value;
466
467 rc = pcal64xxa_i2c_read(i2c, PCAL6408A_REG_INTERRUPT_STATUS, &value);
468 if (rc != 0) {
469 return -EIO;
470 }
471
472 *int_sources = value;
473
474 /* This read also clears the generated interrupt if any. */
475 rc = pcal64xxa_i2c_read(i2c, PCAL6408A_REG_INPUT_PORT, &value);
476 if (rc != 0) {
477 return -EIO;
478 }
479
480 *input_port = value;
481
482 return 0;
483 }
484
pcal6408a_outputs_write(const struct i2c_dt_spec * i2c,uint16_t outputs)485 static int pcal6408a_outputs_write(const struct i2c_dt_spec *i2c, uint16_t outputs)
486 {
487 int rc;
488
489 /*
490 * No need to limit `out` to only pins configured as outputs,
491 * as the chip anyway ignores all other bits in the register.
492 */
493 rc = pcal64xxa_i2c_write(i2c, PCAL6408A_REG_OUTPUT_PORT, (uint8_t)outputs);
494
495 if (rc != 0) {
496 LOG_ERR("failed to write output port: %d", rc);
497 return -EIO;
498 }
499
500 return 0;
501 }
502
pcal6408a_triggers_apply(const struct i2c_dt_spec * i2c,const struct pcal64xxa_triggers * triggers)503 static int pcal6408a_triggers_apply(const struct i2c_dt_spec *i2c,
504 const struct pcal64xxa_triggers *triggers)
505 {
506 int rc;
507 uint8_t input_latch = ~triggers->masked;
508 uint8_t interrupt_mask = triggers->masked;
509
510 rc = pcal64xxa_i2c_write(i2c, PCAL6408A_REG_INPUT_LATCH, (uint8_t)input_latch);
511 if (rc != 0) {
512 LOG_ERR("failed to configure input latch: %d", rc);
513 return -EIO;
514 }
515
516 rc = pcal64xxa_i2c_write(i2c, PCAL6408A_REG_INTERRUPT_MASK, (uint8_t)interrupt_mask);
517 if (rc != 0) {
518 LOG_ERR("failed to configure interrupt mask: %d", rc);
519 return -EIO;
520 }
521
522 return 0;
523 }
524
pcal6408a_reset_state_apply(const struct i2c_dt_spec * i2c)525 static int pcal6408a_reset_state_apply(const struct i2c_dt_spec *i2c)
526 {
527 int rc;
528 static const uint8_t reset_state[][2] = {
529 {PCAL6408A_REG_POLARITY_INVERSION, 0},
530 {PCAL6408A_REG_OUTPUT_DRIVE_STRENGTH_0, 0xff},
531 {PCAL6408A_REG_OUTPUT_DRIVE_STRENGTH_1, 0xff},
532 {PCAL6408A_REG_OUTPUT_PORT_CONFIGURATION, 0},
533 };
534
535 for (int i = 0; i < ARRAY_SIZE(reset_state); ++i) {
536 rc = pcal64xxa_i2c_write(i2c, reset_state[i][0], reset_state[i][1]);
537 if (rc != 0) {
538 LOG_ERR("failed to reset register %02x: %d", reset_state[i][0], rc);
539 return -EIO;
540 }
541 }
542
543 return 0;
544 }
545
546 static const struct pcal64xxa_chip_api pcal6408a_chip_api = {
547 .pins_cfg_apply = pcal6408a_pins_cfg_apply,
548 .triggers_apply = pcal6408a_triggers_apply,
549 .inputs_read = pcal6408a_inputs_read,
550 .outputs_write = pcal6408a_outputs_write,
551 .reset_state_apply = pcal6408a_reset_state_apply,
552 };
553 #endif /* DT_HAS_COMPAT_STATUS_OKAY(nxp_pcal6408a) */
554
555 #if DT_HAS_COMPAT_STATUS_OKAY(nxp_pcal6416a)
pcal6416a_pins_cfg_apply(const struct i2c_dt_spec * i2c,const struct pcal64xxa_pins_cfg * pins_cfg)556 static int pcal6416a_pins_cfg_apply(const struct i2c_dt_spec *i2c,
557 const struct pcal64xxa_pins_cfg *pins_cfg)
558 {
559 int rc;
560
561 rc = pcal64xxa_i2c_write(i2c, PCAL6416A_REG_PULL_UP_DOWN_SELECT_0,
562 (uint8_t)pins_cfg->pull_ups_selected);
563 if (rc != 0) {
564 return -EIO;
565 }
566
567 rc = pcal64xxa_i2c_write(i2c, PCAL6416A_REG_PULL_UP_DOWN_SELECT_1,
568 (uint8_t)(pins_cfg->pull_ups_selected >> 8));
569 if (rc != 0) {
570 return -EIO;
571 }
572
573 rc = pcal64xxa_i2c_write(i2c, PCAL6416A_REG_PULL_UP_DOWN_ENABLE_0,
574 (uint8_t)pins_cfg->pulls_enabled);
575 if (rc != 0) {
576 return -EIO;
577 }
578
579 rc = pcal64xxa_i2c_write(i2c, PCAL6416A_REG_PULL_UP_DOWN_ENABLE_1,
580 (uint8_t)(pins_cfg->pulls_enabled >> 8));
581 if (rc != 0) {
582 return -EIO;
583 }
584
585 rc = pcal64xxa_i2c_write(i2c, PCAL6416A_REG_OUTPUT_PORT_0, (uint8_t)pins_cfg->outputs_high);
586 if (rc != 0) {
587 return -EIO;
588 }
589
590 rc = pcal64xxa_i2c_write(i2c, PCAL6416A_REG_OUTPUT_PORT_1,
591 (uint8_t)(pins_cfg->outputs_high >> 8));
592 if (rc != 0) {
593 return -EIO;
594 }
595
596 rc = pcal64xxa_i2c_write(i2c, PCAL6416A_REG_CONFIGURATION_0,
597 (uint8_t)pins_cfg->configured_as_inputs);
598 if (rc != 0) {
599 return -EIO;
600 }
601
602 rc = pcal64xxa_i2c_write(i2c, PCAL6416A_REG_CONFIGURATION_1,
603 (uint8_t)(pins_cfg->configured_as_inputs >> 8));
604 if (rc != 0) {
605 return -EIO;
606 }
607
608 return 0;
609 }
610
pcal6416a_inputs_read(const struct i2c_dt_spec * i2c,uint16_t * int_sources,uint16_t * input_port)611 static int pcal6416a_inputs_read(const struct i2c_dt_spec *i2c, uint16_t *int_sources,
612 uint16_t *input_port)
613 {
614 int rc;
615 uint8_t value_low;
616 uint8_t value_high;
617
618 rc = pcal64xxa_i2c_read(i2c, PCAL6416A_REG_INTERRUPT_STATUS_0, &value_low);
619 if (rc != 0) {
620 return -EIO;
621 }
622
623 rc = pcal64xxa_i2c_read(i2c, PCAL6416A_REG_INTERRUPT_STATUS_1, &value_high);
624 if (rc != 0) {
625 return -EIO;
626 }
627
628 *int_sources = value_low | (value_high << 8);
629
630 /* This read also clears the generated interrupt if any. */
631 rc = pcal64xxa_i2c_read(i2c, PCAL6416A_REG_INPUT_PORT_0, &value_low);
632 if (rc != 0) {
633 return -EIO;
634 }
635
636 rc = pcal64xxa_i2c_read(i2c, PCAL6416A_REG_INPUT_PORT_1, &value_high);
637 if (rc != 0) {
638 LOG_ERR("failed to read input port: %d", rc);
639 return -EIO;
640 }
641
642 *input_port = value_low | (value_high << 8);
643
644 return 0;
645 }
646
pcal6416a_outputs_write(const struct i2c_dt_spec * i2c,uint16_t outputs)647 static int pcal6416a_outputs_write(const struct i2c_dt_spec *i2c, uint16_t outputs)
648 {
649 int rc;
650
651 /*
652 * No need to limit `out` to only pins configured as outputs,
653 * as the chip anyway ignores all other bits in the register.
654 */
655 rc = pcal64xxa_i2c_write(i2c, PCAL6416A_REG_OUTPUT_PORT_0, (uint8_t)outputs);
656
657 if (rc != 0) {
658 LOG_ERR("failed to write output port: %d", rc);
659 return -EIO;
660 }
661
662 rc = pcal64xxa_i2c_write(i2c, PCAL6416A_REG_OUTPUT_PORT_1, (uint8_t)(outputs >> 8));
663
664 if (rc != 0) {
665 LOG_ERR("failed to write output port: %d", rc);
666 return -EIO;
667 }
668
669 return 0;
670 }
671
pcal6416a_triggers_apply(const struct i2c_dt_spec * i2c,const struct pcal64xxa_triggers * triggers)672 static int pcal6416a_triggers_apply(const struct i2c_dt_spec *i2c,
673 const struct pcal64xxa_triggers *triggers)
674 {
675 int rc;
676 uint16_t input_latch = ~triggers->masked;
677 uint16_t interrupt_mask = triggers->masked;
678
679 rc = pcal64xxa_i2c_write(i2c, PCAL6416A_REG_INPUT_LATCH_0, (uint8_t)input_latch);
680 if (rc != 0) {
681 LOG_ERR("failed to configure input latch: %d", rc);
682 return -EIO;
683 }
684
685 rc = pcal64xxa_i2c_write(i2c, PCAL6416A_REG_INPUT_LATCH_1, (uint8_t)(input_latch >> 8));
686 if (rc != 0) {
687 LOG_ERR("failed to configure input latch: %d", rc);
688 return -EIO;
689 }
690
691 rc = pcal64xxa_i2c_write(i2c, PCAL6416A_REG_INTERRUPT_MASK_0, (uint8_t)interrupt_mask);
692 if (rc != 0) {
693 LOG_ERR("failed to configure interrupt mask: %d", rc);
694 return -EIO;
695 }
696
697 rc = pcal64xxa_i2c_write(i2c, PCAL6416A_REG_INTERRUPT_MASK_1,
698 (uint8_t)(interrupt_mask >> 8));
699 if (rc != 0) {
700 LOG_ERR("failed to configure interrupt mask: %d", rc);
701 return -EIO;
702 }
703
704 return 0;
705 }
706
pcal6416a_reset_state_apply(const struct i2c_dt_spec * i2c)707 static int pcal6416a_reset_state_apply(const struct i2c_dt_spec *i2c)
708 {
709 int rc;
710 static const uint8_t reset_state[][2] = {
711 {PCAL6416A_REG_POLARITY_INVERSION_0, 0},
712 {PCAL6416A_REG_POLARITY_INVERSION_1, 0},
713 {PCAL6416A_REG_OUTPUT_DRIVE_STRENGTH_0_0, 0xff},
714 {PCAL6416A_REG_OUTPUT_DRIVE_STRENGTH_0_1, 0xff},
715 {PCAL6416A_REG_OUTPUT_DRIVE_STRENGTH_1_0, 0xff},
716 {PCAL6416A_REG_OUTPUT_DRIVE_STRENGTH_1_1, 0xff},
717 {PCAL6416A_REG_OUTPUT_PORT_CONFIGURATION, 0},
718 };
719
720 for (int i = 0; i < ARRAY_SIZE(reset_state); ++i) {
721 rc = pcal64xxa_i2c_write(i2c, reset_state[i][0], reset_state[i][1]);
722 if (rc != 0) {
723 LOG_ERR("failed to reset register %02x: %d", reset_state[i][0], rc);
724 return -EIO;
725 }
726 }
727
728 return 0;
729 }
730
731 static const struct pcal64xxa_chip_api pcal6416a_chip_api = {
732 .pins_cfg_apply = pcal6416a_pins_cfg_apply,
733 .triggers_apply = pcal6416a_triggers_apply,
734 .inputs_read = pcal6416a_inputs_read,
735 .outputs_write = pcal6416a_outputs_write,
736 .reset_state_apply = pcal6416a_reset_state_apply,
737 };
738 #endif /* DT_HAS_COMPAT_STATUS_OKAY(nxp_pcal6416a) */
739
pcal64xxa_init(const struct device * dev)740 int pcal64xxa_init(const struct device *dev)
741 {
742 const struct pcal64xxa_drv_cfg *drv_cfg = dev->config;
743 struct pcal64xxa_drv_data *drv_data = dev->data;
744 const struct pcal64xxa_pins_cfg initial_pins_cfg = {
745 .configured_as_inputs = 0xFFFF,
746 .outputs_high = 0,
747 .pull_ups_selected = 0,
748 .pulls_enabled = 0,
749 };
750 const struct pcal64xxa_triggers initial_triggers = {
751 .masked = 0xFFFF,
752 };
753 int rc;
754 uint16_t int_sources;
755
756 LOG_DBG("initializing PCAL64XXA");
757
758 if (drv_cfg->ngpios != 8U && drv_cfg->ngpios != 16U) {
759 LOG_ERR("Invalid value ngpios=%u. Expected 8 or 16!", drv_cfg->ngpios);
760 return -EINVAL;
761 }
762
763 /*
764 * executing the is ready check on i2c_bus_dev instead of on i2c.bus
765 * to avoid a const warning
766 */
767 if (!i2c_is_ready_dt(&drv_cfg->i2c)) {
768 LOG_ERR("%s is not ready", drv_cfg->i2c.bus->name);
769 return -ENODEV;
770 }
771
772 /* If the RESET line is available, use it to reset the expander.
773 * Otherwise, write reset values to registers that are not used by
774 * this driver.
775 */
776 if (drv_cfg->gpio_reset.port != NULL) {
777 if (!gpio_is_ready_dt(&drv_cfg->gpio_reset)) {
778 LOG_ERR("reset gpio device is not ready");
779 return -ENODEV;
780 }
781
782 rc = gpio_pin_configure_dt(&drv_cfg->gpio_reset, GPIO_OUTPUT_ACTIVE);
783 if (rc != 0) {
784 LOG_ERR("%s: failed to configure RESET line: %d", dev->name, rc);
785 return -EIO;
786 }
787
788 /* RESET signal needs to be active for a minimum of 30 ns. */
789 k_busy_wait(1);
790
791 rc = gpio_pin_set_dt(&drv_cfg->gpio_reset, 0);
792 if (rc != 0) {
793 LOG_ERR("%s: failed to deactivate RESET line: %d", dev->name, rc);
794 return -EIO;
795 }
796
797 /* Give the expander at least 200 ns to recover after reset. */
798 k_busy_wait(1);
799 } else {
800 rc = drv_cfg->chip_api->reset_state_apply(&drv_cfg->i2c);
801
802 if (rc != 0) {
803 LOG_ERR("failed to apply reset state to device %s", dev->name);
804 return rc;
805 }
806 }
807
808 /* Set initial configuration of the pins. */
809 rc = drv_cfg->chip_api->pins_cfg_apply(&drv_cfg->i2c, &initial_pins_cfg);
810 if (rc != 0) {
811 LOG_ERR("failed to apply pin config for device %s", dev->name);
812 return rc;
813 }
814
815 drv_data->pins_cfg = initial_pins_cfg;
816
817 /* Read initial state of the input port register. */
818 rc = drv_cfg->chip_api->inputs_read(&drv_cfg->i2c, &int_sources,
819 &drv_data->input_port_last);
820 if (rc != 0) {
821 LOG_ERR("failed to read inputs for device %s", dev->name);
822 return rc;
823 }
824
825 /* Set initial state of the interrupt related registers. */
826 rc = drv_cfg->chip_api->triggers_apply(&drv_cfg->i2c, &initial_triggers);
827 if (rc != 0) {
828 LOG_ERR("failed to apply triggers for device %s", dev->name);
829 return rc;
830 }
831
832 drv_data->triggers = initial_triggers;
833
834 /* If the INT line is available, configure the callback for it. */
835 if (drv_cfg->gpio_interrupt.port != NULL) {
836 if (!gpio_is_ready_dt(&drv_cfg->gpio_interrupt)) {
837 LOG_ERR("interrupt gpio device is not ready");
838 return -ENODEV;
839 }
840
841 rc = gpio_pin_configure_dt(&drv_cfg->gpio_interrupt, GPIO_INPUT);
842 if (rc != 0) {
843 LOG_ERR("%s: failed to configure INT line: %d", dev->name, rc);
844 return -EIO;
845 }
846
847 rc = gpio_pin_interrupt_configure_dt(&drv_cfg->gpio_interrupt,
848 GPIO_INT_EDGE_TO_ACTIVE);
849 if (rc != 0) {
850 LOG_ERR("%s: failed to configure INT interrupt: %d", dev->name, rc);
851 return -EIO;
852 }
853
854 gpio_init_callback(&drv_data->int_gpio_cb, pcal64xxa_int_gpio_handler,
855 BIT(drv_cfg->gpio_interrupt.pin));
856 rc = gpio_add_callback(drv_cfg->gpio_interrupt.port, &drv_data->int_gpio_cb);
857 if (rc != 0) {
858 LOG_ERR("%s: failed to add INT callback: %d", dev->name, rc);
859 return -EIO;
860 }
861 }
862
863 /* Device configured, unlock it so that it can be used. */
864 k_sem_give(&drv_data->lock);
865
866 return 0;
867 }
868
869 #define PCAL64XXA_INIT_INT_GPIO_FIELDS(idx) \
870 COND_CODE_1(DT_INST_NODE_HAS_PROP(idx, int_gpios), \
871 (GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(idx), int_gpios, 0)), \
872 ({0}))
873
874 #define PCAL64XXA_INIT_RESET_GPIO_FIELDS(idx) \
875 COND_CODE_1(DT_INST_NODE_HAS_PROP(idx, reset_gpios), \
876 (GPIO_DT_SPEC_GET_BY_IDX(DT_DRV_INST(idx), reset_gpios, 0)), \
877 ({0}))
878
879 #define GPIO_PCAL6408A_INST(idx) \
880 static const struct gpio_driver_api pcal6408a_drv_api##idx = { \
881 .pin_configure = pcal64xxa_pin_configure, \
882 .port_get_raw = pcal64xxa_port_get_raw, \
883 .port_set_masked_raw = pcal64xxa_port_set_masked_raw, \
884 .port_set_bits_raw = pcal64xxa_port_set_bits_raw, \
885 .port_clear_bits_raw = pcal64xxa_port_clear_bits_raw, \
886 .port_toggle_bits = pcal64xxa_port_toggle_bits, \
887 .pin_interrupt_configure = pcal64xxa_pin_interrupt_configure, \
888 .manage_callback = pcal64xxa_manage_callback, \
889 }; \
890 static const struct pcal64xxa_drv_cfg pcal6408a_cfg##idx = { \
891 .common = \
892 { \
893 .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(idx), \
894 }, \
895 .i2c = I2C_DT_SPEC_INST_GET(idx), \
896 .ngpios = DT_INST_PROP(idx, ngpios), \
897 .gpio_interrupt = PCAL64XXA_INIT_INT_GPIO_FIELDS(idx), \
898 .gpio_reset = PCAL64XXA_INIT_RESET_GPIO_FIELDS(idx), \
899 .chip_api = &pcal6408a_chip_api, \
900 }; \
901 static struct pcal64xxa_drv_data pcal6408a_data##idx = { \
902 .lock = Z_SEM_INITIALIZER(pcal6408a_data##idx.lock, 1, 1), \
903 .work = Z_WORK_INITIALIZER(pcal64xxa_work_handler), \
904 .dev = DEVICE_DT_INST_GET(idx), \
905 }; \
906 DEVICE_DT_INST_DEFINE(idx, pcal64xxa_init, NULL, &pcal6408a_data##idx, \
907 &pcal6408a_cfg##idx, POST_KERNEL, \
908 CONFIG_GPIO_PCAL64XXA_INIT_PRIORITY, &pcal6408a_drv_api##idx);
909
910 #define DT_DRV_COMPAT nxp_pcal6408a
911 DT_INST_FOREACH_STATUS_OKAY(GPIO_PCAL6408A_INST)
912
913 #define GPIO_PCAL6416A_INST(idx) \
914 static const struct gpio_driver_api pcal6416a_drv_api##idx = { \
915 .pin_configure = pcal64xxa_pin_configure, \
916 .port_get_raw = pcal64xxa_port_get_raw, \
917 .port_set_masked_raw = pcal64xxa_port_set_masked_raw, \
918 .port_set_bits_raw = pcal64xxa_port_set_bits_raw, \
919 .port_clear_bits_raw = pcal64xxa_port_clear_bits_raw, \
920 .port_toggle_bits = pcal64xxa_port_toggle_bits, \
921 .pin_interrupt_configure = pcal64xxa_pin_interrupt_configure, \
922 .manage_callback = pcal64xxa_manage_callback, \
923 }; \
924 static const struct pcal64xxa_drv_cfg pcal6416a_cfg##idx = { \
925 .common = \
926 { \
927 .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(idx), \
928 }, \
929 .i2c = I2C_DT_SPEC_INST_GET(idx), \
930 .ngpios = DT_INST_PROP(idx, ngpios), \
931 .gpio_interrupt = PCAL64XXA_INIT_INT_GPIO_FIELDS(idx), \
932 .gpio_reset = PCAL64XXA_INIT_RESET_GPIO_FIELDS(idx), \
933 .chip_api = &pcal6416a_chip_api, \
934 }; \
935 static struct pcal64xxa_drv_data pcal6416a_data##idx = { \
936 .lock = Z_SEM_INITIALIZER(pcal6416a_data##idx.lock, 1, 1), \
937 .work = Z_WORK_INITIALIZER(pcal64xxa_work_handler), \
938 .dev = DEVICE_DT_INST_GET(idx), \
939 }; \
940 DEVICE_DT_INST_DEFINE(idx, pcal64xxa_init, NULL, &pcal6416a_data##idx, \
941 &pcal6416a_cfg##idx, POST_KERNEL, \
942 CONFIG_GPIO_PCAL64XXA_INIT_PRIORITY, &pcal6416a_drv_api##idx);
943
944 #undef DT_DRV_COMPAT
945 #define DT_DRV_COMPAT nxp_pcal6416a
946 DT_INST_FOREACH_STATUS_OKAY(GPIO_PCAL6416A_INST)
947