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, ®);
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), ®);
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), ®);
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), ®);
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 ®);
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