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