Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

gpio: Add Nuvoton NCT6694 GPIO support

This driver supports GPIO and IRQ functionality for NCT6694 MFD
device based on USB interface.

Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Acked-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Signed-off-by: Ming Yu <a0282524688@gmail.com>
Link: https://lore.kernel.org/r/20250912091952.1169369-3-a0282524688@gmail.com
Signed-off-by: Lee Jones <lee@kernel.org>

authored by

Ming Yu and committed by
Lee Jones
611a995e 51dad33e

+513
+1
MAINTAINERS
··· 18085 18085 NUVOTON NCT6694 MFD DRIVER 18086 18086 M: Ming Yu <tmyu0@nuvoton.com> 18087 18087 S: Supported 18088 + F: drivers/gpio/gpio-nct6694.c 18088 18089 F: drivers/mfd/nct6694.c 18089 18090 F: include/linux/mfd/nct6694.h 18090 18091
+12
drivers/gpio/Kconfig
··· 1522 1522 This driver can also be built as a module. If so, the module will be 1523 1523 called gpio-max77759. 1524 1524 1525 + config GPIO_NCT6694 1526 + tristate "Nuvoton NCT6694 GPIO controller support" 1527 + depends on MFD_NCT6694 1528 + select GENERIC_IRQ_CHIP 1529 + select GPIOLIB_IRQCHIP 1530 + help 1531 + This driver supports 8 GPIO pins per bank that can all be interrupt 1532 + sources. 1533 + 1534 + This driver can also be built as a module. If so, the module will be 1535 + called gpio-nct6694. 1536 + 1525 1537 config GPIO_PALMAS 1526 1538 tristate "TI PALMAS series PMICs GPIO" 1527 1539 depends on MFD_PALMAS
+1
drivers/gpio/Makefile
··· 128 128 obj-$(CONFIG_GPIO_MVEBU) += gpio-mvebu.o 129 129 obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o 130 130 obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o 131 + obj-$(CONFIG_GPIO_NCT6694) += gpio-nct6694.o 131 132 obj-$(CONFIG_GPIO_NOMADIK) += gpio-nomadik.o 132 133 obj-$(CONFIG_GPIO_NPCM_SGPIO) += gpio-npcm-sgpio.o 133 134 obj-$(CONFIG_GPIO_OCTEON) += gpio-octeon.o
+499
drivers/gpio/gpio-nct6694.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Nuvoton NCT6694 GPIO controller driver based on USB interface. 4 + * 5 + * Copyright (C) 2025 Nuvoton Technology Corp. 6 + */ 7 + 8 + #include <linux/bits.h> 9 + #include <linux/gpio/driver.h> 10 + #include <linux/idr.h> 11 + #include <linux/interrupt.h> 12 + #include <linux/mfd/nct6694.h> 13 + #include <linux/module.h> 14 + #include <linux/platform_device.h> 15 + 16 + /* 17 + * USB command module type for NCT6694 GPIO controller. 18 + * This defines the module type used for communication with the NCT6694 19 + * GPIO controller over the USB interface. 20 + */ 21 + #define NCT6694_GPIO_MOD 0xFF 22 + 23 + #define NCT6694_GPIO_VER 0x90 24 + #define NCT6694_GPIO_VALID 0x110 25 + #define NCT6694_GPI_DATA 0x120 26 + #define NCT6694_GPO_DIR 0x170 27 + #define NCT6694_GPO_TYPE 0x180 28 + #define NCT6694_GPO_DATA 0x190 29 + 30 + #define NCT6694_GPI_STS 0x130 31 + #define NCT6694_GPI_CLR 0x140 32 + #define NCT6694_GPI_FALLING 0x150 33 + #define NCT6694_GPI_RISING 0x160 34 + 35 + #define NCT6694_NR_GPIO 8 36 + 37 + struct nct6694_gpio_data { 38 + struct nct6694 *nct6694; 39 + struct gpio_chip gpio; 40 + struct mutex lock; 41 + /* Protect irq operation */ 42 + struct mutex irq_lock; 43 + 44 + unsigned char reg_val; 45 + unsigned char irq_trig_falling; 46 + unsigned char irq_trig_rising; 47 + 48 + /* Current gpio group */ 49 + unsigned char group; 50 + int irq; 51 + }; 52 + 53 + static int nct6694_get_direction(struct gpio_chip *gpio, unsigned int offset) 54 + { 55 + struct nct6694_gpio_data *data = gpiochip_get_data(gpio); 56 + const struct nct6694_cmd_header cmd_hd = { 57 + .mod = NCT6694_GPIO_MOD, 58 + .offset = cpu_to_le16(NCT6694_GPO_DIR + data->group), 59 + .len = cpu_to_le16(sizeof(data->reg_val)) 60 + }; 61 + int ret; 62 + 63 + guard(mutex)(&data->lock); 64 + 65 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val); 66 + if (ret < 0) 67 + return ret; 68 + 69 + return !(BIT(offset) & data->reg_val); 70 + } 71 + 72 + static int nct6694_direction_input(struct gpio_chip *gpio, unsigned int offset) 73 + { 74 + struct nct6694_gpio_data *data = gpiochip_get_data(gpio); 75 + const struct nct6694_cmd_header cmd_hd = { 76 + .mod = NCT6694_GPIO_MOD, 77 + .offset = cpu_to_le16(NCT6694_GPO_DIR + data->group), 78 + .len = cpu_to_le16(sizeof(data->reg_val)) 79 + }; 80 + int ret; 81 + 82 + guard(mutex)(&data->lock); 83 + 84 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val); 85 + if (ret < 0) 86 + return ret; 87 + 88 + data->reg_val &= ~BIT(offset); 89 + 90 + return nct6694_write_msg(data->nct6694, &cmd_hd, &data->reg_val); 91 + } 92 + 93 + static int nct6694_direction_output(struct gpio_chip *gpio, 94 + unsigned int offset, int val) 95 + { 96 + struct nct6694_gpio_data *data = gpiochip_get_data(gpio); 97 + struct nct6694_cmd_header cmd_hd = { 98 + .mod = NCT6694_GPIO_MOD, 99 + .offset = cpu_to_le16(NCT6694_GPO_DIR + data->group), 100 + .len = cpu_to_le16(sizeof(data->reg_val)) 101 + }; 102 + int ret; 103 + 104 + guard(mutex)(&data->lock); 105 + 106 + /* Set direction to output */ 107 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val); 108 + if (ret < 0) 109 + return ret; 110 + 111 + data->reg_val |= BIT(offset); 112 + ret = nct6694_write_msg(data->nct6694, &cmd_hd, &data->reg_val); 113 + if (ret < 0) 114 + return ret; 115 + 116 + /* Then set output level */ 117 + cmd_hd.offset = cpu_to_le16(NCT6694_GPO_DATA + data->group); 118 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val); 119 + if (ret < 0) 120 + return ret; 121 + 122 + if (val) 123 + data->reg_val |= BIT(offset); 124 + else 125 + data->reg_val &= ~BIT(offset); 126 + 127 + return nct6694_write_msg(data->nct6694, &cmd_hd, &data->reg_val); 128 + } 129 + 130 + static int nct6694_get_value(struct gpio_chip *gpio, unsigned int offset) 131 + { 132 + struct nct6694_gpio_data *data = gpiochip_get_data(gpio); 133 + struct nct6694_cmd_header cmd_hd = { 134 + .mod = NCT6694_GPIO_MOD, 135 + .offset = cpu_to_le16(NCT6694_GPO_DIR + data->group), 136 + .len = cpu_to_le16(sizeof(data->reg_val)) 137 + }; 138 + int ret; 139 + 140 + guard(mutex)(&data->lock); 141 + 142 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val); 143 + if (ret < 0) 144 + return ret; 145 + 146 + if (BIT(offset) & data->reg_val) { 147 + cmd_hd.offset = cpu_to_le16(NCT6694_GPO_DATA + data->group); 148 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val); 149 + if (ret < 0) 150 + return ret; 151 + 152 + return !!(BIT(offset) & data->reg_val); 153 + } 154 + 155 + cmd_hd.offset = cpu_to_le16(NCT6694_GPI_DATA + data->group); 156 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val); 157 + if (ret < 0) 158 + return ret; 159 + 160 + return !!(BIT(offset) & data->reg_val); 161 + } 162 + 163 + static int nct6694_set_value(struct gpio_chip *gpio, unsigned int offset, 164 + int val) 165 + { 166 + struct nct6694_gpio_data *data = gpiochip_get_data(gpio); 167 + const struct nct6694_cmd_header cmd_hd = { 168 + .mod = NCT6694_GPIO_MOD, 169 + .offset = cpu_to_le16(NCT6694_GPO_DATA + data->group), 170 + .len = cpu_to_le16(sizeof(data->reg_val)) 171 + }; 172 + int ret; 173 + 174 + guard(mutex)(&data->lock); 175 + 176 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val); 177 + if (ret < 0) 178 + return ret; 179 + 180 + if (val) 181 + data->reg_val |= BIT(offset); 182 + else 183 + data->reg_val &= ~BIT(offset); 184 + 185 + return nct6694_write_msg(data->nct6694, &cmd_hd, &data->reg_val); 186 + } 187 + 188 + static int nct6694_set_config(struct gpio_chip *gpio, unsigned int offset, 189 + unsigned long config) 190 + { 191 + struct nct6694_gpio_data *data = gpiochip_get_data(gpio); 192 + const struct nct6694_cmd_header cmd_hd = { 193 + .mod = NCT6694_GPIO_MOD, 194 + .offset = cpu_to_le16(NCT6694_GPO_TYPE + data->group), 195 + .len = cpu_to_le16(sizeof(data->reg_val)) 196 + }; 197 + int ret; 198 + 199 + guard(mutex)(&data->lock); 200 + 201 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val); 202 + if (ret < 0) 203 + return ret; 204 + 205 + switch (pinconf_to_config_param(config)) { 206 + case PIN_CONFIG_DRIVE_OPEN_DRAIN: 207 + data->reg_val |= BIT(offset); 208 + break; 209 + case PIN_CONFIG_DRIVE_PUSH_PULL: 210 + data->reg_val &= ~BIT(offset); 211 + break; 212 + default: 213 + return -ENOTSUPP; 214 + } 215 + 216 + return nct6694_write_msg(data->nct6694, &cmd_hd, &data->reg_val); 217 + } 218 + 219 + static int nct6694_init_valid_mask(struct gpio_chip *gpio, 220 + unsigned long *valid_mask, 221 + unsigned int ngpios) 222 + { 223 + struct nct6694_gpio_data *data = gpiochip_get_data(gpio); 224 + const struct nct6694_cmd_header cmd_hd = { 225 + .mod = NCT6694_GPIO_MOD, 226 + .offset = cpu_to_le16(NCT6694_GPIO_VALID + data->group), 227 + .len = cpu_to_le16(sizeof(data->reg_val)) 228 + }; 229 + int ret; 230 + 231 + guard(mutex)(&data->lock); 232 + 233 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val); 234 + if (ret < 0) 235 + return ret; 236 + 237 + *valid_mask = data->reg_val; 238 + 239 + return ret; 240 + } 241 + 242 + static irqreturn_t nct6694_irq_handler(int irq, void *priv) 243 + { 244 + struct nct6694_gpio_data *data = priv; 245 + struct nct6694_cmd_header cmd_hd = { 246 + .mod = NCT6694_GPIO_MOD, 247 + .offset = cpu_to_le16(NCT6694_GPI_STS + data->group), 248 + .len = cpu_to_le16(sizeof(data->reg_val)) 249 + }; 250 + unsigned char status; 251 + int ret; 252 + 253 + guard(mutex)(&data->lock); 254 + 255 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val); 256 + if (ret) 257 + return IRQ_NONE; 258 + 259 + status = data->reg_val; 260 + 261 + while (status) { 262 + int bit = __ffs(status); 263 + 264 + data->reg_val = BIT(bit); 265 + handle_nested_irq(irq_find_mapping(data->gpio.irq.domain, bit)); 266 + status &= ~BIT(bit); 267 + cmd_hd.offset = cpu_to_le16(NCT6694_GPI_CLR + data->group); 268 + nct6694_write_msg(data->nct6694, &cmd_hd, &data->reg_val); 269 + } 270 + 271 + return IRQ_HANDLED; 272 + } 273 + 274 + static int nct6694_get_irq_trig(struct nct6694_gpio_data *data) 275 + { 276 + struct nct6694_cmd_header cmd_hd = { 277 + .mod = NCT6694_GPIO_MOD, 278 + .offset = cpu_to_le16(NCT6694_GPI_FALLING + data->group), 279 + .len = cpu_to_le16(sizeof(data->reg_val)) 280 + }; 281 + int ret; 282 + 283 + guard(mutex)(&data->lock); 284 + 285 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->irq_trig_falling); 286 + if (ret) 287 + return ret; 288 + 289 + cmd_hd.offset = cpu_to_le16(NCT6694_GPI_RISING + data->group); 290 + return nct6694_read_msg(data->nct6694, &cmd_hd, &data->irq_trig_rising); 291 + } 292 + 293 + static void nct6694_irq_mask(struct irq_data *d) 294 + { 295 + struct gpio_chip *gpio = irq_data_get_irq_chip_data(d); 296 + irq_hw_number_t hwirq = irqd_to_hwirq(d); 297 + 298 + gpiochip_disable_irq(gpio, hwirq); 299 + } 300 + 301 + static void nct6694_irq_unmask(struct irq_data *d) 302 + { 303 + struct gpio_chip *gpio = irq_data_get_irq_chip_data(d); 304 + irq_hw_number_t hwirq = irqd_to_hwirq(d); 305 + 306 + gpiochip_enable_irq(gpio, hwirq); 307 + } 308 + 309 + static int nct6694_irq_set_type(struct irq_data *d, unsigned int type) 310 + { 311 + struct gpio_chip *gpio = irq_data_get_irq_chip_data(d); 312 + struct nct6694_gpio_data *data = gpiochip_get_data(gpio); 313 + irq_hw_number_t hwirq = irqd_to_hwirq(d); 314 + 315 + guard(mutex)(&data->lock); 316 + 317 + switch (type) { 318 + case IRQ_TYPE_EDGE_RISING: 319 + data->irq_trig_rising |= BIT(hwirq); 320 + break; 321 + 322 + case IRQ_TYPE_EDGE_FALLING: 323 + data->irq_trig_falling |= BIT(hwirq); 324 + break; 325 + 326 + case IRQ_TYPE_EDGE_BOTH: 327 + data->irq_trig_rising |= BIT(hwirq); 328 + data->irq_trig_falling |= BIT(hwirq); 329 + break; 330 + 331 + default: 332 + return -ENOTSUPP; 333 + } 334 + 335 + return 0; 336 + } 337 + 338 + static void nct6694_irq_bus_lock(struct irq_data *d) 339 + { 340 + struct gpio_chip *gpio = irq_data_get_irq_chip_data(d); 341 + struct nct6694_gpio_data *data = gpiochip_get_data(gpio); 342 + 343 + mutex_lock(&data->irq_lock); 344 + } 345 + 346 + static void nct6694_irq_bus_sync_unlock(struct irq_data *d) 347 + { 348 + struct gpio_chip *gpio = irq_data_get_irq_chip_data(d); 349 + struct nct6694_gpio_data *data = gpiochip_get_data(gpio); 350 + struct nct6694_cmd_header cmd_hd = { 351 + .mod = NCT6694_GPIO_MOD, 352 + .offset = cpu_to_le16(NCT6694_GPI_FALLING + data->group), 353 + .len = cpu_to_le16(sizeof(data->reg_val)) 354 + }; 355 + 356 + scoped_guard(mutex, &data->lock) { 357 + nct6694_write_msg(data->nct6694, &cmd_hd, &data->irq_trig_falling); 358 + 359 + cmd_hd.offset = cpu_to_le16(NCT6694_GPI_RISING + data->group); 360 + nct6694_write_msg(data->nct6694, &cmd_hd, &data->irq_trig_rising); 361 + } 362 + 363 + mutex_unlock(&data->irq_lock); 364 + } 365 + 366 + static const struct irq_chip nct6694_irq_chip = { 367 + .name = "gpio-nct6694", 368 + .irq_mask = nct6694_irq_mask, 369 + .irq_unmask = nct6694_irq_unmask, 370 + .irq_set_type = nct6694_irq_set_type, 371 + .irq_bus_lock = nct6694_irq_bus_lock, 372 + .irq_bus_sync_unlock = nct6694_irq_bus_sync_unlock, 373 + .flags = IRQCHIP_IMMUTABLE, 374 + GPIOCHIP_IRQ_RESOURCE_HELPERS, 375 + }; 376 + 377 + static void nct6694_irq_dispose_mapping(void *d) 378 + { 379 + struct nct6694_gpio_data *data = d; 380 + 381 + irq_dispose_mapping(data->irq); 382 + } 383 + 384 + static void nct6694_gpio_ida_free(void *d) 385 + { 386 + struct nct6694_gpio_data *data = d; 387 + struct nct6694 *nct6694 = data->nct6694; 388 + 389 + ida_free(&nct6694->gpio_ida, data->group); 390 + } 391 + 392 + static int nct6694_gpio_probe(struct platform_device *pdev) 393 + { 394 + struct device *dev = &pdev->dev; 395 + struct nct6694 *nct6694 = dev_get_drvdata(dev->parent); 396 + struct nct6694_gpio_data *data; 397 + struct gpio_irq_chip *girq; 398 + int ret, i; 399 + char **names; 400 + 401 + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 402 + if (!data) 403 + return -ENOMEM; 404 + 405 + data->nct6694 = nct6694; 406 + 407 + ret = ida_alloc(&nct6694->gpio_ida, GFP_KERNEL); 408 + if (ret < 0) 409 + return ret; 410 + data->group = ret; 411 + 412 + ret = devm_add_action_or_reset(dev, nct6694_gpio_ida_free, data); 413 + if (ret) 414 + return ret; 415 + 416 + names = devm_kcalloc(dev, NCT6694_NR_GPIO, sizeof(char *), 417 + GFP_KERNEL); 418 + if (!names) 419 + return -ENOMEM; 420 + 421 + for (i = 0; i < NCT6694_NR_GPIO; i++) { 422 + names[i] = devm_kasprintf(dev, GFP_KERNEL, "GPIO%X%d", 423 + data->group, i); 424 + if (!names[i]) 425 + return -ENOMEM; 426 + } 427 + 428 + data->irq = irq_create_mapping(nct6694->domain, 429 + NCT6694_IRQ_GPIO0 + data->group); 430 + if (!data->irq) 431 + return -EINVAL; 432 + 433 + ret = devm_add_action_or_reset(dev, nct6694_irq_dispose_mapping, data); 434 + if (ret) 435 + return ret; 436 + 437 + data->gpio.names = (const char * const*)names; 438 + data->gpio.label = pdev->name; 439 + data->gpio.direction_input = nct6694_direction_input; 440 + data->gpio.get = nct6694_get_value; 441 + data->gpio.direction_output = nct6694_direction_output; 442 + data->gpio.set = nct6694_set_value; 443 + data->gpio.get_direction = nct6694_get_direction; 444 + data->gpio.set_config = nct6694_set_config; 445 + data->gpio.init_valid_mask = nct6694_init_valid_mask; 446 + data->gpio.base = -1; 447 + data->gpio.can_sleep = false; 448 + data->gpio.owner = THIS_MODULE; 449 + data->gpio.ngpio = NCT6694_NR_GPIO; 450 + 451 + platform_set_drvdata(pdev, data); 452 + 453 + ret = devm_mutex_init(dev, &data->lock); 454 + if (ret) 455 + return ret; 456 + 457 + ret = devm_mutex_init(dev, &data->irq_lock); 458 + if (ret) 459 + return ret; 460 + 461 + ret = nct6694_get_irq_trig(data); 462 + if (ret) { 463 + dev_err_probe(dev, ret, "Failed to get irq trigger type\n"); 464 + return ret; 465 + } 466 + 467 + girq = &data->gpio.irq; 468 + gpio_irq_chip_set_chip(girq, &nct6694_irq_chip); 469 + girq->parent_handler = NULL; 470 + girq->num_parents = 0; 471 + girq->parents = NULL; 472 + girq->default_type = IRQ_TYPE_NONE; 473 + girq->handler = handle_level_irq; 474 + girq->threaded = true; 475 + 476 + ret = devm_request_threaded_irq(dev, data->irq, NULL, nct6694_irq_handler, 477 + IRQF_ONESHOT | IRQF_SHARED, 478 + "gpio-nct6694", data); 479 + if (ret) { 480 + dev_err_probe(dev, ret, "Failed to request irq\n"); 481 + return ret; 482 + } 483 + 484 + return devm_gpiochip_add_data(dev, &data->gpio, data); 485 + } 486 + 487 + static struct platform_driver nct6694_gpio_driver = { 488 + .driver = { 489 + .name = "nct6694-gpio", 490 + }, 491 + .probe = nct6694_gpio_probe, 492 + }; 493 + 494 + module_platform_driver(nct6694_gpio_driver); 495 + 496 + MODULE_DESCRIPTION("USB-GPIO controller driver for NCT6694"); 497 + MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>"); 498 + MODULE_LICENSE("GPL"); 499 + MODULE_ALIAS("platform:nct6694-gpio");