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