1 /*
2  * Copyright (c) 2021 Nuvoton Technology Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT nuvoton_nct38xx_gpio_port
8 
9 #include "gpio_nct38xx.h"
10 #include <zephyr/drivers/gpio/gpio_utils.h>
11 #include <zephyr/drivers/gpio.h>
12 #include <zephyr/drivers/mfd/nct38xx.h>
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_DECLARE(gpio_ntc38xx, CONFIG_GPIO_LOG_LEVEL);
15 
16 /* Driver config */
17 struct gpio_nct38xx_port_config {
18 	/* gpio_driver_config needs to be first */
19 	struct gpio_driver_config common;
20 	/* NCT38XX controller dev */
21 	const struct device *mfd;
22 	/* GPIO port index */
23 	uint8_t gpio_port;
24 	/* GPIO port 0 pinmux mask */
25 	uint8_t pinmux_mask;
26 };
27 
28 /* Driver data */
29 struct gpio_nct38xx_port_data {
30 	/* gpio_driver_data needs to be first */
31 	struct gpio_driver_data common;
32 	/* GPIO callback list */
33 	sys_slist_t cb_list_gpio;
34 	/* lock NCT38xx register access */
35 	struct k_sem *lock;
36 	/* I2C device for the MFD parent */
37 	const struct i2c_dt_spec *i2c_dev;
38 };
39 
40 /* GPIO api functions */
gpio_nct38xx_pin_config(const struct device * dev,gpio_pin_t pin,gpio_flags_t flags)41 static int gpio_nct38xx_pin_config(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags)
42 {
43 	const struct gpio_nct38xx_port_config *const config = dev->config;
44 	struct gpio_nct38xx_port_data *const data = dev->data;
45 	uint32_t mask;
46 	uint8_t new_reg;
47 	int ret;
48 
49 	/* Don't support simultaneous in/out mode */
50 	if (((flags & GPIO_INPUT) != 0) && ((flags & GPIO_OUTPUT) != 0)) {
51 		return -ENOTSUP;
52 	}
53 
54 	/* Don't support "open source" mode */
55 	if (((flags & GPIO_SINGLE_ENDED) != 0) && ((flags & GPIO_LINE_OPEN_DRAIN) == 0)) {
56 		return -ENOTSUP;
57 	}
58 
59 	/* Don't support pull-up/pull-down */
60 	if (((flags & GPIO_PULL_UP) != 0) || ((flags & GPIO_PULL_DOWN) != 0)) {
61 		return -ENOTSUP;
62 	}
63 
64 	k_sem_take(data->lock, K_FOREVER);
65 
66 	/* Pin multiplexing */
67 	if (config->gpio_port == 0) {
68 		/* Set the mux control bit, but ensure the reserved fields
69 		 * are cleared.  Note that pinmux_mask contains the set
70 		 * of non-reserved bits.
71 		 */
72 		new_reg = BIT(pin) & config->pinmux_mask;
73 		mask = BIT(pin) | ~config->pinmux_mask;
74 
75 		ret = i2c_reg_update_byte_dt(data->i2c_dev, NCT38XX_REG_MUX_CONTROL, mask, new_reg);
76 		if (ret < 0) {
77 			goto done;
78 		}
79 	}
80 
81 	/* Configure pin as input. */
82 	if (flags & GPIO_INPUT) {
83 		/* Clear the direction bit to set as an input */
84 		new_reg = 0;
85 		mask = BIT(pin);
86 		ret = i2c_reg_update_byte_dt(data->i2c_dev, NCT38XX_REG_GPIO_DIR(config->gpio_port),
87 					     mask, new_reg);
88 
89 		goto done;
90 	}
91 
92 	/* Select open drain 0:push-pull 1:open-drain */
93 	mask = BIT(pin);
94 	if (flags & GPIO_OPEN_DRAIN) {
95 		new_reg = mask;
96 	} else {
97 		new_reg = 0;
98 	}
99 	ret = i2c_reg_update_byte_dt(data->i2c_dev, NCT38XX_REG_GPIO_OD_SEL(config->gpio_port),
100 				     mask, new_reg);
101 	if (ret < 0) {
102 		goto done;
103 	}
104 
105 	/* Set level 0:low 1:high */
106 	if (flags & GPIO_OUTPUT_INIT_HIGH) {
107 		new_reg = mask;
108 	} else if (flags & GPIO_OUTPUT_INIT_LOW) {
109 		new_reg = 0;
110 	}
111 	ret = i2c_reg_update_byte_dt(data->i2c_dev, NCT38XX_REG_GPIO_DATA_OUT(config->gpio_port),
112 				     mask, new_reg);
113 	if (ret < 0) {
114 		goto done;
115 	}
116 
117 	/* Configure pin as output, if requested 0:input 1:output */
118 	if (flags & GPIO_OUTPUT) {
119 		new_reg = BIT(pin);
120 		ret = i2c_reg_update_byte_dt(data->i2c_dev, NCT38XX_REG_GPIO_DIR(config->gpio_port),
121 					     mask, new_reg);
122 	}
123 
124 done:
125 	k_sem_give(data->lock);
126 	return ret;
127 }
128 
129 #ifdef CONFIG_GPIO_GET_CONFIG
gpio_nct38xx_pin_get_config(const struct device * dev,gpio_pin_t pin,gpio_flags_t * flags)130 int gpio_nct38xx_pin_get_config(const struct device *dev, gpio_pin_t pin, gpio_flags_t *flags)
131 {
132 	const struct gpio_nct38xx_port_config *const config = dev->config;
133 	struct gpio_nct38xx_port_data *const data = dev->data;
134 	uint32_t mask = BIT(pin);
135 	uint8_t reg;
136 	int ret;
137 
138 	k_sem_take(data->lock, K_FOREVER);
139 
140 	if (config->gpio_port == 0) {
141 		if (mask & (~config->common.port_pin_mask)) {
142 			ret = -ENOTSUP;
143 			goto done;
144 		}
145 
146 		ret = i2c_reg_read_byte_dt(data->i2c_dev, NCT38XX_REG_MUX_CONTROL, &reg);
147 		if (ret < 0) {
148 			goto done;
149 		}
150 
151 		if ((mask & config->pinmux_mask) && (mask & (~reg))) {
152 			*flags = GPIO_DISCONNECTED;
153 			goto done;
154 		}
155 	}
156 
157 	ret = i2c_reg_read_byte_dt(data->i2c_dev, NCT38XX_REG_GPIO_DIR(config->gpio_port), &reg);
158 	if (ret < 0) {
159 		goto done;
160 	}
161 
162 	if (reg & mask) {
163 		/* Output */
164 		*flags = GPIO_OUTPUT;
165 
166 		/* 0 - push-pull, 1 - open-drain */
167 		ret = i2c_reg_read_byte_dt(data->i2c_dev,
168 					   NCT38XX_REG_GPIO_OD_SEL(config->gpio_port), &reg);
169 		if (ret < 0) {
170 			goto done;
171 		}
172 
173 		if (mask & reg) {
174 			*flags |= GPIO_OPEN_DRAIN;
175 		}
176 
177 		/* Output value */
178 		ret = i2c_reg_read_byte_dt(data->i2c_dev,
179 					   NCT38XX_REG_GPIO_DATA_OUT(config->gpio_port), &reg);
180 		if (ret < 0) {
181 			goto done;
182 		}
183 
184 		if (mask & reg) {
185 			*flags |= GPIO_OUTPUT_HIGH;
186 		} else {
187 			*flags |= GPIO_OUTPUT_LOW;
188 		}
189 	} else {
190 		/* Input */
191 		*flags = GPIO_INPUT;
192 	}
193 
194 done:
195 	k_sem_give(data->lock);
196 	return ret;
197 }
198 #endif /* CONFIG_GPIO_GET_CONFIG */
199 
gpio_nct38xx_port_get_raw(const struct device * dev,gpio_port_value_t * value)200 static int gpio_nct38xx_port_get_raw(const struct device *dev, gpio_port_value_t *value)
201 {
202 	int ret;
203 	const struct gpio_nct38xx_port_config *const config = dev->config;
204 	struct gpio_nct38xx_port_data *const data = dev->data;
205 
206 	k_sem_take(data->lock, K_FOREVER);
207 
208 	ret = i2c_reg_read_byte_dt(data->i2c_dev, NCT38XX_REG_GPIO_DATA_IN(config->gpio_port),
209 				   (uint8_t *)value);
210 
211 	k_sem_give(data->lock);
212 	return ret;
213 }
214 
gpio_nct38xx_port_set_masked_raw(const struct device * dev,gpio_port_pins_t mask,gpio_port_value_t value)215 static int gpio_nct38xx_port_set_masked_raw(const struct device *dev, gpio_port_pins_t mask,
216 					    gpio_port_value_t value)
217 {
218 	const struct gpio_nct38xx_port_config *const config = dev->config;
219 	struct gpio_nct38xx_port_data *const data = dev->data;
220 	int ret;
221 
222 	k_sem_take(data->lock, K_FOREVER);
223 
224 	ret = i2c_reg_update_byte_dt(data->i2c_dev, NCT38XX_REG_GPIO_DATA_OUT(config->gpio_port),
225 				     mask, value);
226 
227 	k_sem_give(data->lock);
228 
229 	return ret;
230 }
231 
gpio_nct38xx_port_set_bits_raw(const struct device * dev,gpio_port_pins_t mask)232 static int gpio_nct38xx_port_set_bits_raw(const struct device *dev, gpio_port_pins_t mask)
233 {
234 	const struct gpio_nct38xx_port_config *const config = dev->config;
235 	struct gpio_nct38xx_port_data *const data = dev->data;
236 	int ret;
237 
238 	k_sem_take(data->lock, K_FOREVER);
239 
240 	ret = i2c_reg_update_byte_dt(data->i2c_dev, NCT38XX_REG_GPIO_DATA_OUT(config->gpio_port),
241 				     mask, mask);
242 
243 	k_sem_give(data->lock);
244 
245 	return ret;
246 }
247 
gpio_nct38xx_port_clear_bits_raw(const struct device * dev,gpio_port_pins_t mask)248 static int gpio_nct38xx_port_clear_bits_raw(const struct device *dev, gpio_port_pins_t mask)
249 {
250 	const struct gpio_nct38xx_port_config *const config = dev->config;
251 	struct gpio_nct38xx_port_data *const data = dev->data;
252 	int ret;
253 
254 	k_sem_take(data->lock, K_FOREVER);
255 
256 	ret = i2c_reg_update_byte_dt(data->i2c_dev, NCT38XX_REG_GPIO_DATA_OUT(config->gpio_port),
257 				     mask, 0);
258 
259 	k_sem_give(data->lock);
260 
261 	return ret;
262 }
263 
gpio_nct38xx_port_toggle_bits(const struct device * dev,gpio_port_pins_t mask)264 static int gpio_nct38xx_port_toggle_bits(const struct device *dev, gpio_port_pins_t mask)
265 {
266 	const struct gpio_nct38xx_port_config *const config = dev->config;
267 	struct gpio_nct38xx_port_data *const data = dev->data;
268 	uint8_t reg, new_reg;
269 	int ret;
270 
271 	k_sem_take(data->lock, K_FOREVER);
272 
273 	ret = i2c_reg_read_byte_dt(data->i2c_dev, NCT38XX_REG_GPIO_DATA_OUT(config->gpio_port),
274 				   &reg);
275 	if (ret < 0) {
276 		goto done;
277 	}
278 	new_reg = reg ^ mask;
279 	if (new_reg != reg) {
280 		ret = i2c_reg_write_byte_dt(data->i2c_dev,
281 					    NCT38XX_REG_GPIO_DATA_OUT(config->gpio_port), new_reg);
282 	}
283 
284 done:
285 	k_sem_give(data->lock);
286 
287 	return ret;
288 }
289 
gpio_nct38xx_pin_interrupt_configure(const struct device * dev,gpio_pin_t pin,enum gpio_int_mode mode,enum gpio_int_trig trig)290 static int gpio_nct38xx_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin,
291 						enum gpio_int_mode mode, enum gpio_int_trig trig)
292 {
293 	const struct gpio_nct38xx_port_config *const config = dev->config;
294 	struct gpio_nct38xx_port_data *const data = dev->data;
295 	uint8_t new_reg, new_rise, new_fall;
296 	int ret;
297 	uint32_t mask = BIT(pin);
298 
299 	k_sem_take(data->lock, K_FOREVER);
300 
301 	/* Disable irq before configuring them */
302 	new_reg = 0;
303 	ret = i2c_reg_update_byte_dt(data->i2c_dev, NCT38XX_REG_GPIO_ALERT_MASK(config->gpio_port),
304 				     mask, new_reg);
305 
306 	/* Configure and enable interrupt? */
307 	if (mode == GPIO_INT_MODE_DISABLED) {
308 		goto done;
309 	}
310 
311 	/* set edge register */
312 	if (mode == GPIO_INT_MODE_EDGE) {
313 		if (trig == GPIO_INT_TRIG_LOW) {
314 			new_rise = 0;
315 			new_fall = mask;
316 		} else if (trig == GPIO_INT_TRIG_HIGH) {
317 			new_rise = mask;
318 			new_fall = 0;
319 		} else if (trig == GPIO_INT_TRIG_BOTH) {
320 			new_rise = mask;
321 			new_fall = mask;
322 		} else {
323 			LOG_ERR("Invalid interrupt trigger type %d", trig);
324 			return -EINVAL;
325 		}
326 	} else {
327 		/* level mode */
328 		new_rise = 0;
329 		new_fall = 0;
330 	}
331 
332 	ret = i2c_reg_update_byte_dt(data->i2c_dev, NCT38XX_REG_GPIO_ALERT_RISE(config->gpio_port),
333 				     mask, new_rise);
334 	if (ret < 0) {
335 		goto done;
336 	}
337 
338 	ret = i2c_reg_update_byte_dt(data->i2c_dev, NCT38XX_REG_GPIO_ALERT_FALL(config->gpio_port),
339 				     mask, new_fall);
340 	if (ret < 0) {
341 		goto done;
342 	}
343 
344 	if (mode == GPIO_INT_MODE_LEVEL) {
345 		/* set active high/low */
346 		if (trig == GPIO_INT_TRIG_LOW) {
347 			new_reg = 0;
348 		} else if (trig == GPIO_INT_TRIG_HIGH) {
349 			new_reg = mask;
350 		} else {
351 			LOG_ERR("Invalid interrupt trigger type %d", trig);
352 			ret = -EINVAL;
353 			goto done;
354 		}
355 		ret = i2c_reg_update_byte_dt(data->i2c_dev,
356 					     NCT38XX_REG_GPIO_ALERT_LEVEL(config->gpio_port), mask,
357 					     new_reg);
358 
359 		if (ret < 0) {
360 			goto done;
361 		}
362 	}
363 
364 	/* Clear pending bit */
365 	ret = i2c_reg_write_byte_dt(data->i2c_dev, NCT38XX_REG_GPIO_ALERT_STAT(config->gpio_port),
366 				    mask);
367 	if (ret < 0) {
368 		goto done;
369 	}
370 
371 	/* Enable it after configuration is completed */
372 	new_reg = mask;
373 	ret = i2c_reg_update_byte_dt(data->i2c_dev, NCT38XX_REG_GPIO_ALERT_MASK(config->gpio_port),
374 				     mask, new_reg);
375 
376 done:
377 	k_sem_give(data->lock);
378 
379 	return ret;
380 }
381 
gpio_nct38xx_manage_callback(const struct device * dev,struct gpio_callback * callback,bool set)382 static int gpio_nct38xx_manage_callback(const struct device *dev, struct gpio_callback *callback,
383 					bool set)
384 {
385 	struct gpio_nct38xx_port_data *const data = dev->data;
386 
387 	return gpio_manage_callback(&data->cb_list_gpio, callback, set);
388 }
389 
390 #ifdef CONFIG_GPIO_GET_DIRECTION
gpio_nct38xx_port_get_direction(const struct device * dev,gpio_port_pins_t mask,gpio_port_pins_t * inputs,gpio_port_pins_t * outputs)391 static int gpio_nct38xx_port_get_direction(const struct device *dev, gpio_port_pins_t mask,
392 					   gpio_port_pins_t *inputs, gpio_port_pins_t *outputs)
393 {
394 	const struct gpio_nct38xx_port_config *const config = dev->config;
395 	struct gpio_nct38xx_port_data *const data = dev->data;
396 	uint8_t dir_reg;
397 	int ret;
398 
399 	k_sem_take(data->lock, K_FOREVER);
400 
401 	if (config->gpio_port == 0) {
402 		uint8_t enabled_gpios;
403 		/* Remove the disabled GPIOs from the mask */
404 		ret = i2c_reg_read_byte_dt(data->i2c_dev, NCT38XX_REG_MUX_CONTROL, &enabled_gpios);
405 		mask &= (enabled_gpios & config->common.port_pin_mask);
406 
407 		if (ret < 0) {
408 			goto done;
409 		}
410 	}
411 
412 	/* Read direction register, 0 - input, 1 - output */
413 	ret = i2c_reg_read_byte_dt(data->i2c_dev, NCT38XX_REG_GPIO_DIR(config->gpio_port),
414 				   &dir_reg);
415 	if (ret < 0) {
416 		goto done;
417 	}
418 
419 	if (inputs) {
420 		*inputs = mask & (~dir_reg);
421 	}
422 
423 	if (outputs) {
424 		*outputs = mask & dir_reg;
425 	}
426 
427 done:
428 	k_sem_give(data->lock);
429 	return ret;
430 }
431 #endif /* CONFIG_GPIO_GET_DIRECTION */
432 
gpio_nct38xx_dispatch_port_isr(const struct device * dev)433 int gpio_nct38xx_dispatch_port_isr(const struct device *dev)
434 {
435 	const struct gpio_nct38xx_port_config *const config = dev->config;
436 	struct gpio_nct38xx_port_data *const data = dev->data;
437 	uint8_t alert_pins, mask;
438 	int ret;
439 
440 	do {
441 		k_sem_take(data->lock, K_FOREVER);
442 		ret = i2c_reg_read_byte_dt(
443 			data->i2c_dev, NCT38XX_REG_GPIO_ALERT_STAT(config->gpio_port), &alert_pins);
444 		if (ret < 0) {
445 			k_sem_give(data->lock);
446 			return ret;
447 		}
448 
449 		ret = i2c_reg_read_byte_dt(data->i2c_dev,
450 					   NCT38XX_REG_GPIO_ALERT_MASK(config->gpio_port), &mask);
451 		if (ret < 0) {
452 			k_sem_give(data->lock);
453 			return ret;
454 		}
455 		alert_pins &= mask;
456 		if (alert_pins) {
457 			ret = i2c_reg_write_byte_dt(data->i2c_dev,
458 						    NCT38XX_REG_GPIO_ALERT_STAT(config->gpio_port),
459 						    alert_pins);
460 			if (ret < 0) {
461 				k_sem_give(data->lock);
462 				return ret;
463 			}
464 		}
465 		k_sem_give(data->lock);
466 
467 		gpio_fire_callbacks(&data->cb_list_gpio, dev, alert_pins);
468 
469 		/*
470 		 * Vendor defined alert is generated if at least one STATn bit
471 		 * changes from 0 to 1. We should guarantee the STATn bit is
472 		 * clear to 0 before leaving isr.
473 		 */
474 	} while (alert_pins);
475 
476 	return 0;
477 }
478 
479 static const struct gpio_driver_api gpio_nct38xx_driver = {
480 	.pin_configure = gpio_nct38xx_pin_config,
481 #ifdef CONFIG_GPIO_GET_CONFIG
482 	.pin_get_config = gpio_nct38xx_pin_get_config,
483 #endif /* CONFIG_GPIO_GET_CONFIG */
484 	.port_get_raw = gpio_nct38xx_port_get_raw,
485 	.port_set_masked_raw = gpio_nct38xx_port_set_masked_raw,
486 	.port_set_bits_raw = gpio_nct38xx_port_set_bits_raw,
487 	.port_clear_bits_raw = gpio_nct38xx_port_clear_bits_raw,
488 	.port_toggle_bits = gpio_nct38xx_port_toggle_bits,
489 	.pin_interrupt_configure = gpio_nct38xx_pin_interrupt_configure,
490 	.manage_callback = gpio_nct38xx_manage_callback,
491 #ifdef CONFIG_GPIO_GET_DIRECTION
492 	.port_get_direction = gpio_nct38xx_port_get_direction,
493 #endif /* CONFIG_GPIO_GET_DIRECTION */
494 };
495 
gpio_nct38xx_port_init(const struct device * dev)496 static int gpio_nct38xx_port_init(const struct device *dev)
497 {
498 	const struct gpio_nct38xx_port_config *const config = dev->config;
499 	struct gpio_nct38xx_port_data *const data = dev->data;
500 
501 	if (!device_is_ready(config->mfd)) {
502 		LOG_ERR("%s is not ready", config->mfd->name);
503 		return -ENODEV;
504 	}
505 
506 	data->lock = mfd_nct38xx_get_lock_reference(config->mfd);
507 	data->i2c_dev = mfd_nct38xx_get_i2c_dt_spec(config->mfd);
508 
509 	return 0;
510 }
511 
512 /* NCT38XX GPIO port driver must be initialized after NCT38XX GPIO driver */
513 BUILD_ASSERT(CONFIG_GPIO_NCT38XX_PORT_INIT_PRIORITY > CONFIG_GPIO_NCT38XX_INIT_PRIORITY);
514 
515 #define GPIO_NCT38XX_PORT_DEVICE_INSTANCE(inst)                                                    \
516 	static const struct gpio_nct38xx_port_config gpio_nct38xx_port_cfg_##inst = {              \
517 		.common = {.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(inst) &                \
518 					    DT_INST_PROP(inst, pin_mask)},                         \
519 		.mfd = DEVICE_DT_GET(DT_INST_GPARENT(inst)),                                       \
520 		.gpio_port = DT_INST_REG_ADDR(inst),                                               \
521 		.pinmux_mask = COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, pinmux_mask),               \
522 					   (DT_INST_PROP(inst, pinmux_mask)), (0)),                \
523 	};                                                                                         \
524 	BUILD_ASSERT(                                                                              \
525 		!(DT_INST_REG_ADDR(inst) == 0 && !(DT_INST_NODE_HAS_PROP(inst, pinmux_mask))),     \
526 		"Port 0 should assign pinmux_mask property.");                                     \
527 	static struct gpio_nct38xx_port_data gpio_nct38xx_port_data_##inst;                        \
528 	DEVICE_DT_INST_DEFINE(inst, gpio_nct38xx_port_init, NULL, &gpio_nct38xx_port_data_##inst,  \
529 			      &gpio_nct38xx_port_cfg_##inst, POST_KERNEL,                          \
530 			      CONFIG_GPIO_NCT38XX_PORT_INIT_PRIORITY, &gpio_nct38xx_driver);
531 
532 DT_INST_FOREACH_STATUS_OKAY(GPIO_NCT38XX_PORT_DEVICE_INSTANCE)
533