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