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