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

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'ib-mfd-gpio-hwmon-i2c-can-rtc-watchdog-v6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd into gpio/for-next

Pull changes from the immutable branch between MFD, GPIO, HWMON, I2C,
CAN, RTC and Watchdog trees containing GPIO support for Nuvoton NCT6694.

+3669
+12
MAINTAINERS
··· 18111 18111 F: include/linux/nubus.h 18112 18112 F: include/uapi/linux/nubus.h 18113 18113 18114 + NUVOTON NCT6694 MFD DRIVER 18115 + M: Ming Yu <tmyu0@nuvoton.com> 18116 + S: Supported 18117 + F: drivers/gpio/gpio-nct6694.c 18118 + F: drivers/hwmon/nct6694-hwmon.c 18119 + F: drivers/i2c/busses/i2c-nct6694.c 18120 + F: drivers/mfd/nct6694.c 18121 + F: drivers/net/can/usb/nct6694_canfd.c 18122 + F: drivers/rtc/rtc-nct6694.c 18123 + F: drivers/watchdog/nct6694_wdt.c 18124 + F: include/linux/mfd/nct6694.h 18125 + 18114 18126 NUVOTON NCT7201 IIO DRIVER 18115 18127 M: Eason Yang <j2anfernee@gmail.com> 18116 18128 L: linux-iio@vger.kernel.org
+12
drivers/gpio/Kconfig
··· 1527 1527 This driver can also be built as a module. If so, the module will be 1528 1528 called gpio-max77759. 1529 1529 1530 + config GPIO_NCT6694 1531 + tristate "Nuvoton NCT6694 GPIO controller support" 1532 + depends on MFD_NCT6694 1533 + select GENERIC_IRQ_CHIP 1534 + select GPIOLIB_IRQCHIP 1535 + help 1536 + This driver supports 8 GPIO pins per bank that can all be interrupt 1537 + sources. 1538 + 1539 + This driver can also be built as a module. If so, the module will be 1540 + called gpio-nct6694. 1541 + 1530 1542 config GPIO_PALMAS 1531 1543 tristate "TI PALMAS series PMICs GPIO" 1532 1544 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");
+10
drivers/hwmon/Kconfig
··· 1698 1698 This driver can also be built as a module. If so, the module 1699 1699 will be called nct6683. 1700 1700 1701 + config SENSORS_NCT6694 1702 + tristate "Nuvoton NCT6694 Hardware Monitor support" 1703 + depends on MFD_NCT6694 1704 + help 1705 + Say Y here to support Nuvoton NCT6694 hardware monitoring 1706 + functionality. 1707 + 1708 + This driver can also be built as a module. If so, the module 1709 + will be called nct6694-hwmon. 1710 + 1701 1711 config SENSORS_NCT6775_CORE 1702 1712 tristate 1703 1713 select REGMAP
+1
drivers/hwmon/Makefile
··· 174 174 obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o 175 175 obj-$(CONFIG_SENSORS_MR75203) += mr75203.o 176 176 obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o 177 + obj-$(CONFIG_SENSORS_NCT6694) += nct6694-hwmon.o 177 178 obj-$(CONFIG_SENSORS_NCT6775_CORE) += nct6775-core.o 178 179 nct6775-objs := nct6775-platform.o 179 180 obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o
+949
drivers/hwmon/nct6694-hwmon.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Nuvoton NCT6694 HWMON driver based on USB interface. 4 + * 5 + * Copyright (C) 2025 Nuvoton Technology Corp. 6 + */ 7 + 8 + #include <linux/bits.h> 9 + #include <linux/bitfield.h> 10 + #include <linux/hwmon.h> 11 + #include <linux/kernel.h> 12 + #include <linux/mfd/core.h> 13 + #include <linux/mfd/nct6694.h> 14 + #include <linux/module.h> 15 + #include <linux/platform_device.h> 16 + #include <linux/slab.h> 17 + 18 + /* 19 + * USB command module type for NCT6694 report channel 20 + * This defines the module type used for communication with the NCT6694 21 + * report channel over the USB interface. 22 + */ 23 + #define NCT6694_RPT_MOD 0xFF 24 + 25 + /* Report channel */ 26 + /* 27 + * The report channel is used to report the status of the hardware monitor 28 + * devices, such as voltage, temperature, fan speed, and PWM. 29 + */ 30 + #define NCT6694_VIN_IDX(x) (0x00 + (x)) 31 + #define NCT6694_TIN_IDX(x) \ 32 + ({ typeof(x) (_x) = (x); \ 33 + ((_x) < 10) ? (0x10 + ((_x) * 2)) : \ 34 + (0x30 + (((_x) - 10) * 2)); }) 35 + #define NCT6694_FIN_IDX(x) (0x50 + ((x) * 2)) 36 + #define NCT6694_PWM_IDX(x) (0x70 + (x)) 37 + #define NCT6694_VIN_STS(x) (0x68 + (x)) 38 + #define NCT6694_TIN_STS(x) (0x6A + (x)) 39 + #define NCT6694_FIN_STS(x) (0x6E + (x)) 40 + 41 + /* 42 + * USB command module type for NCT6694 HWMON controller. 43 + * This defines the module type used for communication with the NCT6694 44 + * HWMON controller over the USB interface. 45 + */ 46 + #define NCT6694_HWMON_MOD 0x00 47 + 48 + /* Command 00h - Hardware Monitor Control */ 49 + #define NCT6694_HWMON_CONTROL 0x00 50 + #define NCT6694_HWMON_CONTROL_SEL 0x00 51 + 52 + /* Command 02h - Alarm Control */ 53 + #define NCT6694_HWMON_ALARM 0x02 54 + #define NCT6694_HWMON_ALARM_SEL 0x00 55 + 56 + /* 57 + * USB command module type for NCT6694 PWM controller. 58 + * This defines the module type used for communication with the NCT6694 59 + * PWM controller over the USB interface. 60 + */ 61 + #define NCT6694_PWM_MOD 0x01 62 + 63 + /* PWM Command - Manual Control */ 64 + #define NCT6694_PWM_CONTROL 0x01 65 + #define NCT6694_PWM_CONTROL_SEL 0x00 66 + 67 + #define NCT6694_FREQ_FROM_REG(reg) ((reg) * 25000 / 255) 68 + #define NCT6694_FREQ_TO_REG(val) \ 69 + (DIV_ROUND_CLOSEST(clamp_val((val), 100, 25000) * 255, 25000)) 70 + 71 + #define NCT6694_LSB_REG_MASK GENMASK(7, 5) 72 + #define NCT6694_TIN_HYST_MASK GENMASK(7, 5) 73 + 74 + enum nct6694_hwmon_temp_mode { 75 + NCT6694_HWMON_TWOTIME_IRQ = 0, 76 + NCT6694_HWMON_ONETIME_IRQ, 77 + NCT6694_HWMON_REALTIME_IRQ, 78 + NCT6694_HWMON_COMPARE_IRQ, 79 + }; 80 + 81 + struct __packed nct6694_hwmon_control { 82 + u8 vin_en[2]; 83 + u8 tin_en[2]; 84 + u8 fin_en[2]; 85 + u8 pwm_en[2]; 86 + u8 reserved1[40]; 87 + u8 pwm_freq[10]; 88 + u8 reserved2[6]; 89 + }; 90 + 91 + struct __packed nct6694_hwmon_alarm { 92 + u8 smi_ctrl; 93 + u8 reserved1[15]; 94 + struct { 95 + u8 hl; 96 + u8 ll; 97 + } vin_limit[16]; 98 + struct { 99 + u8 hyst; 100 + s8 hl; 101 + } tin_cfg[32]; 102 + __be16 fin_ll[10]; 103 + u8 reserved2[4]; 104 + }; 105 + 106 + struct __packed nct6694_pwm_control { 107 + u8 mal_en[2]; 108 + u8 mal_val[10]; 109 + u8 reserved[12]; 110 + }; 111 + 112 + union __packed nct6694_hwmon_rpt { 113 + u8 vin; 114 + struct { 115 + u8 msb; 116 + u8 lsb; 117 + } tin; 118 + __be16 fin; 119 + u8 pwm; 120 + u8 status; 121 + }; 122 + 123 + union __packed nct6694_hwmon_msg { 124 + struct nct6694_hwmon_alarm hwmon_alarm; 125 + struct nct6694_pwm_control pwm_ctrl; 126 + }; 127 + 128 + struct nct6694_hwmon_data { 129 + struct nct6694 *nct6694; 130 + struct mutex lock; 131 + struct nct6694_hwmon_control hwmon_en; 132 + union nct6694_hwmon_rpt *rpt; 133 + union nct6694_hwmon_msg *msg; 134 + }; 135 + 136 + static inline long in_from_reg(u8 reg) 137 + { 138 + return reg * 16; 139 + } 140 + 141 + static inline u8 in_to_reg(long val) 142 + { 143 + return DIV_ROUND_CLOSEST(val, 16); 144 + } 145 + 146 + static inline long temp_from_reg(s8 reg) 147 + { 148 + return reg * 1000; 149 + } 150 + 151 + static inline s8 temp_to_reg(long val) 152 + { 153 + return DIV_ROUND_CLOSEST(val, 1000); 154 + } 155 + 156 + #define NCT6694_HWMON_IN_CONFIG (HWMON_I_INPUT | HWMON_I_ENABLE | \ 157 + HWMON_I_MAX | HWMON_I_MIN | \ 158 + HWMON_I_ALARM) 159 + #define NCT6694_HWMON_TEMP_CONFIG (HWMON_T_INPUT | HWMON_T_ENABLE | \ 160 + HWMON_T_MAX | HWMON_T_MAX_HYST | \ 161 + HWMON_T_MAX_ALARM) 162 + #define NCT6694_HWMON_FAN_CONFIG (HWMON_F_INPUT | HWMON_F_ENABLE | \ 163 + HWMON_F_MIN | HWMON_F_MIN_ALARM) 164 + #define NCT6694_HWMON_PWM_CONFIG (HWMON_PWM_INPUT | HWMON_PWM_ENABLE | \ 165 + HWMON_PWM_FREQ) 166 + static const struct hwmon_channel_info *nct6694_info[] = { 167 + HWMON_CHANNEL_INFO(in, 168 + NCT6694_HWMON_IN_CONFIG, /* VIN0 */ 169 + NCT6694_HWMON_IN_CONFIG, /* VIN1 */ 170 + NCT6694_HWMON_IN_CONFIG, /* VIN2 */ 171 + NCT6694_HWMON_IN_CONFIG, /* VIN3 */ 172 + NCT6694_HWMON_IN_CONFIG, /* VIN5 */ 173 + NCT6694_HWMON_IN_CONFIG, /* VIN6 */ 174 + NCT6694_HWMON_IN_CONFIG, /* VIN7 */ 175 + NCT6694_HWMON_IN_CONFIG, /* VIN14 */ 176 + NCT6694_HWMON_IN_CONFIG, /* VIN15 */ 177 + NCT6694_HWMON_IN_CONFIG, /* VIN16 */ 178 + NCT6694_HWMON_IN_CONFIG, /* VBAT */ 179 + NCT6694_HWMON_IN_CONFIG, /* VSB */ 180 + NCT6694_HWMON_IN_CONFIG, /* AVSB */ 181 + NCT6694_HWMON_IN_CONFIG, /* VCC */ 182 + NCT6694_HWMON_IN_CONFIG, /* VHIF */ 183 + NCT6694_HWMON_IN_CONFIG), /* VTT */ 184 + 185 + HWMON_CHANNEL_INFO(temp, 186 + NCT6694_HWMON_TEMP_CONFIG, /* THR1 */ 187 + NCT6694_HWMON_TEMP_CONFIG, /* THR2 */ 188 + NCT6694_HWMON_TEMP_CONFIG, /* THR14 */ 189 + NCT6694_HWMON_TEMP_CONFIG, /* THR15 */ 190 + NCT6694_HWMON_TEMP_CONFIG, /* THR16 */ 191 + NCT6694_HWMON_TEMP_CONFIG, /* TDP0 */ 192 + NCT6694_HWMON_TEMP_CONFIG, /* TDP1 */ 193 + NCT6694_HWMON_TEMP_CONFIG, /* TDP2 */ 194 + NCT6694_HWMON_TEMP_CONFIG, /* TDP3 */ 195 + NCT6694_HWMON_TEMP_CONFIG, /* TDP4 */ 196 + NCT6694_HWMON_TEMP_CONFIG, /* DTIN0 */ 197 + NCT6694_HWMON_TEMP_CONFIG, /* DTIN1 */ 198 + NCT6694_HWMON_TEMP_CONFIG, /* DTIN2 */ 199 + NCT6694_HWMON_TEMP_CONFIG, /* DTIN3 */ 200 + NCT6694_HWMON_TEMP_CONFIG, /* DTIN4 */ 201 + NCT6694_HWMON_TEMP_CONFIG, /* DTIN5 */ 202 + NCT6694_HWMON_TEMP_CONFIG, /* DTIN6 */ 203 + NCT6694_HWMON_TEMP_CONFIG, /* DTIN7 */ 204 + NCT6694_HWMON_TEMP_CONFIG, /* DTIN8 */ 205 + NCT6694_HWMON_TEMP_CONFIG, /* DTIN9 */ 206 + NCT6694_HWMON_TEMP_CONFIG, /* DTIN10 */ 207 + NCT6694_HWMON_TEMP_CONFIG, /* DTIN11 */ 208 + NCT6694_HWMON_TEMP_CONFIG, /* DTIN12 */ 209 + NCT6694_HWMON_TEMP_CONFIG, /* DTIN13 */ 210 + NCT6694_HWMON_TEMP_CONFIG, /* DTIN14 */ 211 + NCT6694_HWMON_TEMP_CONFIG), /* DTIN15 */ 212 + 213 + HWMON_CHANNEL_INFO(fan, 214 + NCT6694_HWMON_FAN_CONFIG, /* FIN0 */ 215 + NCT6694_HWMON_FAN_CONFIG, /* FIN1 */ 216 + NCT6694_HWMON_FAN_CONFIG, /* FIN2 */ 217 + NCT6694_HWMON_FAN_CONFIG, /* FIN3 */ 218 + NCT6694_HWMON_FAN_CONFIG, /* FIN4 */ 219 + NCT6694_HWMON_FAN_CONFIG, /* FIN5 */ 220 + NCT6694_HWMON_FAN_CONFIG, /* FIN6 */ 221 + NCT6694_HWMON_FAN_CONFIG, /* FIN7 */ 222 + NCT6694_HWMON_FAN_CONFIG, /* FIN8 */ 223 + NCT6694_HWMON_FAN_CONFIG), /* FIN9 */ 224 + 225 + HWMON_CHANNEL_INFO(pwm, 226 + NCT6694_HWMON_PWM_CONFIG, /* PWM0 */ 227 + NCT6694_HWMON_PWM_CONFIG, /* PWM1 */ 228 + NCT6694_HWMON_PWM_CONFIG, /* PWM2 */ 229 + NCT6694_HWMON_PWM_CONFIG, /* PWM3 */ 230 + NCT6694_HWMON_PWM_CONFIG, /* PWM4 */ 231 + NCT6694_HWMON_PWM_CONFIG, /* PWM5 */ 232 + NCT6694_HWMON_PWM_CONFIG, /* PWM6 */ 233 + NCT6694_HWMON_PWM_CONFIG, /* PWM7 */ 234 + NCT6694_HWMON_PWM_CONFIG, /* PWM8 */ 235 + NCT6694_HWMON_PWM_CONFIG), /* PWM9 */ 236 + NULL 237 + }; 238 + 239 + static int nct6694_in_read(struct device *dev, u32 attr, int channel, 240 + long *val) 241 + { 242 + struct nct6694_hwmon_data *data = dev_get_drvdata(dev); 243 + struct nct6694_cmd_header cmd_hd; 244 + unsigned char vin_en; 245 + int ret; 246 + 247 + guard(mutex)(&data->lock); 248 + 249 + switch (attr) { 250 + case hwmon_in_enable: 251 + vin_en = data->hwmon_en.vin_en[(channel / 8)]; 252 + *val = !!(vin_en & BIT(channel % 8)); 253 + 254 + return 0; 255 + case hwmon_in_input: 256 + cmd_hd = (struct nct6694_cmd_header) { 257 + .mod = NCT6694_RPT_MOD, 258 + .offset = cpu_to_le16(NCT6694_VIN_IDX(channel)), 259 + .len = cpu_to_le16(sizeof(data->rpt->vin)) 260 + }; 261 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, 262 + &data->rpt->vin); 263 + if (ret) 264 + return ret; 265 + 266 + *val = in_from_reg(data->rpt->vin); 267 + 268 + return 0; 269 + case hwmon_in_max: 270 + cmd_hd = (struct nct6694_cmd_header) { 271 + .mod = NCT6694_HWMON_MOD, 272 + .cmd = NCT6694_HWMON_ALARM, 273 + .sel = NCT6694_HWMON_ALARM_SEL, 274 + .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm)) 275 + }; 276 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, 277 + &data->msg->hwmon_alarm); 278 + if (ret) 279 + return ret; 280 + 281 + *val = in_from_reg(data->msg->hwmon_alarm.vin_limit[channel].hl); 282 + 283 + return 0; 284 + case hwmon_in_min: 285 + cmd_hd = (struct nct6694_cmd_header) { 286 + .mod = NCT6694_HWMON_MOD, 287 + .cmd = NCT6694_HWMON_ALARM, 288 + .sel = NCT6694_HWMON_ALARM_SEL, 289 + .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm)) 290 + }; 291 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, 292 + &data->msg->hwmon_alarm); 293 + if (ret) 294 + return ret; 295 + 296 + *val = in_from_reg(data->msg->hwmon_alarm.vin_limit[channel].ll); 297 + 298 + return 0; 299 + case hwmon_in_alarm: 300 + cmd_hd = (struct nct6694_cmd_header) { 301 + .mod = NCT6694_RPT_MOD, 302 + .offset = cpu_to_le16(NCT6694_VIN_STS(channel / 8)), 303 + .len = cpu_to_le16(sizeof(data->rpt->status)) 304 + }; 305 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, 306 + &data->rpt->status); 307 + if (ret) 308 + return ret; 309 + 310 + *val = !!(data->rpt->status & BIT(channel % 8)); 311 + 312 + return 0; 313 + default: 314 + return -EOPNOTSUPP; 315 + } 316 + } 317 + 318 + static int nct6694_temp_read(struct device *dev, u32 attr, int channel, 319 + long *val) 320 + { 321 + struct nct6694_hwmon_data *data = dev_get_drvdata(dev); 322 + struct nct6694_cmd_header cmd_hd; 323 + unsigned char temp_en, temp_hyst; 324 + signed char temp_max; 325 + int ret, temp_raw; 326 + 327 + guard(mutex)(&data->lock); 328 + 329 + switch (attr) { 330 + case hwmon_temp_enable: 331 + temp_en = data->hwmon_en.tin_en[channel / 8]; 332 + *val = !!(temp_en & BIT(channel % 8)); 333 + 334 + return 0; 335 + case hwmon_temp_input: 336 + cmd_hd = (struct nct6694_cmd_header) { 337 + .mod = NCT6694_RPT_MOD, 338 + .offset = cpu_to_le16(NCT6694_TIN_IDX(channel)), 339 + .len = cpu_to_le16(sizeof(data->rpt->tin)) 340 + }; 341 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, 342 + &data->rpt->tin); 343 + if (ret) 344 + return ret; 345 + 346 + temp_raw = data->rpt->tin.msb << 3; 347 + temp_raw |= FIELD_GET(NCT6694_LSB_REG_MASK, data->rpt->tin.lsb); 348 + 349 + /* Real temperature(milli degrees Celsius) = temp_raw * 1000 * 0.125 */ 350 + *val = sign_extend32(temp_raw, 10) * 125; 351 + 352 + return 0; 353 + case hwmon_temp_max: 354 + cmd_hd = (struct nct6694_cmd_header) { 355 + .mod = NCT6694_HWMON_MOD, 356 + .cmd = NCT6694_HWMON_ALARM, 357 + .sel = NCT6694_HWMON_ALARM_SEL, 358 + .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm)) 359 + }; 360 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, 361 + &data->msg->hwmon_alarm); 362 + if (ret) 363 + return ret; 364 + 365 + *val = temp_from_reg(data->msg->hwmon_alarm.tin_cfg[channel].hl); 366 + 367 + return 0; 368 + case hwmon_temp_max_hyst: 369 + cmd_hd = (struct nct6694_cmd_header) { 370 + .mod = NCT6694_HWMON_MOD, 371 + .cmd = NCT6694_HWMON_ALARM, 372 + .sel = NCT6694_HWMON_ALARM_SEL, 373 + .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm)) 374 + }; 375 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, 376 + &data->msg->hwmon_alarm); 377 + if (ret) 378 + return ret; 379 + 380 + temp_max = data->msg->hwmon_alarm.tin_cfg[channel].hl; 381 + temp_hyst = FIELD_GET(NCT6694_TIN_HYST_MASK, 382 + data->msg->hwmon_alarm.tin_cfg[channel].hyst); 383 + *val = temp_from_reg(temp_max - temp_hyst); 384 + 385 + return 0; 386 + case hwmon_temp_max_alarm: 387 + cmd_hd = (struct nct6694_cmd_header) { 388 + .mod = NCT6694_RPT_MOD, 389 + .offset = cpu_to_le16(NCT6694_TIN_STS(channel / 8)), 390 + .len = cpu_to_le16(sizeof(data->rpt->status)) 391 + }; 392 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, 393 + &data->rpt->status); 394 + if (ret) 395 + return ret; 396 + 397 + *val = !!(data->rpt->status & BIT(channel % 8)); 398 + 399 + return 0; 400 + default: 401 + return -EOPNOTSUPP; 402 + } 403 + } 404 + 405 + static int nct6694_fan_read(struct device *dev, u32 attr, int channel, 406 + long *val) 407 + { 408 + struct nct6694_hwmon_data *data = dev_get_drvdata(dev); 409 + struct nct6694_cmd_header cmd_hd; 410 + unsigned char fanin_en; 411 + int ret; 412 + 413 + guard(mutex)(&data->lock); 414 + 415 + switch (attr) { 416 + case hwmon_fan_enable: 417 + fanin_en = data->hwmon_en.fin_en[channel / 8]; 418 + *val = !!(fanin_en & BIT(channel % 8)); 419 + 420 + return 0; 421 + case hwmon_fan_input: 422 + cmd_hd = (struct nct6694_cmd_header) { 423 + .mod = NCT6694_RPT_MOD, 424 + .offset = cpu_to_le16(NCT6694_FIN_IDX(channel)), 425 + .len = cpu_to_le16(sizeof(data->rpt->fin)) 426 + }; 427 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, 428 + &data->rpt->fin); 429 + if (ret) 430 + return ret; 431 + 432 + *val = be16_to_cpu(data->rpt->fin); 433 + 434 + return 0; 435 + case hwmon_fan_min: 436 + cmd_hd = (struct nct6694_cmd_header) { 437 + .mod = NCT6694_HWMON_MOD, 438 + .cmd = NCT6694_HWMON_ALARM, 439 + .sel = NCT6694_HWMON_ALARM_SEL, 440 + .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm)) 441 + }; 442 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, 443 + &data->msg->hwmon_alarm); 444 + if (ret) 445 + return ret; 446 + 447 + *val = be16_to_cpu(data->msg->hwmon_alarm.fin_ll[channel]); 448 + 449 + return 0; 450 + case hwmon_fan_min_alarm: 451 + cmd_hd = (struct nct6694_cmd_header) { 452 + .mod = NCT6694_RPT_MOD, 453 + .offset = cpu_to_le16(NCT6694_FIN_STS(channel / 8)), 454 + .len = cpu_to_le16(sizeof(data->rpt->status)) 455 + }; 456 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, 457 + &data->rpt->status); 458 + if (ret) 459 + return ret; 460 + 461 + *val = !!(data->rpt->status & BIT(channel % 8)); 462 + 463 + return 0; 464 + default: 465 + return -EOPNOTSUPP; 466 + } 467 + } 468 + 469 + static int nct6694_pwm_read(struct device *dev, u32 attr, int channel, 470 + long *val) 471 + { 472 + struct nct6694_hwmon_data *data = dev_get_drvdata(dev); 473 + struct nct6694_cmd_header cmd_hd; 474 + unsigned char pwm_en; 475 + int ret; 476 + 477 + guard(mutex)(&data->lock); 478 + 479 + switch (attr) { 480 + case hwmon_pwm_enable: 481 + pwm_en = data->hwmon_en.pwm_en[channel / 8]; 482 + *val = !!(pwm_en & BIT(channel % 8)); 483 + 484 + return 0; 485 + case hwmon_pwm_input: 486 + cmd_hd = (struct nct6694_cmd_header) { 487 + .mod = NCT6694_RPT_MOD, 488 + .offset = cpu_to_le16(NCT6694_PWM_IDX(channel)), 489 + .len = cpu_to_le16(sizeof(data->rpt->pwm)) 490 + }; 491 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, 492 + &data->rpt->pwm); 493 + if (ret) 494 + return ret; 495 + 496 + *val = data->rpt->pwm; 497 + 498 + return 0; 499 + case hwmon_pwm_freq: 500 + *val = NCT6694_FREQ_FROM_REG(data->hwmon_en.pwm_freq[channel]); 501 + 502 + return 0; 503 + default: 504 + return -EOPNOTSUPP; 505 + } 506 + } 507 + 508 + static int nct6694_in_write(struct device *dev, u32 attr, int channel, 509 + long val) 510 + { 511 + struct nct6694_hwmon_data *data = dev_get_drvdata(dev); 512 + struct nct6694_cmd_header cmd_hd; 513 + int ret; 514 + 515 + guard(mutex)(&data->lock); 516 + 517 + switch (attr) { 518 + case hwmon_in_enable: 519 + if (val == 0) 520 + data->hwmon_en.vin_en[channel / 8] &= ~BIT(channel % 8); 521 + else if (val == 1) 522 + data->hwmon_en.vin_en[channel / 8] |= BIT(channel % 8); 523 + else 524 + return -EINVAL; 525 + 526 + cmd_hd = (struct nct6694_cmd_header) { 527 + .mod = NCT6694_HWMON_MOD, 528 + .cmd = NCT6694_HWMON_CONTROL, 529 + .sel = NCT6694_HWMON_CONTROL_SEL, 530 + .len = cpu_to_le16(sizeof(data->hwmon_en)) 531 + }; 532 + 533 + return nct6694_write_msg(data->nct6694, &cmd_hd, 534 + &data->hwmon_en); 535 + case hwmon_in_max: 536 + cmd_hd = (struct nct6694_cmd_header) { 537 + .mod = NCT6694_HWMON_MOD, 538 + .cmd = NCT6694_HWMON_ALARM, 539 + .sel = NCT6694_HWMON_ALARM_SEL, 540 + .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm)) 541 + }; 542 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, 543 + &data->msg->hwmon_alarm); 544 + if (ret) 545 + return ret; 546 + 547 + val = clamp_val(val, 0, 2032); 548 + data->msg->hwmon_alarm.vin_limit[channel].hl = in_to_reg(val); 549 + 550 + return nct6694_write_msg(data->nct6694, &cmd_hd, 551 + &data->msg->hwmon_alarm); 552 + case hwmon_in_min: 553 + cmd_hd = (struct nct6694_cmd_header) { 554 + .mod = NCT6694_HWMON_MOD, 555 + .cmd = NCT6694_HWMON_ALARM, 556 + .sel = NCT6694_HWMON_ALARM_SEL, 557 + .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm)) 558 + }; 559 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, 560 + &data->msg->hwmon_alarm); 561 + if (ret) 562 + return ret; 563 + 564 + val = clamp_val(val, 0, 2032); 565 + data->msg->hwmon_alarm.vin_limit[channel].ll = in_to_reg(val); 566 + 567 + return nct6694_write_msg(data->nct6694, &cmd_hd, 568 + &data->msg->hwmon_alarm); 569 + default: 570 + return -EOPNOTSUPP; 571 + } 572 + } 573 + 574 + static int nct6694_temp_write(struct device *dev, u32 attr, int channel, 575 + long val) 576 + { 577 + struct nct6694_hwmon_data *data = dev_get_drvdata(dev); 578 + struct nct6694_cmd_header cmd_hd; 579 + unsigned char temp_hyst; 580 + signed char temp_max; 581 + int ret; 582 + 583 + guard(mutex)(&data->lock); 584 + 585 + switch (attr) { 586 + case hwmon_temp_enable: 587 + if (val == 0) 588 + data->hwmon_en.tin_en[channel / 8] &= ~BIT(channel % 8); 589 + else if (val == 1) 590 + data->hwmon_en.tin_en[channel / 8] |= BIT(channel % 8); 591 + else 592 + return -EINVAL; 593 + 594 + cmd_hd = (struct nct6694_cmd_header) { 595 + .mod = NCT6694_HWMON_MOD, 596 + .cmd = NCT6694_HWMON_CONTROL, 597 + .sel = NCT6694_HWMON_CONTROL_SEL, 598 + .len = cpu_to_le16(sizeof(data->hwmon_en)) 599 + }; 600 + 601 + return nct6694_write_msg(data->nct6694, &cmd_hd, 602 + &data->hwmon_en); 603 + case hwmon_temp_max: 604 + cmd_hd = (struct nct6694_cmd_header) { 605 + .mod = NCT6694_HWMON_MOD, 606 + .cmd = NCT6694_HWMON_ALARM, 607 + .sel = NCT6694_HWMON_ALARM_SEL, 608 + .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm)) 609 + }; 610 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, 611 + &data->msg->hwmon_alarm); 612 + if (ret) 613 + return ret; 614 + 615 + val = clamp_val(val, -127000, 127000); 616 + data->msg->hwmon_alarm.tin_cfg[channel].hl = temp_to_reg(val); 617 + 618 + return nct6694_write_msg(data->nct6694, &cmd_hd, 619 + &data->msg->hwmon_alarm); 620 + case hwmon_temp_max_hyst: 621 + cmd_hd = (struct nct6694_cmd_header) { 622 + .mod = NCT6694_HWMON_MOD, 623 + .cmd = NCT6694_HWMON_ALARM, 624 + .sel = NCT6694_HWMON_ALARM_SEL, 625 + .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm)) 626 + }; 627 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, 628 + &data->msg->hwmon_alarm); 629 + 630 + val = clamp_val(val, -127000, 127000); 631 + temp_max = data->msg->hwmon_alarm.tin_cfg[channel].hl; 632 + temp_hyst = temp_max - temp_to_reg(val); 633 + temp_hyst = clamp_val(temp_hyst, 0, 7); 634 + data->msg->hwmon_alarm.tin_cfg[channel].hyst = 635 + (data->msg->hwmon_alarm.tin_cfg[channel].hyst & ~NCT6694_TIN_HYST_MASK) | 636 + FIELD_PREP(NCT6694_TIN_HYST_MASK, temp_hyst); 637 + 638 + return nct6694_write_msg(data->nct6694, &cmd_hd, 639 + &data->msg->hwmon_alarm); 640 + default: 641 + return -EOPNOTSUPP; 642 + } 643 + } 644 + 645 + static int nct6694_fan_write(struct device *dev, u32 attr, int channel, 646 + long val) 647 + { 648 + struct nct6694_hwmon_data *data = dev_get_drvdata(dev); 649 + struct nct6694_cmd_header cmd_hd; 650 + int ret; 651 + 652 + guard(mutex)(&data->lock); 653 + 654 + switch (attr) { 655 + case hwmon_fan_enable: 656 + if (val == 0) 657 + data->hwmon_en.fin_en[channel / 8] &= ~BIT(channel % 8); 658 + else if (val == 1) 659 + data->hwmon_en.fin_en[channel / 8] |= BIT(channel % 8); 660 + else 661 + return -EINVAL; 662 + 663 + cmd_hd = (struct nct6694_cmd_header) { 664 + .mod = NCT6694_HWMON_MOD, 665 + .cmd = NCT6694_HWMON_CONTROL, 666 + .sel = NCT6694_HWMON_CONTROL_SEL, 667 + .len = cpu_to_le16(sizeof(data->hwmon_en)) 668 + }; 669 + 670 + return nct6694_write_msg(data->nct6694, &cmd_hd, 671 + &data->hwmon_en); 672 + case hwmon_fan_min: 673 + cmd_hd = (struct nct6694_cmd_header) { 674 + .mod = NCT6694_HWMON_MOD, 675 + .cmd = NCT6694_HWMON_ALARM, 676 + .sel = NCT6694_HWMON_ALARM_SEL, 677 + .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm)) 678 + }; 679 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, 680 + &data->msg->hwmon_alarm); 681 + if (ret) 682 + return ret; 683 + 684 + val = clamp_val(val, 1, 65535); 685 + data->msg->hwmon_alarm.fin_ll[channel] = cpu_to_be16(val); 686 + 687 + return nct6694_write_msg(data->nct6694, &cmd_hd, 688 + &data->msg->hwmon_alarm); 689 + default: 690 + return -EOPNOTSUPP; 691 + } 692 + } 693 + 694 + static int nct6694_pwm_write(struct device *dev, u32 attr, int channel, 695 + long val) 696 + { 697 + struct nct6694_hwmon_data *data = dev_get_drvdata(dev); 698 + struct nct6694_cmd_header cmd_hd; 699 + int ret; 700 + 701 + guard(mutex)(&data->lock); 702 + 703 + switch (attr) { 704 + case hwmon_pwm_enable: 705 + if (val == 0) 706 + data->hwmon_en.pwm_en[channel / 8] &= ~BIT(channel % 8); 707 + else if (val == 1) 708 + data->hwmon_en.pwm_en[channel / 8] |= BIT(channel % 8); 709 + else 710 + return -EINVAL; 711 + 712 + cmd_hd = (struct nct6694_cmd_header) { 713 + .mod = NCT6694_HWMON_MOD, 714 + .cmd = NCT6694_HWMON_CONTROL, 715 + .sel = NCT6694_HWMON_CONTROL_SEL, 716 + .len = cpu_to_le16(sizeof(data->hwmon_en)) 717 + }; 718 + 719 + return nct6694_write_msg(data->nct6694, &cmd_hd, 720 + &data->hwmon_en); 721 + case hwmon_pwm_input: 722 + if (val < 0 || val > 255) 723 + return -EINVAL; 724 + 725 + cmd_hd = (struct nct6694_cmd_header) { 726 + .mod = NCT6694_PWM_MOD, 727 + .cmd = NCT6694_PWM_CONTROL, 728 + .sel = NCT6694_PWM_CONTROL_SEL, 729 + .len = cpu_to_le16(sizeof(data->msg->pwm_ctrl)) 730 + }; 731 + 732 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, 733 + &data->msg->pwm_ctrl); 734 + if (ret) 735 + return ret; 736 + 737 + data->msg->pwm_ctrl.mal_val[channel] = val; 738 + 739 + return nct6694_write_msg(data->nct6694, &cmd_hd, 740 + &data->msg->pwm_ctrl); 741 + case hwmon_pwm_freq: 742 + cmd_hd = (struct nct6694_cmd_header) { 743 + .mod = NCT6694_HWMON_MOD, 744 + .cmd = NCT6694_HWMON_CONTROL, 745 + .sel = NCT6694_HWMON_CONTROL_SEL, 746 + .len = cpu_to_le16(sizeof(data->hwmon_en)) 747 + }; 748 + 749 + data->hwmon_en.pwm_freq[channel] = NCT6694_FREQ_TO_REG(val); 750 + 751 + return nct6694_write_msg(data->nct6694, &cmd_hd, 752 + &data->hwmon_en); 753 + default: 754 + return -EOPNOTSUPP; 755 + } 756 + } 757 + 758 + static int nct6694_read(struct device *dev, enum hwmon_sensor_types type, 759 + u32 attr, int channel, long *val) 760 + { 761 + switch (type) { 762 + case hwmon_in: 763 + /* in mV */ 764 + return nct6694_in_read(dev, attr, channel, val); 765 + case hwmon_temp: 766 + /* in mC */ 767 + return nct6694_temp_read(dev, attr, channel, val); 768 + case hwmon_fan: 769 + /* in RPM */ 770 + return nct6694_fan_read(dev, attr, channel, val); 771 + case hwmon_pwm: 772 + /* in value 0~255 */ 773 + return nct6694_pwm_read(dev, attr, channel, val); 774 + default: 775 + return -EOPNOTSUPP; 776 + } 777 + } 778 + 779 + static int nct6694_write(struct device *dev, enum hwmon_sensor_types type, 780 + u32 attr, int channel, long val) 781 + { 782 + switch (type) { 783 + case hwmon_in: 784 + return nct6694_in_write(dev, attr, channel, val); 785 + case hwmon_temp: 786 + return nct6694_temp_write(dev, attr, channel, val); 787 + case hwmon_fan: 788 + return nct6694_fan_write(dev, attr, channel, val); 789 + case hwmon_pwm: 790 + return nct6694_pwm_write(dev, attr, channel, val); 791 + default: 792 + return -EOPNOTSUPP; 793 + } 794 + } 795 + 796 + static umode_t nct6694_is_visible(const void *data, 797 + enum hwmon_sensor_types type, 798 + u32 attr, int channel) 799 + { 800 + switch (type) { 801 + case hwmon_in: 802 + switch (attr) { 803 + case hwmon_in_enable: 804 + case hwmon_in_max: 805 + case hwmon_in_min: 806 + return 0644; 807 + case hwmon_in_alarm: 808 + case hwmon_in_input: 809 + return 0444; 810 + default: 811 + return 0; 812 + } 813 + case hwmon_temp: 814 + switch (attr) { 815 + case hwmon_temp_enable: 816 + case hwmon_temp_max: 817 + case hwmon_temp_max_hyst: 818 + return 0644; 819 + case hwmon_temp_input: 820 + case hwmon_temp_max_alarm: 821 + return 0444; 822 + default: 823 + return 0; 824 + } 825 + case hwmon_fan: 826 + switch (attr) { 827 + case hwmon_fan_enable: 828 + case hwmon_fan_min: 829 + return 0644; 830 + case hwmon_fan_input: 831 + case hwmon_fan_min_alarm: 832 + return 0444; 833 + default: 834 + return 0; 835 + } 836 + case hwmon_pwm: 837 + switch (attr) { 838 + case hwmon_pwm_enable: 839 + case hwmon_pwm_freq: 840 + case hwmon_pwm_input: 841 + return 0644; 842 + default: 843 + return 0; 844 + } 845 + default: 846 + return 0; 847 + } 848 + } 849 + 850 + static const struct hwmon_ops nct6694_hwmon_ops = { 851 + .is_visible = nct6694_is_visible, 852 + .read = nct6694_read, 853 + .write = nct6694_write, 854 + }; 855 + 856 + static const struct hwmon_chip_info nct6694_chip_info = { 857 + .ops = &nct6694_hwmon_ops, 858 + .info = nct6694_info, 859 + }; 860 + 861 + static int nct6694_hwmon_init(struct nct6694_hwmon_data *data) 862 + { 863 + struct nct6694_cmd_header cmd_hd = { 864 + .mod = NCT6694_HWMON_MOD, 865 + .cmd = NCT6694_HWMON_CONTROL, 866 + .sel = NCT6694_HWMON_CONTROL_SEL, 867 + .len = cpu_to_le16(sizeof(data->hwmon_en)) 868 + }; 869 + int ret; 870 + 871 + /* 872 + * Record each Hardware Monitor Channel enable status 873 + * and PWM frequency register 874 + */ 875 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, 876 + &data->hwmon_en); 877 + if (ret) 878 + return ret; 879 + 880 + cmd_hd = (struct nct6694_cmd_header) { 881 + .mod = NCT6694_HWMON_MOD, 882 + .cmd = NCT6694_HWMON_ALARM, 883 + .sel = NCT6694_HWMON_ALARM_SEL, 884 + .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm)) 885 + }; 886 + 887 + /* Select hwmon device alarm mode */ 888 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, 889 + &data->msg->hwmon_alarm); 890 + if (ret) 891 + return ret; 892 + 893 + data->msg->hwmon_alarm.smi_ctrl = NCT6694_HWMON_REALTIME_IRQ; 894 + 895 + return nct6694_write_msg(data->nct6694, &cmd_hd, 896 + &data->msg->hwmon_alarm); 897 + } 898 + 899 + static int nct6694_hwmon_probe(struct platform_device *pdev) 900 + { 901 + struct nct6694_hwmon_data *data; 902 + struct nct6694 *nct6694 = dev_get_drvdata(pdev->dev.parent); 903 + struct device *hwmon_dev; 904 + int ret; 905 + 906 + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 907 + if (!data) 908 + return -ENOMEM; 909 + 910 + data->rpt = devm_kzalloc(&pdev->dev, sizeof(union nct6694_hwmon_rpt), 911 + GFP_KERNEL); 912 + if (!data->rpt) 913 + return -ENOMEM; 914 + 915 + data->msg = devm_kzalloc(&pdev->dev, sizeof(union nct6694_hwmon_msg), 916 + GFP_KERNEL); 917 + if (!data->msg) 918 + return -ENOMEM; 919 + 920 + data->nct6694 = nct6694; 921 + ret = devm_mutex_init(&pdev->dev, &data->lock); 922 + if (ret) 923 + return ret; 924 + 925 + ret = nct6694_hwmon_init(data); 926 + if (ret) 927 + return ret; 928 + 929 + /* Register hwmon device to HWMON framework */ 930 + hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, 931 + "nct6694", data, 932 + &nct6694_chip_info, 933 + NULL); 934 + return PTR_ERR_OR_ZERO(hwmon_dev); 935 + } 936 + 937 + static struct platform_driver nct6694_hwmon_driver = { 938 + .driver = { 939 + .name = "nct6694-hwmon", 940 + }, 941 + .probe = nct6694_hwmon_probe, 942 + }; 943 + 944 + module_platform_driver(nct6694_hwmon_driver); 945 + 946 + MODULE_DESCRIPTION("USB-HWMON driver for NCT6694"); 947 + MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>"); 948 + MODULE_LICENSE("GPL"); 949 + MODULE_ALIAS("platform:nct6694-hwmon");
+10
drivers/i2c/busses/Kconfig
··· 1357 1357 This driver can also be built as a module. If so, the module 1358 1358 will be called i2c-ljca. 1359 1359 1360 + config I2C_NCT6694 1361 + tristate "Nuvoton NCT6694 I2C adapter support" 1362 + depends on MFD_NCT6694 1363 + help 1364 + If you say yes to this option, support will be included for Nuvoton 1365 + NCT6694, a USB to I2C interface. 1366 + 1367 + This driver can also be built as a module. If so, the module will 1368 + be called i2c-nct6694. 1369 + 1360 1370 config I2C_CP2615 1361 1371 tristate "Silicon Labs CP2615 USB sound card and I2C adapter" 1362 1372 depends on USB
+1
drivers/i2c/busses/Makefile
··· 135 135 obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o 136 136 obj-$(CONFIG_I2C_DLN2) += i2c-dln2.o 137 137 obj-$(CONFIG_I2C_LJCA) += i2c-ljca.o 138 + obj-$(CONFIG_I2C_NCT6694) += i2c-nct6694.o 138 139 obj-$(CONFIG_I2C_CP2615) += i2c-cp2615.o 139 140 obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o 140 141 obj-$(CONFIG_I2C_PCI1XXXX) += i2c-mchp-pci1xxxx.o
+196
drivers/i2c/busses/i2c-nct6694.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Nuvoton NCT6694 I2C adapter driver based on USB interface. 4 + * 5 + * Copyright (C) 2025 Nuvoton Technology Corp. 6 + */ 7 + 8 + #include <linux/i2c.h> 9 + #include <linux/idr.h> 10 + #include <linux/kernel.h> 11 + #include <linux/mfd/nct6694.h> 12 + #include <linux/module.h> 13 + #include <linux/platform_device.h> 14 + 15 + /* 16 + * USB command module type for NCT6694 I2C controller. 17 + * This defines the module type used for communication with the NCT6694 18 + * I2C controller over the USB interface. 19 + */ 20 + #define NCT6694_I2C_MOD 0x03 21 + 22 + /* Command 00h - I2C Deliver */ 23 + #define NCT6694_I2C_DELIVER 0x00 24 + #define NCT6694_I2C_DELIVER_SEL 0x00 25 + 26 + #define NCT6694_I2C_MAX_XFER_SIZE 64 27 + #define NCT6694_I2C_MAX_DEVS 6 28 + 29 + static unsigned char br_reg[NCT6694_I2C_MAX_DEVS] = {[0 ... (NCT6694_I2C_MAX_DEVS - 1)] = 0xFF}; 30 + 31 + module_param_array(br_reg, byte, NULL, 0644); 32 + MODULE_PARM_DESC(br_reg, 33 + "I2C Baudrate register per adapter: (0=25K, 1=50K, 2=100K, 3=200K, 4=400K, 5=800K, 6=1M), default=2"); 34 + 35 + enum nct6694_i2c_baudrate { 36 + NCT6694_I2C_BR_25K = 0, 37 + NCT6694_I2C_BR_50K, 38 + NCT6694_I2C_BR_100K, 39 + NCT6694_I2C_BR_200K, 40 + NCT6694_I2C_BR_400K, 41 + NCT6694_I2C_BR_800K, 42 + NCT6694_I2C_BR_1M 43 + }; 44 + 45 + struct __packed nct6694_i2c_deliver { 46 + u8 port; 47 + u8 br; 48 + u8 addr; 49 + u8 w_cnt; 50 + u8 r_cnt; 51 + u8 rsv[11]; 52 + u8 write_data[NCT6694_I2C_MAX_XFER_SIZE]; 53 + u8 read_data[NCT6694_I2C_MAX_XFER_SIZE]; 54 + }; 55 + 56 + struct nct6694_i2c_data { 57 + struct device *dev; 58 + struct nct6694 *nct6694; 59 + struct i2c_adapter adapter; 60 + struct nct6694_i2c_deliver deliver; 61 + unsigned char port; 62 + unsigned char br; 63 + }; 64 + 65 + static int nct6694_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) 66 + { 67 + struct nct6694_i2c_data *data = adap->algo_data; 68 + struct nct6694_i2c_deliver *deliver = &data->deliver; 69 + static const struct nct6694_cmd_header cmd_hd = { 70 + .mod = NCT6694_I2C_MOD, 71 + .cmd = NCT6694_I2C_DELIVER, 72 + .sel = NCT6694_I2C_DELIVER_SEL, 73 + .len = cpu_to_le16(sizeof(*deliver)) 74 + }; 75 + int ret, i; 76 + 77 + for (i = 0; i < num; i++) { 78 + struct i2c_msg *msg_temp = &msgs[i]; 79 + 80 + memset(deliver, 0, sizeof(*deliver)); 81 + 82 + deliver->port = data->port; 83 + deliver->br = data->br; 84 + deliver->addr = i2c_8bit_addr_from_msg(msg_temp); 85 + if (msg_temp->flags & I2C_M_RD) { 86 + deliver->r_cnt = msg_temp->len; 87 + ret = nct6694_write_msg(data->nct6694, &cmd_hd, deliver); 88 + if (ret < 0) 89 + return ret; 90 + 91 + memcpy(msg_temp->buf, deliver->read_data, msg_temp->len); 92 + } else { 93 + deliver->w_cnt = msg_temp->len; 94 + memcpy(deliver->write_data, msg_temp->buf, msg_temp->len); 95 + ret = nct6694_write_msg(data->nct6694, &cmd_hd, deliver); 96 + if (ret < 0) 97 + return ret; 98 + } 99 + } 100 + 101 + return num; 102 + } 103 + 104 + static u32 nct6694_i2c_func(struct i2c_adapter *adapter) 105 + { 106 + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; 107 + } 108 + 109 + static const struct i2c_adapter_quirks nct6694_i2c_quirks = { 110 + .max_read_len = NCT6694_I2C_MAX_XFER_SIZE, 111 + .max_write_len = NCT6694_I2C_MAX_XFER_SIZE, 112 + }; 113 + 114 + static const struct i2c_algorithm nct6694_i2c_algo = { 115 + .xfer = nct6694_i2c_xfer, 116 + .functionality = nct6694_i2c_func, 117 + }; 118 + 119 + static int nct6694_i2c_set_baudrate(struct nct6694_i2c_data *data) 120 + { 121 + if (data->port >= NCT6694_I2C_MAX_DEVS) { 122 + dev_err(data->dev, "Invalid I2C port index %d\n", data->port); 123 + return -EINVAL; 124 + } 125 + 126 + if (br_reg[data->port] > NCT6694_I2C_BR_1M) { 127 + dev_warn(data->dev, "Invalid baudrate %d for I2C%d, using 100K\n", 128 + br_reg[data->port], data->port); 129 + br_reg[data->port] = NCT6694_I2C_BR_100K; 130 + } 131 + 132 + data->br = br_reg[data->port]; 133 + 134 + return 0; 135 + } 136 + 137 + static void nct6694_i2c_ida_free(void *d) 138 + { 139 + struct nct6694_i2c_data *data = d; 140 + struct nct6694 *nct6694 = data->nct6694; 141 + 142 + ida_free(&nct6694->i2c_ida, data->port); 143 + } 144 + 145 + static int nct6694_i2c_probe(struct platform_device *pdev) 146 + { 147 + struct device *dev = &pdev->dev; 148 + struct nct6694 *nct6694 = dev_get_drvdata(dev->parent); 149 + struct nct6694_i2c_data *data; 150 + int ret; 151 + 152 + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 153 + if (!data) 154 + return -ENOMEM; 155 + 156 + data->dev = dev; 157 + data->nct6694 = nct6694; 158 + 159 + ret = ida_alloc(&nct6694->i2c_ida, GFP_KERNEL); 160 + if (ret < 0) 161 + return ret; 162 + data->port = ret; 163 + 164 + ret = devm_add_action_or_reset(dev, nct6694_i2c_ida_free, data); 165 + if (ret) 166 + return ret; 167 + 168 + ret = nct6694_i2c_set_baudrate(data); 169 + if (ret) 170 + return ret; 171 + 172 + sprintf(data->adapter.name, "NCT6694 I2C Adapter %d", data->port); 173 + data->adapter.owner = THIS_MODULE; 174 + data->adapter.algo = &nct6694_i2c_algo; 175 + data->adapter.quirks = &nct6694_i2c_quirks; 176 + data->adapter.dev.parent = dev; 177 + data->adapter.algo_data = data; 178 + 179 + platform_set_drvdata(pdev, data); 180 + 181 + return devm_i2c_add_adapter(dev, &data->adapter); 182 + } 183 + 184 + static struct platform_driver nct6694_i2c_driver = { 185 + .driver = { 186 + .name = "nct6694-i2c", 187 + }, 188 + .probe = nct6694_i2c_probe, 189 + }; 190 + 191 + module_platform_driver(nct6694_i2c_driver); 192 + 193 + MODULE_DESCRIPTION("USB-I2C adapter driver for NCT6694"); 194 + MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>"); 195 + MODULE_LICENSE("GPL"); 196 + MODULE_ALIAS("platform:nct6694-i2c");
+15
drivers/mfd/Kconfig
··· 1134 1134 This driver can also be built as a module. If so the module 1135 1135 will be called menf21bmc. 1136 1136 1137 + config MFD_NCT6694 1138 + tristate "Nuvoton NCT6694 support" 1139 + select MFD_CORE 1140 + depends on USB 1141 + help 1142 + This enables support for the Nuvoton USB device NCT6694, which shares 1143 + peripherals. 1144 + The Nuvoton NCT6694 is a peripheral expander with 16 GPIO chips, 1145 + 6 I2C controllers, 2 CANfd controllers, 2 Watchdog timers, ADC, 1146 + PWM, and RTC. 1147 + This driver provides core APIs to access the NCT6694 hardware 1148 + monitoring and control features. 1149 + Additional drivers must be enabled to utilize the specific 1150 + functionalities of the device. 1151 + 1137 1152 config MFD_OCELOT 1138 1153 tristate "Microsemi Ocelot External Control Support" 1139 1154 depends on SPI_MASTER
+2
drivers/mfd/Makefile
··· 121 121 obj-$(CONFIG_MFD_MC13XXX_SPI) += mc13xxx-spi.o 122 122 obj-$(CONFIG_MFD_MC13XXX_I2C) += mc13xxx-i2c.o 123 123 124 + obj-$(CONFIG_MFD_NCT6694) += nct6694.o 125 + 124 126 obj-$(CONFIG_MFD_CORE) += mfd-core.o 125 127 126 128 ocelot-soc-objs := ocelot-core.o ocelot-spi.o
+388
drivers/mfd/nct6694.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2025 Nuvoton Technology Corp. 4 + * 5 + * Nuvoton NCT6694 core driver using USB interface to provide 6 + * access to the NCT6694 hardware monitoring and control features. 7 + * 8 + * The NCT6694 is an integrated controller that provides GPIO, I2C, 9 + * CAN, WDT, HWMON and RTC management. 10 + */ 11 + 12 + #include <linux/bits.h> 13 + #include <linux/interrupt.h> 14 + #include <linux/idr.h> 15 + #include <linux/irq.h> 16 + #include <linux/irqdomain.h> 17 + #include <linux/kernel.h> 18 + #include <linux/mfd/core.h> 19 + #include <linux/mfd/nct6694.h> 20 + #include <linux/module.h> 21 + #include <linux/slab.h> 22 + #include <linux/spinlock.h> 23 + #include <linux/usb.h> 24 + 25 + static const struct mfd_cell nct6694_devs[] = { 26 + MFD_CELL_NAME("nct6694-gpio"), 27 + MFD_CELL_NAME("nct6694-gpio"), 28 + MFD_CELL_NAME("nct6694-gpio"), 29 + MFD_CELL_NAME("nct6694-gpio"), 30 + MFD_CELL_NAME("nct6694-gpio"), 31 + MFD_CELL_NAME("nct6694-gpio"), 32 + MFD_CELL_NAME("nct6694-gpio"), 33 + MFD_CELL_NAME("nct6694-gpio"), 34 + MFD_CELL_NAME("nct6694-gpio"), 35 + MFD_CELL_NAME("nct6694-gpio"), 36 + MFD_CELL_NAME("nct6694-gpio"), 37 + MFD_CELL_NAME("nct6694-gpio"), 38 + MFD_CELL_NAME("nct6694-gpio"), 39 + MFD_CELL_NAME("nct6694-gpio"), 40 + MFD_CELL_NAME("nct6694-gpio"), 41 + MFD_CELL_NAME("nct6694-gpio"), 42 + 43 + MFD_CELL_NAME("nct6694-i2c"), 44 + MFD_CELL_NAME("nct6694-i2c"), 45 + MFD_CELL_NAME("nct6694-i2c"), 46 + MFD_CELL_NAME("nct6694-i2c"), 47 + MFD_CELL_NAME("nct6694-i2c"), 48 + MFD_CELL_NAME("nct6694-i2c"), 49 + 50 + MFD_CELL_NAME("nct6694-canfd"), 51 + MFD_CELL_NAME("nct6694-canfd"), 52 + 53 + MFD_CELL_NAME("nct6694-wdt"), 54 + MFD_CELL_NAME("nct6694-wdt"), 55 + 56 + MFD_CELL_NAME("nct6694-hwmon"), 57 + 58 + MFD_CELL_NAME("nct6694-rtc"), 59 + }; 60 + 61 + static int nct6694_response_err_handling(struct nct6694 *nct6694, unsigned char err_status) 62 + { 63 + switch (err_status) { 64 + case NCT6694_NO_ERROR: 65 + return 0; 66 + case NCT6694_NOT_SUPPORT_ERROR: 67 + dev_err(nct6694->dev, "Command is not supported!\n"); 68 + break; 69 + case NCT6694_NO_RESPONSE_ERROR: 70 + dev_warn(nct6694->dev, "Command received no response!\n"); 71 + break; 72 + case NCT6694_TIMEOUT_ERROR: 73 + dev_warn(nct6694->dev, "Command timed out!\n"); 74 + break; 75 + case NCT6694_PENDING: 76 + dev_err(nct6694->dev, "Command is pending!\n"); 77 + break; 78 + default: 79 + return -EINVAL; 80 + } 81 + 82 + return -EIO; 83 + } 84 + 85 + /** 86 + * nct6694_read_msg() - Read message from NCT6694 device 87 + * @nct6694: NCT6694 device pointer 88 + * @cmd_hd: command header structure 89 + * @buf: buffer to store the response data 90 + * 91 + * Sends a command to the NCT6694 device and reads the response. 92 + * The command header is specified in @cmd_hd, and the response 93 + * data is stored in @buf. 94 + * 95 + * Return: Negative value on error or 0 on success. 96 + */ 97 + int nct6694_read_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *cmd_hd, void *buf) 98 + { 99 + union nct6694_usb_msg *msg = nct6694->usb_msg; 100 + struct usb_device *udev = nct6694->udev; 101 + int tx_len, rx_len, ret; 102 + 103 + guard(mutex)(&nct6694->access_lock); 104 + 105 + memcpy(&msg->cmd_header, cmd_hd, sizeof(*cmd_hd)); 106 + msg->cmd_header.hctrl = NCT6694_HCTRL_GET; 107 + 108 + /* Send command packet to USB device */ 109 + ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, NCT6694_BULK_OUT_EP), &msg->cmd_header, 110 + sizeof(*msg), &tx_len, NCT6694_URB_TIMEOUT); 111 + if (ret) 112 + return ret; 113 + 114 + /* Receive response packet from USB device */ 115 + ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, NCT6694_BULK_IN_EP), &msg->response_header, 116 + sizeof(*msg), &rx_len, NCT6694_URB_TIMEOUT); 117 + if (ret) 118 + return ret; 119 + 120 + /* Receive data packet from USB device */ 121 + ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, NCT6694_BULK_IN_EP), buf, 122 + le16_to_cpu(cmd_hd->len), &rx_len, NCT6694_URB_TIMEOUT); 123 + if (ret) 124 + return ret; 125 + 126 + if (rx_len != le16_to_cpu(cmd_hd->len)) { 127 + dev_err(nct6694->dev, "Expected received length %d, but got %d\n", 128 + le16_to_cpu(cmd_hd->len), rx_len); 129 + return -EIO; 130 + } 131 + 132 + return nct6694_response_err_handling(nct6694, msg->response_header.sts); 133 + } 134 + EXPORT_SYMBOL_GPL(nct6694_read_msg); 135 + 136 + /** 137 + * nct6694_write_msg() - Write message to NCT6694 device 138 + * @nct6694: NCT6694 device pointer 139 + * @cmd_hd: command header structure 140 + * @buf: buffer containing the data to be sent 141 + * 142 + * Sends a command to the NCT6694 device and writes the data 143 + * from @buf. The command header is specified in @cmd_hd. 144 + * 145 + * Return: Negative value on error or 0 on success. 146 + */ 147 + int nct6694_write_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *cmd_hd, void *buf) 148 + { 149 + union nct6694_usb_msg *msg = nct6694->usb_msg; 150 + struct usb_device *udev = nct6694->udev; 151 + int tx_len, rx_len, ret; 152 + 153 + guard(mutex)(&nct6694->access_lock); 154 + 155 + memcpy(&msg->cmd_header, cmd_hd, sizeof(*cmd_hd)); 156 + msg->cmd_header.hctrl = NCT6694_HCTRL_SET; 157 + 158 + /* Send command packet to USB device */ 159 + ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, NCT6694_BULK_OUT_EP), &msg->cmd_header, 160 + sizeof(*msg), &tx_len, NCT6694_URB_TIMEOUT); 161 + if (ret) 162 + return ret; 163 + 164 + /* Send data packet to USB device */ 165 + ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, NCT6694_BULK_OUT_EP), buf, 166 + le16_to_cpu(cmd_hd->len), &tx_len, NCT6694_URB_TIMEOUT); 167 + if (ret) 168 + return ret; 169 + 170 + /* Receive response packet from USB device */ 171 + ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, NCT6694_BULK_IN_EP), &msg->response_header, 172 + sizeof(*msg), &rx_len, NCT6694_URB_TIMEOUT); 173 + if (ret) 174 + return ret; 175 + 176 + /* Receive data packet from USB device */ 177 + ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, NCT6694_BULK_IN_EP), buf, 178 + le16_to_cpu(cmd_hd->len), &rx_len, NCT6694_URB_TIMEOUT); 179 + if (ret) 180 + return ret; 181 + 182 + if (rx_len != le16_to_cpu(cmd_hd->len)) { 183 + dev_err(nct6694->dev, "Expected transmitted length %d, but got %d\n", 184 + le16_to_cpu(cmd_hd->len), rx_len); 185 + return -EIO; 186 + } 187 + 188 + return nct6694_response_err_handling(nct6694, msg->response_header.sts); 189 + } 190 + EXPORT_SYMBOL_GPL(nct6694_write_msg); 191 + 192 + static void usb_int_callback(struct urb *urb) 193 + { 194 + struct nct6694 *nct6694 = urb->context; 195 + __le32 *status_le = urb->transfer_buffer; 196 + u32 int_status; 197 + int ret; 198 + 199 + switch (urb->status) { 200 + case 0: 201 + break; 202 + case -ECONNRESET: 203 + case -ENOENT: 204 + case -ESHUTDOWN: 205 + return; 206 + default: 207 + goto resubmit; 208 + } 209 + 210 + int_status = le32_to_cpu(*status_le); 211 + 212 + while (int_status) { 213 + int irq = __ffs(int_status); 214 + 215 + generic_handle_irq_safe(irq_find_mapping(nct6694->domain, irq)); 216 + int_status &= ~BIT(irq); 217 + } 218 + 219 + resubmit: 220 + ret = usb_submit_urb(urb, GFP_ATOMIC); 221 + if (ret) 222 + dev_warn(nct6694->dev, "Failed to resubmit urb, status %pe", ERR_PTR(ret)); 223 + } 224 + 225 + static void nct6694_irq_enable(struct irq_data *data) 226 + { 227 + struct nct6694 *nct6694 = irq_data_get_irq_chip_data(data); 228 + irq_hw_number_t hwirq = irqd_to_hwirq(data); 229 + 230 + guard(spinlock_irqsave)(&nct6694->irq_lock); 231 + 232 + nct6694->irq_enable |= BIT(hwirq); 233 + } 234 + 235 + static void nct6694_irq_disable(struct irq_data *data) 236 + { 237 + struct nct6694 *nct6694 = irq_data_get_irq_chip_data(data); 238 + irq_hw_number_t hwirq = irqd_to_hwirq(data); 239 + 240 + guard(spinlock_irqsave)(&nct6694->irq_lock); 241 + 242 + nct6694->irq_enable &= ~BIT(hwirq); 243 + } 244 + 245 + static const struct irq_chip nct6694_irq_chip = { 246 + .name = "nct6694-irq", 247 + .flags = IRQCHIP_SKIP_SET_WAKE, 248 + .irq_enable = nct6694_irq_enable, 249 + .irq_disable = nct6694_irq_disable, 250 + }; 251 + 252 + static int nct6694_irq_domain_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) 253 + { 254 + struct nct6694 *nct6694 = d->host_data; 255 + 256 + irq_set_chip_data(irq, nct6694); 257 + irq_set_chip_and_handler(irq, &nct6694_irq_chip, handle_simple_irq); 258 + 259 + return 0; 260 + } 261 + 262 + static void nct6694_irq_domain_unmap(struct irq_domain *d, unsigned int irq) 263 + { 264 + irq_set_chip_and_handler(irq, NULL, NULL); 265 + irq_set_chip_data(irq, NULL); 266 + } 267 + 268 + static const struct irq_domain_ops nct6694_irq_domain_ops = { 269 + .map = nct6694_irq_domain_map, 270 + .unmap = nct6694_irq_domain_unmap, 271 + }; 272 + 273 + static int nct6694_usb_probe(struct usb_interface *iface, 274 + const struct usb_device_id *id) 275 + { 276 + struct usb_device *udev = interface_to_usbdev(iface); 277 + struct usb_endpoint_descriptor *int_endpoint; 278 + struct usb_host_interface *interface; 279 + struct device *dev = &iface->dev; 280 + struct nct6694 *nct6694; 281 + int ret; 282 + 283 + nct6694 = devm_kzalloc(dev, sizeof(*nct6694), GFP_KERNEL); 284 + if (!nct6694) 285 + return -ENOMEM; 286 + 287 + nct6694->usb_msg = devm_kzalloc(dev, sizeof(union nct6694_usb_msg), GFP_KERNEL); 288 + if (!nct6694->usb_msg) 289 + return -ENOMEM; 290 + 291 + nct6694->int_buffer = devm_kzalloc(dev, sizeof(*nct6694->int_buffer), GFP_KERNEL); 292 + if (!nct6694->int_buffer) 293 + return -ENOMEM; 294 + 295 + nct6694->int_in_urb = usb_alloc_urb(0, GFP_KERNEL); 296 + if (!nct6694->int_in_urb) 297 + return -ENOMEM; 298 + 299 + nct6694->domain = irq_domain_create_simple(NULL, NCT6694_NR_IRQS, 0, 300 + &nct6694_irq_domain_ops, 301 + nct6694); 302 + if (!nct6694->domain) { 303 + ret = -ENODEV; 304 + goto err_urb; 305 + } 306 + 307 + nct6694->dev = dev; 308 + nct6694->udev = udev; 309 + 310 + ida_init(&nct6694->gpio_ida); 311 + ida_init(&nct6694->i2c_ida); 312 + ida_init(&nct6694->canfd_ida); 313 + ida_init(&nct6694->wdt_ida); 314 + 315 + spin_lock_init(&nct6694->irq_lock); 316 + 317 + ret = devm_mutex_init(dev, &nct6694->access_lock); 318 + if (ret) 319 + goto err_ida; 320 + 321 + interface = iface->cur_altsetting; 322 + 323 + int_endpoint = &interface->endpoint[0].desc; 324 + if (!usb_endpoint_is_int_in(int_endpoint)) { 325 + ret = -ENODEV; 326 + goto err_ida; 327 + } 328 + 329 + usb_fill_int_urb(nct6694->int_in_urb, udev, usb_rcvintpipe(udev, NCT6694_INT_IN_EP), 330 + nct6694->int_buffer, sizeof(*nct6694->int_buffer), usb_int_callback, 331 + nct6694, int_endpoint->bInterval); 332 + 333 + ret = usb_submit_urb(nct6694->int_in_urb, GFP_KERNEL); 334 + if (ret) 335 + goto err_ida; 336 + 337 + usb_set_intfdata(iface, nct6694); 338 + 339 + ret = mfd_add_hotplug_devices(dev, nct6694_devs, ARRAY_SIZE(nct6694_devs)); 340 + if (ret) 341 + goto err_mfd; 342 + 343 + return 0; 344 + 345 + err_mfd: 346 + usb_kill_urb(nct6694->int_in_urb); 347 + err_ida: 348 + ida_destroy(&nct6694->wdt_ida); 349 + ida_destroy(&nct6694->canfd_ida); 350 + ida_destroy(&nct6694->i2c_ida); 351 + ida_destroy(&nct6694->gpio_ida); 352 + irq_domain_remove(nct6694->domain); 353 + err_urb: 354 + usb_free_urb(nct6694->int_in_urb); 355 + return ret; 356 + } 357 + 358 + static void nct6694_usb_disconnect(struct usb_interface *iface) 359 + { 360 + struct nct6694 *nct6694 = usb_get_intfdata(iface); 361 + 362 + mfd_remove_devices(nct6694->dev); 363 + usb_kill_urb(nct6694->int_in_urb); 364 + ida_destroy(&nct6694->wdt_ida); 365 + ida_destroy(&nct6694->canfd_ida); 366 + ida_destroy(&nct6694->i2c_ida); 367 + ida_destroy(&nct6694->gpio_ida); 368 + irq_domain_remove(nct6694->domain); 369 + usb_free_urb(nct6694->int_in_urb); 370 + } 371 + 372 + static const struct usb_device_id nct6694_ids[] = { 373 + { USB_DEVICE_AND_INTERFACE_INFO(NCT6694_VENDOR_ID, NCT6694_PRODUCT_ID, 0xFF, 0x00, 0x00) }, 374 + { } 375 + }; 376 + MODULE_DEVICE_TABLE(usb, nct6694_ids); 377 + 378 + static struct usb_driver nct6694_usb_driver = { 379 + .name = "nct6694", 380 + .id_table = nct6694_ids, 381 + .probe = nct6694_usb_probe, 382 + .disconnect = nct6694_usb_disconnect, 383 + }; 384 + module_usb_driver(nct6694_usb_driver); 385 + 386 + MODULE_DESCRIPTION("Nuvoton NCT6694 core driver"); 387 + MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>"); 388 + MODULE_LICENSE("GPL");
+11
drivers/net/can/usb/Kconfig
··· 134 134 This driver supports the CAN BUS Analyzer interface 135 135 from Microchip (http://www.microchip.com/development-tools/). 136 136 137 + config CAN_NCT6694 138 + tristate "Nuvoton NCT6694 Socket CANfd support" 139 + depends on MFD_NCT6694 140 + select CAN_RX_OFFLOAD 141 + help 142 + If you say yes to this option, support will be included for Nuvoton 143 + NCT6694, a USB device to socket CANfd controller. 144 + 145 + This driver can also be built as a module. If so, the module will 146 + be called nct6694_canfd. 147 + 137 148 config CAN_PEAK_USB 138 149 tristate "PEAK PCAN-USB/USB Pro interfaces for CAN 2.0b/CAN-FD" 139 150 help
+1
drivers/net/can/usb/Makefile
··· 11 11 obj-$(CONFIG_CAN_GS_USB) += gs_usb.o 12 12 obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb/ 13 13 obj-$(CONFIG_CAN_MCBA_USB) += mcba_usb.o 14 + obj-$(CONFIG_CAN_NCT6694) += nct6694_canfd.o 14 15 obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/ 15 16 obj-$(CONFIG_CAN_UCAN) += ucan.o
+832
drivers/net/can/usb/nct6694_canfd.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* Nuvoton NCT6694 Socket CANfd driver based on USB interface. 3 + * 4 + * Copyright (C) 2025 Nuvoton Technology Corp. 5 + */ 6 + 7 + #include <linux/bitfield.h> 8 + #include <linux/can/dev.h> 9 + #include <linux/can/rx-offload.h> 10 + #include <linux/ethtool.h> 11 + #include <linux/idr.h> 12 + #include <linux/irqdomain.h> 13 + #include <linux/kernel.h> 14 + #include <linux/mfd/nct6694.h> 15 + #include <linux/module.h> 16 + #include <linux/netdevice.h> 17 + #include <linux/platform_device.h> 18 + 19 + #define DEVICE_NAME "nct6694-canfd" 20 + 21 + /* USB command module type for NCT6694 CANfd controller. 22 + * This defines the module type used for communication with the NCT6694 23 + * CANfd controller over the USB interface. 24 + */ 25 + #define NCT6694_CANFD_MOD 0x05 26 + 27 + /* Command 00h - CAN Setting and Initialization */ 28 + #define NCT6694_CANFD_SETTING 0x00 29 + #define NCT6694_CANFD_SETTING_ACTIVE_CTRL1 BIT(0) 30 + #define NCT6694_CANFD_SETTING_ACTIVE_CTRL2 BIT(1) 31 + #define NCT6694_CANFD_SETTING_ACTIVE_NBTP_DBTP BIT(2) 32 + #define NCT6694_CANFD_SETTING_CTRL1_MON BIT(0) 33 + #define NCT6694_CANFD_SETTING_CTRL1_NISO BIT(1) 34 + #define NCT6694_CANFD_SETTING_CTRL1_LBCK BIT(2) 35 + #define NCT6694_CANFD_SETTING_NBTP_NTSEG2 GENMASK(6, 0) 36 + #define NCT6694_CANFD_SETTING_NBTP_NTSEG1 GENMASK(15, 8) 37 + #define NCT6694_CANFD_SETTING_NBTP_NBRP GENMASK(24, 16) 38 + #define NCT6694_CANFD_SETTING_NBTP_NSJW GENMASK(31, 25) 39 + #define NCT6694_CANFD_SETTING_DBTP_DSJW GENMASK(3, 0) 40 + #define NCT6694_CANFD_SETTING_DBTP_DTSEG2 GENMASK(7, 4) 41 + #define NCT6694_CANFD_SETTING_DBTP_DTSEG1 GENMASK(12, 8) 42 + #define NCT6694_CANFD_SETTING_DBTP_DBRP GENMASK(20, 16) 43 + #define NCT6694_CANFD_SETTING_DBTP_TDC BIT(23) 44 + 45 + /* Command 01h - CAN Information */ 46 + #define NCT6694_CANFD_INFORMATION 0x01 47 + #define NCT6694_CANFD_INFORMATION_SEL 0x00 48 + 49 + /* Command 02h - CAN Event */ 50 + #define NCT6694_CANFD_EVENT 0x02 51 + #define NCT6694_CANFD_EVENT_SEL(idx, mask) \ 52 + ((idx ? 0x80 : 0x00) | ((mask) & 0x7F)) 53 + 54 + #define NCT6694_CANFD_EVENT_MASK GENMASK(5, 0) 55 + #define NCT6694_CANFD_EVT_TX_FIFO_EMPTY BIT(7) /* Read-clear */ 56 + #define NCT6694_CANFD_EVT_RX_DATA_LOST BIT(5) /* Read-clear */ 57 + #define NCT6694_CANFD_EVT_RX_DATA_IN BIT(7) /* Read-clear */ 58 + 59 + /* Command 10h - CAN Deliver */ 60 + #define NCT6694_CANFD_DELIVER 0x10 61 + #define NCT6694_CANFD_DELIVER_SEL(buf_cnt) \ 62 + ((buf_cnt) & 0xFF) 63 + 64 + /* Command 11h - CAN Receive */ 65 + #define NCT6694_CANFD_RECEIVE 0x11 66 + #define NCT6694_CANFD_RECEIVE_SEL(idx, buf_cnt) \ 67 + ((idx ? 0x80 : 0x00) | ((buf_cnt) & 0x7F)) 68 + 69 + #define NCT6694_CANFD_FRAME_TAG(idx) (0xC0 | (idx)) 70 + #define NCT6694_CANFD_FRAME_FLAG_EFF BIT(0) 71 + #define NCT6694_CANFD_FRAME_FLAG_RTR BIT(1) 72 + #define NCT6694_CANFD_FRAME_FLAG_FD BIT(2) 73 + #define NCT6694_CANFD_FRAME_FLAG_BRS BIT(3) 74 + #define NCT6694_CANFD_FRAME_FLAG_ERR BIT(4) 75 + 76 + #define NCT6694_NAPI_WEIGHT 32 77 + 78 + enum nct6694_event_err { 79 + NCT6694_CANFD_EVT_ERR_NO_ERROR = 0, 80 + NCT6694_CANFD_EVT_ERR_CRC_ERROR, 81 + NCT6694_CANFD_EVT_ERR_STUFF_ERROR, 82 + NCT6694_CANFD_EVT_ERR_ACK_ERROR, 83 + NCT6694_CANFD_EVT_ERR_FORM_ERROR, 84 + NCT6694_CANFD_EVT_ERR_BIT_ERROR, 85 + NCT6694_CANFD_EVT_ERR_TIMEOUT_ERROR, 86 + NCT6694_CANFD_EVT_ERR_UNKNOWN_ERROR, 87 + }; 88 + 89 + enum nct6694_event_status { 90 + NCT6694_CANFD_EVT_STS_ERROR_ACTIVE = 0, 91 + NCT6694_CANFD_EVT_STS_ERROR_PASSIVE, 92 + NCT6694_CANFD_EVT_STS_BUS_OFF, 93 + NCT6694_CANFD_EVT_STS_WARNING, 94 + }; 95 + 96 + struct __packed nct6694_canfd_setting { 97 + __le32 nbr; 98 + __le32 dbr; 99 + u8 active; 100 + u8 reserved[3]; 101 + __le16 ctrl1; 102 + __le16 ctrl2; 103 + __le32 nbtp; 104 + __le32 dbtp; 105 + }; 106 + 107 + struct __packed nct6694_canfd_information { 108 + u8 tx_fifo_cnt; 109 + u8 rx_fifo_cnt; 110 + u8 reserved[2]; 111 + __le32 can_clk; 112 + }; 113 + 114 + struct __packed nct6694_canfd_event { 115 + u8 err; 116 + u8 status; 117 + u8 tx_evt; 118 + u8 rx_evt; 119 + u8 rec; 120 + u8 tec; 121 + u8 reserved[2]; 122 + }; 123 + 124 + struct __packed nct6694_canfd_frame { 125 + u8 tag; 126 + u8 flag; 127 + u8 reserved; 128 + u8 length; 129 + __le32 id; 130 + u8 data[CANFD_MAX_DLEN]; 131 + }; 132 + 133 + struct nct6694_canfd_priv { 134 + struct can_priv can; /* must be the first member */ 135 + struct can_rx_offload offload; 136 + struct net_device *ndev; 137 + struct nct6694 *nct6694; 138 + struct workqueue_struct *wq; 139 + struct work_struct tx_work; 140 + struct nct6694_canfd_frame tx; 141 + struct nct6694_canfd_frame rx; 142 + struct nct6694_canfd_event event[2]; 143 + struct can_berr_counter bec; 144 + }; 145 + 146 + static inline struct nct6694_canfd_priv *rx_offload_to_priv(struct can_rx_offload *offload) 147 + { 148 + return container_of(offload, struct nct6694_canfd_priv, offload); 149 + } 150 + 151 + static const struct can_bittiming_const nct6694_canfd_bittiming_nominal_const = { 152 + .name = DEVICE_NAME, 153 + .tseg1_min = 1, 154 + .tseg1_max = 256, 155 + .tseg2_min = 1, 156 + .tseg2_max = 128, 157 + .sjw_max = 128, 158 + .brp_min = 1, 159 + .brp_max = 512, 160 + .brp_inc = 1, 161 + }; 162 + 163 + static const struct can_bittiming_const nct6694_canfd_bittiming_data_const = { 164 + .name = DEVICE_NAME, 165 + .tseg1_min = 1, 166 + .tseg1_max = 32, 167 + .tseg2_min = 1, 168 + .tseg2_max = 16, 169 + .sjw_max = 16, 170 + .brp_min = 1, 171 + .brp_max = 32, 172 + .brp_inc = 1, 173 + }; 174 + 175 + static void nct6694_canfd_rx_offload(struct can_rx_offload *offload, 176 + struct sk_buff *skb) 177 + { 178 + struct nct6694_canfd_priv *priv = rx_offload_to_priv(offload); 179 + int ret; 180 + 181 + ret = can_rx_offload_queue_tail(offload, skb); 182 + if (ret) 183 + priv->ndev->stats.rx_fifo_errors++; 184 + } 185 + 186 + static void nct6694_canfd_handle_lost_msg(struct net_device *ndev) 187 + { 188 + struct nct6694_canfd_priv *priv = netdev_priv(ndev); 189 + struct net_device_stats *stats = &ndev->stats; 190 + struct can_frame *cf; 191 + struct sk_buff *skb; 192 + 193 + netdev_dbg(ndev, "RX FIFO overflow, message(s) lost.\n"); 194 + 195 + stats->rx_errors++; 196 + stats->rx_over_errors++; 197 + 198 + skb = alloc_can_err_skb(ndev, &cf); 199 + if (!skb) 200 + return; 201 + 202 + cf->can_id |= CAN_ERR_CRTL; 203 + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; 204 + 205 + nct6694_canfd_rx_offload(&priv->offload, skb); 206 + } 207 + 208 + static void nct6694_canfd_handle_rx(struct net_device *ndev, u8 rx_evt) 209 + { 210 + struct net_device_stats *stats = &ndev->stats; 211 + struct nct6694_canfd_priv *priv = netdev_priv(ndev); 212 + struct nct6694_canfd_frame *frame = &priv->rx; 213 + const struct nct6694_cmd_header cmd_hd = { 214 + .mod = NCT6694_CANFD_MOD, 215 + .cmd = NCT6694_CANFD_RECEIVE, 216 + .sel = NCT6694_CANFD_RECEIVE_SEL(ndev->dev_port, 1), 217 + .len = cpu_to_le16(sizeof(*frame)) 218 + }; 219 + struct sk_buff *skb; 220 + int ret; 221 + 222 + ret = nct6694_read_msg(priv->nct6694, &cmd_hd, frame); 223 + if (ret) 224 + return; 225 + 226 + if (frame->flag & NCT6694_CANFD_FRAME_FLAG_FD) { 227 + struct canfd_frame *cfd; 228 + 229 + skb = alloc_canfd_skb(priv->ndev, &cfd); 230 + if (!skb) { 231 + stats->rx_dropped++; 232 + return; 233 + } 234 + 235 + cfd->can_id = le32_to_cpu(frame->id); 236 + cfd->len = canfd_sanitize_len(frame->length); 237 + if (frame->flag & NCT6694_CANFD_FRAME_FLAG_EFF) 238 + cfd->can_id |= CAN_EFF_FLAG; 239 + if (frame->flag & NCT6694_CANFD_FRAME_FLAG_BRS) 240 + cfd->flags |= CANFD_BRS; 241 + if (frame->flag & NCT6694_CANFD_FRAME_FLAG_ERR) 242 + cfd->flags |= CANFD_ESI; 243 + 244 + memcpy(cfd->data, frame->data, cfd->len); 245 + } else { 246 + struct can_frame *cf; 247 + 248 + skb = alloc_can_skb(priv->ndev, &cf); 249 + if (!skb) { 250 + stats->rx_dropped++; 251 + return; 252 + } 253 + 254 + cf->can_id = le32_to_cpu(frame->id); 255 + cf->len = can_cc_dlc2len(frame->length); 256 + if (frame->flag & NCT6694_CANFD_FRAME_FLAG_EFF) 257 + cf->can_id |= CAN_EFF_FLAG; 258 + 259 + if (frame->flag & NCT6694_CANFD_FRAME_FLAG_RTR) 260 + cf->can_id |= CAN_RTR_FLAG; 261 + else 262 + memcpy(cf->data, frame->data, cf->len); 263 + } 264 + 265 + nct6694_canfd_rx_offload(&priv->offload, skb); 266 + } 267 + 268 + static int nct6694_canfd_get_berr_counter(const struct net_device *ndev, 269 + struct can_berr_counter *bec) 270 + { 271 + struct nct6694_canfd_priv *priv = netdev_priv(ndev); 272 + 273 + *bec = priv->bec; 274 + 275 + return 0; 276 + } 277 + 278 + static void nct6694_canfd_handle_state_change(struct net_device *ndev, u8 status) 279 + { 280 + struct nct6694_canfd_priv *priv = netdev_priv(ndev); 281 + enum can_state new_state, rx_state, tx_state; 282 + struct can_berr_counter bec; 283 + struct can_frame *cf; 284 + struct sk_buff *skb; 285 + 286 + nct6694_canfd_get_berr_counter(ndev, &bec); 287 + can_state_get_by_berr_counter(ndev, &bec, &tx_state, &rx_state); 288 + 289 + new_state = max(tx_state, rx_state); 290 + 291 + /* state hasn't changed */ 292 + if (new_state == priv->can.state) 293 + return; 294 + 295 + skb = alloc_can_err_skb(ndev, &cf); 296 + 297 + can_change_state(ndev, cf, tx_state, rx_state); 298 + 299 + if (new_state == CAN_STATE_BUS_OFF) { 300 + can_bus_off(ndev); 301 + } else if (cf) { 302 + cf->can_id |= CAN_ERR_CNT; 303 + cf->data[6] = bec.txerr; 304 + cf->data[7] = bec.rxerr; 305 + } 306 + 307 + if (skb) 308 + nct6694_canfd_rx_offload(&priv->offload, skb); 309 + } 310 + 311 + static void nct6694_canfd_handle_bus_err(struct net_device *ndev, u8 bus_err) 312 + { 313 + struct nct6694_canfd_priv *priv = netdev_priv(ndev); 314 + struct can_frame *cf; 315 + struct sk_buff *skb; 316 + 317 + priv->can.can_stats.bus_error++; 318 + 319 + skb = alloc_can_err_skb(ndev, &cf); 320 + if (cf) 321 + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; 322 + 323 + switch (bus_err) { 324 + case NCT6694_CANFD_EVT_ERR_CRC_ERROR: 325 + netdev_dbg(ndev, "CRC error\n"); 326 + ndev->stats.rx_errors++; 327 + if (cf) 328 + cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ; 329 + break; 330 + 331 + case NCT6694_CANFD_EVT_ERR_STUFF_ERROR: 332 + netdev_dbg(ndev, "Stuff error\n"); 333 + ndev->stats.rx_errors++; 334 + if (cf) 335 + cf->data[2] |= CAN_ERR_PROT_STUFF; 336 + break; 337 + 338 + case NCT6694_CANFD_EVT_ERR_ACK_ERROR: 339 + netdev_dbg(ndev, "Ack error\n"); 340 + ndev->stats.tx_errors++; 341 + if (cf) { 342 + cf->can_id |= CAN_ERR_ACK; 343 + cf->data[2] |= CAN_ERR_PROT_TX; 344 + } 345 + break; 346 + 347 + case NCT6694_CANFD_EVT_ERR_FORM_ERROR: 348 + netdev_dbg(ndev, "Form error\n"); 349 + ndev->stats.rx_errors++; 350 + if (cf) 351 + cf->data[2] |= CAN_ERR_PROT_FORM; 352 + break; 353 + 354 + case NCT6694_CANFD_EVT_ERR_BIT_ERROR: 355 + netdev_dbg(ndev, "Bit error\n"); 356 + ndev->stats.tx_errors++; 357 + if (cf) 358 + cf->data[2] |= CAN_ERR_PROT_TX | CAN_ERR_PROT_BIT; 359 + break; 360 + 361 + default: 362 + break; 363 + } 364 + 365 + if (skb) 366 + nct6694_canfd_rx_offload(&priv->offload, skb); 367 + } 368 + 369 + static void nct6694_canfd_handle_tx(struct net_device *ndev) 370 + { 371 + struct nct6694_canfd_priv *priv = netdev_priv(ndev); 372 + struct net_device_stats *stats = &ndev->stats; 373 + 374 + stats->tx_bytes += can_rx_offload_get_echo_skb_queue_tail(&priv->offload, 375 + 0, NULL); 376 + stats->tx_packets++; 377 + netif_wake_queue(ndev); 378 + } 379 + 380 + static irqreturn_t nct6694_canfd_irq(int irq, void *data) 381 + { 382 + struct net_device *ndev = data; 383 + struct nct6694_canfd_priv *priv = netdev_priv(ndev); 384 + struct nct6694_canfd_event *event = &priv->event[ndev->dev_port]; 385 + const struct nct6694_cmd_header cmd_hd = { 386 + .mod = NCT6694_CANFD_MOD, 387 + .cmd = NCT6694_CANFD_EVENT, 388 + .sel = NCT6694_CANFD_EVENT_SEL(ndev->dev_port, NCT6694_CANFD_EVENT_MASK), 389 + .len = cpu_to_le16(sizeof(priv->event)) 390 + }; 391 + irqreturn_t handled = IRQ_NONE; 392 + int ret; 393 + 394 + ret = nct6694_read_msg(priv->nct6694, &cmd_hd, priv->event); 395 + if (ret < 0) 396 + return handled; 397 + 398 + if (event->rx_evt & NCT6694_CANFD_EVT_RX_DATA_IN) { 399 + nct6694_canfd_handle_rx(ndev, event->rx_evt); 400 + handled = IRQ_HANDLED; 401 + } 402 + 403 + if (event->rx_evt & NCT6694_CANFD_EVT_RX_DATA_LOST) { 404 + nct6694_canfd_handle_lost_msg(ndev); 405 + handled = IRQ_HANDLED; 406 + } 407 + 408 + if (event->status) { 409 + nct6694_canfd_handle_state_change(ndev, event->status); 410 + handled = IRQ_HANDLED; 411 + } 412 + 413 + if (event->err != NCT6694_CANFD_EVT_ERR_NO_ERROR) { 414 + if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) 415 + nct6694_canfd_handle_bus_err(ndev, event->err); 416 + handled = IRQ_HANDLED; 417 + } 418 + 419 + if (event->tx_evt & NCT6694_CANFD_EVT_TX_FIFO_EMPTY) { 420 + nct6694_canfd_handle_tx(ndev); 421 + handled = IRQ_HANDLED; 422 + } 423 + 424 + if (handled) 425 + can_rx_offload_threaded_irq_finish(&priv->offload); 426 + 427 + priv->bec.rxerr = event->rec; 428 + priv->bec.txerr = event->tec; 429 + 430 + return handled; 431 + } 432 + 433 + static void nct6694_canfd_tx_work(struct work_struct *work) 434 + { 435 + struct nct6694_canfd_priv *priv = container_of(work, 436 + struct nct6694_canfd_priv, 437 + tx_work); 438 + struct nct6694_canfd_frame *frame = &priv->tx; 439 + struct net_device *ndev = priv->ndev; 440 + struct net_device_stats *stats = &ndev->stats; 441 + struct sk_buff *skb = priv->can.echo_skb[0]; 442 + static const struct nct6694_cmd_header cmd_hd = { 443 + .mod = NCT6694_CANFD_MOD, 444 + .cmd = NCT6694_CANFD_DELIVER, 445 + .sel = NCT6694_CANFD_DELIVER_SEL(1), 446 + .len = cpu_to_le16(sizeof(*frame)) 447 + }; 448 + u32 txid; 449 + int err; 450 + 451 + memset(frame, 0, sizeof(*frame)); 452 + 453 + frame->tag = NCT6694_CANFD_FRAME_TAG(ndev->dev_port); 454 + 455 + if (can_is_canfd_skb(skb)) { 456 + struct canfd_frame *cfd = (struct canfd_frame *)skb->data; 457 + 458 + if (cfd->flags & CANFD_BRS) 459 + frame->flag |= NCT6694_CANFD_FRAME_FLAG_BRS; 460 + 461 + if (cfd->can_id & CAN_EFF_FLAG) { 462 + txid = cfd->can_id & CAN_EFF_MASK; 463 + frame->flag |= NCT6694_CANFD_FRAME_FLAG_EFF; 464 + } else { 465 + txid = cfd->can_id & CAN_SFF_MASK; 466 + } 467 + frame->flag |= NCT6694_CANFD_FRAME_FLAG_FD; 468 + frame->id = cpu_to_le32(txid); 469 + frame->length = canfd_sanitize_len(cfd->len); 470 + 471 + memcpy(frame->data, cfd->data, frame->length); 472 + } else { 473 + struct can_frame *cf = (struct can_frame *)skb->data; 474 + 475 + if (cf->can_id & CAN_EFF_FLAG) { 476 + txid = cf->can_id & CAN_EFF_MASK; 477 + frame->flag |= NCT6694_CANFD_FRAME_FLAG_EFF; 478 + } else { 479 + txid = cf->can_id & CAN_SFF_MASK; 480 + } 481 + 482 + if (cf->can_id & CAN_RTR_FLAG) 483 + frame->flag |= NCT6694_CANFD_FRAME_FLAG_RTR; 484 + else 485 + memcpy(frame->data, cf->data, cf->len); 486 + 487 + frame->id = cpu_to_le32(txid); 488 + frame->length = cf->len; 489 + } 490 + 491 + err = nct6694_write_msg(priv->nct6694, &cmd_hd, frame); 492 + if (err) { 493 + can_free_echo_skb(ndev, 0, NULL); 494 + stats->tx_dropped++; 495 + stats->tx_errors++; 496 + netif_wake_queue(ndev); 497 + } 498 + } 499 + 500 + static netdev_tx_t nct6694_canfd_start_xmit(struct sk_buff *skb, 501 + struct net_device *ndev) 502 + { 503 + struct nct6694_canfd_priv *priv = netdev_priv(ndev); 504 + 505 + if (can_dev_dropped_skb(ndev, skb)) 506 + return NETDEV_TX_OK; 507 + 508 + netif_stop_queue(ndev); 509 + can_put_echo_skb(skb, ndev, 0, 0); 510 + queue_work(priv->wq, &priv->tx_work); 511 + 512 + return NETDEV_TX_OK; 513 + } 514 + 515 + static int nct6694_canfd_start(struct net_device *ndev) 516 + { 517 + struct nct6694_canfd_priv *priv = netdev_priv(ndev); 518 + const struct can_bittiming *n_bt = &priv->can.bittiming; 519 + const struct can_bittiming *d_bt = &priv->can.fd.data_bittiming; 520 + struct nct6694_canfd_setting *setting __free(kfree) = NULL; 521 + const struct nct6694_cmd_header cmd_hd = { 522 + .mod = NCT6694_CANFD_MOD, 523 + .cmd = NCT6694_CANFD_SETTING, 524 + .sel = ndev->dev_port, 525 + .len = cpu_to_le16(sizeof(*setting)) 526 + }; 527 + u32 en_tdc; 528 + int ret; 529 + 530 + setting = kzalloc(sizeof(*setting), GFP_KERNEL); 531 + if (!setting) 532 + return -ENOMEM; 533 + 534 + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) 535 + setting->ctrl1 |= cpu_to_le16(NCT6694_CANFD_SETTING_CTRL1_MON); 536 + 537 + if (priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO) 538 + setting->ctrl1 |= cpu_to_le16(NCT6694_CANFD_SETTING_CTRL1_NISO); 539 + 540 + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) 541 + setting->ctrl1 |= cpu_to_le16(NCT6694_CANFD_SETTING_CTRL1_LBCK); 542 + 543 + /* Disable clock divider */ 544 + setting->ctrl2 = 0; 545 + 546 + setting->nbtp = cpu_to_le32(FIELD_PREP(NCT6694_CANFD_SETTING_NBTP_NSJW, 547 + n_bt->sjw - 1) | 548 + FIELD_PREP(NCT6694_CANFD_SETTING_NBTP_NBRP, 549 + n_bt->brp - 1) | 550 + FIELD_PREP(NCT6694_CANFD_SETTING_NBTP_NTSEG2, 551 + n_bt->phase_seg2 - 1) | 552 + FIELD_PREP(NCT6694_CANFD_SETTING_NBTP_NTSEG1, 553 + n_bt->prop_seg + n_bt->phase_seg1 - 1)); 554 + 555 + if (d_bt->brp <= 2) 556 + en_tdc = NCT6694_CANFD_SETTING_DBTP_TDC; 557 + else 558 + en_tdc = 0; 559 + 560 + setting->dbtp = cpu_to_le32(FIELD_PREP(NCT6694_CANFD_SETTING_DBTP_DSJW, 561 + d_bt->sjw - 1) | 562 + FIELD_PREP(NCT6694_CANFD_SETTING_DBTP_DBRP, 563 + d_bt->brp - 1) | 564 + FIELD_PREP(NCT6694_CANFD_SETTING_DBTP_DTSEG2, 565 + d_bt->phase_seg2 - 1) | 566 + FIELD_PREP(NCT6694_CANFD_SETTING_DBTP_DTSEG1, 567 + d_bt->prop_seg + d_bt->phase_seg1 - 1) | 568 + en_tdc); 569 + 570 + setting->active = NCT6694_CANFD_SETTING_ACTIVE_CTRL1 | 571 + NCT6694_CANFD_SETTING_ACTIVE_CTRL2 | 572 + NCT6694_CANFD_SETTING_ACTIVE_NBTP_DBTP; 573 + 574 + ret = nct6694_write_msg(priv->nct6694, &cmd_hd, setting); 575 + if (ret) 576 + return ret; 577 + 578 + priv->can.state = CAN_STATE_ERROR_ACTIVE; 579 + 580 + return 0; 581 + } 582 + 583 + static void nct6694_canfd_stop(struct net_device *ndev) 584 + { 585 + struct nct6694_canfd_priv *priv = netdev_priv(ndev); 586 + struct nct6694_canfd_setting *setting __free(kfree) = NULL; 587 + const struct nct6694_cmd_header cmd_hd = { 588 + .mod = NCT6694_CANFD_MOD, 589 + .cmd = NCT6694_CANFD_SETTING, 590 + .sel = ndev->dev_port, 591 + .len = cpu_to_le16(sizeof(*setting)) 592 + }; 593 + 594 + /* The NCT6694 cannot be stopped. To ensure safe operation and avoid 595 + * interference, the control mode is set to Listen-Only mode. This 596 + * mode allows the device to monitor bus activity without actively 597 + * participating in communication. 598 + */ 599 + setting = kzalloc(sizeof(*setting), GFP_KERNEL); 600 + if (!setting) 601 + return; 602 + 603 + nct6694_read_msg(priv->nct6694, &cmd_hd, setting); 604 + setting->ctrl1 = cpu_to_le16(NCT6694_CANFD_SETTING_CTRL1_MON); 605 + setting->active = NCT6694_CANFD_SETTING_ACTIVE_CTRL1; 606 + nct6694_write_msg(priv->nct6694, &cmd_hd, setting); 607 + 608 + priv->can.state = CAN_STATE_STOPPED; 609 + } 610 + 611 + static int nct6694_canfd_close(struct net_device *ndev) 612 + { 613 + struct nct6694_canfd_priv *priv = netdev_priv(ndev); 614 + 615 + netif_stop_queue(ndev); 616 + nct6694_canfd_stop(ndev); 617 + destroy_workqueue(priv->wq); 618 + free_irq(ndev->irq, ndev); 619 + can_rx_offload_disable(&priv->offload); 620 + close_candev(ndev); 621 + return 0; 622 + } 623 + 624 + static int nct6694_canfd_set_mode(struct net_device *ndev, enum can_mode mode) 625 + { 626 + int ret; 627 + 628 + switch (mode) { 629 + case CAN_MODE_START: 630 + ret = nct6694_canfd_start(ndev); 631 + if (ret) 632 + return ret; 633 + 634 + netif_wake_queue(ndev); 635 + break; 636 + 637 + default: 638 + return -EOPNOTSUPP; 639 + } 640 + 641 + return ret; 642 + } 643 + 644 + static int nct6694_canfd_open(struct net_device *ndev) 645 + { 646 + struct nct6694_canfd_priv *priv = netdev_priv(ndev); 647 + int ret; 648 + 649 + ret = open_candev(ndev); 650 + if (ret) 651 + return ret; 652 + 653 + can_rx_offload_enable(&priv->offload); 654 + 655 + ret = request_threaded_irq(ndev->irq, NULL, 656 + nct6694_canfd_irq, IRQF_ONESHOT, 657 + "nct6694_canfd", ndev); 658 + if (ret) { 659 + netdev_err(ndev, "Failed to request IRQ\n"); 660 + goto can_rx_offload_disable; 661 + } 662 + 663 + priv->wq = alloc_ordered_workqueue("%s-nct6694_wq", 664 + WQ_FREEZABLE | WQ_MEM_RECLAIM, 665 + ndev->name); 666 + if (!priv->wq) { 667 + ret = -ENOMEM; 668 + goto free_irq; 669 + } 670 + 671 + ret = nct6694_canfd_start(ndev); 672 + if (ret) 673 + goto destroy_wq; 674 + 675 + netif_start_queue(ndev); 676 + 677 + return 0; 678 + 679 + destroy_wq: 680 + destroy_workqueue(priv->wq); 681 + free_irq: 682 + free_irq(ndev->irq, ndev); 683 + can_rx_offload_disable: 684 + can_rx_offload_disable(&priv->offload); 685 + close_candev(ndev); 686 + return ret; 687 + } 688 + 689 + static const struct net_device_ops nct6694_canfd_netdev_ops = { 690 + .ndo_open = nct6694_canfd_open, 691 + .ndo_stop = nct6694_canfd_close, 692 + .ndo_start_xmit = nct6694_canfd_start_xmit, 693 + .ndo_change_mtu = can_change_mtu, 694 + }; 695 + 696 + static const struct ethtool_ops nct6694_canfd_ethtool_ops = { 697 + .get_ts_info = ethtool_op_get_ts_info, 698 + }; 699 + 700 + static int nct6694_canfd_get_clock(struct nct6694_canfd_priv *priv) 701 + { 702 + struct nct6694_canfd_information *info __free(kfree) = NULL; 703 + static const struct nct6694_cmd_header cmd_hd = { 704 + .mod = NCT6694_CANFD_MOD, 705 + .cmd = NCT6694_CANFD_INFORMATION, 706 + .sel = NCT6694_CANFD_INFORMATION_SEL, 707 + .len = cpu_to_le16(sizeof(*info)) 708 + }; 709 + int ret; 710 + 711 + info = kzalloc(sizeof(*info), GFP_KERNEL); 712 + if (!info) 713 + return -ENOMEM; 714 + 715 + ret = nct6694_read_msg(priv->nct6694, &cmd_hd, info); 716 + if (ret) 717 + return ret; 718 + 719 + return le32_to_cpu(info->can_clk); 720 + } 721 + 722 + static int nct6694_canfd_probe(struct platform_device *pdev) 723 + { 724 + struct nct6694 *nct6694 = dev_get_drvdata(pdev->dev.parent); 725 + struct nct6694_canfd_priv *priv; 726 + struct net_device *ndev; 727 + int port, irq, ret, can_clk; 728 + 729 + port = ida_alloc(&nct6694->canfd_ida, GFP_KERNEL); 730 + if (port < 0) 731 + return port; 732 + 733 + irq = irq_create_mapping(nct6694->domain, 734 + NCT6694_IRQ_CAN0 + port); 735 + if (!irq) { 736 + ret = -EINVAL; 737 + goto free_ida; 738 + } 739 + 740 + ndev = alloc_candev(sizeof(struct nct6694_canfd_priv), 1); 741 + if (!ndev) { 742 + ret = -ENOMEM; 743 + goto dispose_irq; 744 + } 745 + 746 + ndev->irq = irq; 747 + ndev->flags |= IFF_ECHO; 748 + ndev->dev_port = port; 749 + ndev->netdev_ops = &nct6694_canfd_netdev_ops; 750 + ndev->ethtool_ops = &nct6694_canfd_ethtool_ops; 751 + 752 + priv = netdev_priv(ndev); 753 + priv->nct6694 = nct6694; 754 + priv->ndev = ndev; 755 + 756 + can_clk = nct6694_canfd_get_clock(priv); 757 + if (can_clk < 0) { 758 + ret = dev_err_probe(&pdev->dev, can_clk, 759 + "Failed to get clock\n"); 760 + goto free_candev; 761 + } 762 + 763 + INIT_WORK(&priv->tx_work, nct6694_canfd_tx_work); 764 + 765 + priv->can.clock.freq = can_clk; 766 + priv->can.bittiming_const = &nct6694_canfd_bittiming_nominal_const; 767 + priv->can.fd.data_bittiming_const = &nct6694_canfd_bittiming_data_const; 768 + priv->can.do_set_mode = nct6694_canfd_set_mode; 769 + priv->can.do_get_berr_counter = nct6694_canfd_get_berr_counter; 770 + priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | 771 + CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_BERR_REPORTING | 772 + CAN_CTRLMODE_FD_NON_ISO; 773 + 774 + ret = can_set_static_ctrlmode(ndev, CAN_CTRLMODE_FD); 775 + if (ret) 776 + goto free_candev; 777 + 778 + ret = can_rx_offload_add_manual(ndev, &priv->offload, 779 + NCT6694_NAPI_WEIGHT); 780 + if (ret) { 781 + dev_err_probe(&pdev->dev, ret, "Failed to add rx_offload\n"); 782 + goto free_candev; 783 + } 784 + 785 + platform_set_drvdata(pdev, priv); 786 + SET_NETDEV_DEV(priv->ndev, &pdev->dev); 787 + 788 + ret = register_candev(priv->ndev); 789 + if (ret) 790 + goto rx_offload_del; 791 + 792 + return 0; 793 + 794 + rx_offload_del: 795 + can_rx_offload_del(&priv->offload); 796 + free_candev: 797 + free_candev(ndev); 798 + dispose_irq: 799 + irq_dispose_mapping(irq); 800 + free_ida: 801 + ida_free(&nct6694->canfd_ida, port); 802 + return ret; 803 + } 804 + 805 + static void nct6694_canfd_remove(struct platform_device *pdev) 806 + { 807 + struct nct6694_canfd_priv *priv = platform_get_drvdata(pdev); 808 + struct nct6694 *nct6694 = priv->nct6694; 809 + struct net_device *ndev = priv->ndev; 810 + int port = ndev->dev_port; 811 + int irq = ndev->irq; 812 + 813 + unregister_candev(ndev); 814 + can_rx_offload_del(&priv->offload); 815 + free_candev(ndev); 816 + irq_dispose_mapping(irq); 817 + ida_free(&nct6694->canfd_ida, port); 818 + } 819 + 820 + static struct platform_driver nct6694_canfd_driver = { 821 + .driver = { 822 + .name = DEVICE_NAME, 823 + }, 824 + .probe = nct6694_canfd_probe, 825 + .remove = nct6694_canfd_remove, 826 + }; 827 + 828 + module_platform_driver(nct6694_canfd_driver); 829 + 830 + MODULE_DESCRIPTION("USB-CAN FD driver for NCT6694"); 831 + MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>"); 832 + MODULE_LICENSE("GPL");
+10
drivers/rtc/Kconfig
··· 416 416 This driver can also be built as a module, if so, the module will be 417 417 called "rtc-nct3018y". 418 418 419 + config RTC_DRV_NCT6694 420 + tristate "Nuvoton NCT6694 RTC support" 421 + depends on MFD_NCT6694 422 + help 423 + If you say yes to this option, support will be included for Nuvoton 424 + NCT6694, a USB device to RTC. 425 + 426 + This driver can also be built as a module. If so, the module will 427 + be called rtc-nct6694. 428 + 419 429 config RTC_DRV_RK808 420 430 tristate "Rockchip RK805/RK808/RK809/RK817/RK818 RTC" 421 431 depends on MFD_RK8XX
+1
drivers/rtc/Makefile
··· 119 119 obj-$(CONFIG_RTC_DRV_MXC_V2) += rtc-mxc_v2.o 120 120 obj-$(CONFIG_RTC_DRV_GAMECUBE) += rtc-gamecube.o 121 121 obj-$(CONFIG_RTC_DRV_NCT3018Y) += rtc-nct3018y.o 122 + obj-$(CONFIG_RTC_DRV_NCT6694) += rtc-nct6694.o 122 123 obj-$(CONFIG_RTC_DRV_NTXEC) += rtc-ntxec.o 123 124 obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o 124 125 obj-$(CONFIG_RTC_DRV_OPAL) += rtc-opal.o
+297
drivers/rtc/rtc-nct6694.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Nuvoton NCT6694 RTC driver based on USB interface. 4 + * 5 + * Copyright (C) 2025 Nuvoton Technology Corp. 6 + */ 7 + 8 + #include <linux/bcd.h> 9 + #include <linux/irqdomain.h> 10 + #include <linux/kernel.h> 11 + #include <linux/mfd/nct6694.h> 12 + #include <linux/module.h> 13 + #include <linux/platform_device.h> 14 + #include <linux/rtc.h> 15 + #include <linux/slab.h> 16 + 17 + /* 18 + * USB command module type for NCT6694 RTC controller. 19 + * This defines the module type used for communication with the NCT6694 20 + * RTC controller over the USB interface. 21 + */ 22 + #define NCT6694_RTC_MOD 0x08 23 + 24 + /* Command 00h - RTC Time */ 25 + #define NCT6694_RTC_TIME 0x0000 26 + #define NCT6694_RTC_TIME_SEL 0x00 27 + 28 + /* Command 01h - RTC Alarm */ 29 + #define NCT6694_RTC_ALARM 0x01 30 + #define NCT6694_RTC_ALARM_SEL 0x00 31 + 32 + /* Command 02h - RTC Status */ 33 + #define NCT6694_RTC_STATUS 0x02 34 + #define NCT6694_RTC_STATUS_SEL 0x00 35 + 36 + #define NCT6694_RTC_IRQ_INT_EN BIT(0) /* Transmit a USB INT-in when RTC alarm */ 37 + #define NCT6694_RTC_IRQ_GPO_EN BIT(5) /* Trigger a GPO Low Pulse when RTC alarm */ 38 + 39 + #define NCT6694_RTC_IRQ_EN (NCT6694_RTC_IRQ_INT_EN | NCT6694_RTC_IRQ_GPO_EN) 40 + #define NCT6694_RTC_IRQ_STS BIT(0) /* Write 1 clear IRQ status */ 41 + 42 + struct __packed nct6694_rtc_time { 43 + u8 sec; 44 + u8 min; 45 + u8 hour; 46 + u8 week; 47 + u8 day; 48 + u8 month; 49 + u8 year; 50 + }; 51 + 52 + struct __packed nct6694_rtc_alarm { 53 + u8 sec; 54 + u8 min; 55 + u8 hour; 56 + u8 alarm_en; 57 + u8 alarm_pend; 58 + }; 59 + 60 + struct __packed nct6694_rtc_status { 61 + u8 irq_en; 62 + u8 irq_pend; 63 + }; 64 + 65 + union __packed nct6694_rtc_msg { 66 + struct nct6694_rtc_time time; 67 + struct nct6694_rtc_alarm alarm; 68 + struct nct6694_rtc_status sts; 69 + }; 70 + 71 + struct nct6694_rtc_data { 72 + struct nct6694 *nct6694; 73 + struct rtc_device *rtc; 74 + union nct6694_rtc_msg *msg; 75 + int irq; 76 + }; 77 + 78 + static int nct6694_rtc_read_time(struct device *dev, struct rtc_time *tm) 79 + { 80 + struct nct6694_rtc_data *data = dev_get_drvdata(dev); 81 + struct nct6694_rtc_time *time = &data->msg->time; 82 + static const struct nct6694_cmd_header cmd_hd = { 83 + .mod = NCT6694_RTC_MOD, 84 + .cmd = NCT6694_RTC_TIME, 85 + .sel = NCT6694_RTC_TIME_SEL, 86 + .len = cpu_to_le16(sizeof(*time)) 87 + }; 88 + int ret; 89 + 90 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, time); 91 + if (ret) 92 + return ret; 93 + 94 + tm->tm_sec = bcd2bin(time->sec); /* tm_sec expect 0 ~ 59 */ 95 + tm->tm_min = bcd2bin(time->min); /* tm_min expect 0 ~ 59 */ 96 + tm->tm_hour = bcd2bin(time->hour); /* tm_hour expect 0 ~ 23 */ 97 + tm->tm_wday = bcd2bin(time->week) - 1; /* tm_wday expect 0 ~ 6 */ 98 + tm->tm_mday = bcd2bin(time->day); /* tm_mday expect 1 ~ 31 */ 99 + tm->tm_mon = bcd2bin(time->month) - 1; /* tm_month expect 0 ~ 11 */ 100 + tm->tm_year = bcd2bin(time->year) + 100; /* tm_year expect since 1900 */ 101 + 102 + return ret; 103 + } 104 + 105 + static int nct6694_rtc_set_time(struct device *dev, struct rtc_time *tm) 106 + { 107 + struct nct6694_rtc_data *data = dev_get_drvdata(dev); 108 + struct nct6694_rtc_time *time = &data->msg->time; 109 + static const struct nct6694_cmd_header cmd_hd = { 110 + .mod = NCT6694_RTC_MOD, 111 + .cmd = NCT6694_RTC_TIME, 112 + .sel = NCT6694_RTC_TIME_SEL, 113 + .len = cpu_to_le16(sizeof(*time)) 114 + }; 115 + 116 + time->sec = bin2bcd(tm->tm_sec); 117 + time->min = bin2bcd(tm->tm_min); 118 + time->hour = bin2bcd(tm->tm_hour); 119 + time->week = bin2bcd(tm->tm_wday + 1); 120 + time->day = bin2bcd(tm->tm_mday); 121 + time->month = bin2bcd(tm->tm_mon + 1); 122 + time->year = bin2bcd(tm->tm_year - 100); 123 + 124 + return nct6694_write_msg(data->nct6694, &cmd_hd, time); 125 + } 126 + 127 + static int nct6694_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) 128 + { 129 + struct nct6694_rtc_data *data = dev_get_drvdata(dev); 130 + struct nct6694_rtc_alarm *alarm = &data->msg->alarm; 131 + static const struct nct6694_cmd_header cmd_hd = { 132 + .mod = NCT6694_RTC_MOD, 133 + .cmd = NCT6694_RTC_ALARM, 134 + .sel = NCT6694_RTC_ALARM_SEL, 135 + .len = cpu_to_le16(sizeof(*alarm)) 136 + }; 137 + int ret; 138 + 139 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, alarm); 140 + if (ret) 141 + return ret; 142 + 143 + alrm->time.tm_sec = bcd2bin(alarm->sec); 144 + alrm->time.tm_min = bcd2bin(alarm->min); 145 + alrm->time.tm_hour = bcd2bin(alarm->hour); 146 + alrm->enabled = alarm->alarm_en; 147 + alrm->pending = alarm->alarm_pend; 148 + 149 + return ret; 150 + } 151 + 152 + static int nct6694_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) 153 + { 154 + struct nct6694_rtc_data *data = dev_get_drvdata(dev); 155 + struct nct6694_rtc_alarm *alarm = &data->msg->alarm; 156 + static const struct nct6694_cmd_header cmd_hd = { 157 + .mod = NCT6694_RTC_MOD, 158 + .cmd = NCT6694_RTC_ALARM, 159 + .sel = NCT6694_RTC_ALARM_SEL, 160 + .len = cpu_to_le16(sizeof(*alarm)) 161 + }; 162 + 163 + alarm->sec = bin2bcd(alrm->time.tm_sec); 164 + alarm->min = bin2bcd(alrm->time.tm_min); 165 + alarm->hour = bin2bcd(alrm->time.tm_hour); 166 + alarm->alarm_en = alrm->enabled ? NCT6694_RTC_IRQ_EN : 0; 167 + alarm->alarm_pend = 0; 168 + 169 + return nct6694_write_msg(data->nct6694, &cmd_hd, alarm); 170 + } 171 + 172 + static int nct6694_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) 173 + { 174 + struct nct6694_rtc_data *data = dev_get_drvdata(dev); 175 + struct nct6694_rtc_status *sts = &data->msg->sts; 176 + static const struct nct6694_cmd_header cmd_hd = { 177 + .mod = NCT6694_RTC_MOD, 178 + .cmd = NCT6694_RTC_STATUS, 179 + .sel = NCT6694_RTC_STATUS_SEL, 180 + .len = cpu_to_le16(sizeof(*sts)) 181 + }; 182 + 183 + if (enabled) 184 + sts->irq_en |= NCT6694_RTC_IRQ_EN; 185 + else 186 + sts->irq_en &= ~NCT6694_RTC_IRQ_EN; 187 + 188 + sts->irq_pend = 0; 189 + 190 + return nct6694_write_msg(data->nct6694, &cmd_hd, sts); 191 + } 192 + 193 + static const struct rtc_class_ops nct6694_rtc_ops = { 194 + .read_time = nct6694_rtc_read_time, 195 + .set_time = nct6694_rtc_set_time, 196 + .read_alarm = nct6694_rtc_read_alarm, 197 + .set_alarm = nct6694_rtc_set_alarm, 198 + .alarm_irq_enable = nct6694_rtc_alarm_irq_enable, 199 + }; 200 + 201 + static irqreturn_t nct6694_irq(int irq, void *dev_id) 202 + { 203 + struct nct6694_rtc_data *data = dev_id; 204 + struct nct6694_rtc_status *sts = &data->msg->sts; 205 + static const struct nct6694_cmd_header cmd_hd = { 206 + .mod = NCT6694_RTC_MOD, 207 + .cmd = NCT6694_RTC_STATUS, 208 + .sel = NCT6694_RTC_STATUS_SEL, 209 + .len = cpu_to_le16(sizeof(*sts)) 210 + }; 211 + int ret; 212 + 213 + rtc_lock(data->rtc); 214 + 215 + sts->irq_en = NCT6694_RTC_IRQ_EN; 216 + sts->irq_pend = NCT6694_RTC_IRQ_STS; 217 + ret = nct6694_write_msg(data->nct6694, &cmd_hd, sts); 218 + if (ret) { 219 + rtc_unlock(data->rtc); 220 + return IRQ_NONE; 221 + } 222 + 223 + rtc_update_irq(data->rtc, 1, RTC_IRQF | RTC_AF); 224 + 225 + rtc_unlock(data->rtc); 226 + 227 + return IRQ_HANDLED; 228 + } 229 + 230 + static void nct6694_irq_dispose_mapping(void *d) 231 + { 232 + struct nct6694_rtc_data *data = d; 233 + 234 + irq_dispose_mapping(data->irq); 235 + } 236 + 237 + static int nct6694_rtc_probe(struct platform_device *pdev) 238 + { 239 + struct nct6694_rtc_data *data; 240 + struct nct6694 *nct6694 = dev_get_drvdata(pdev->dev.parent); 241 + int ret; 242 + 243 + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 244 + if (!data) 245 + return -ENOMEM; 246 + 247 + data->msg = devm_kzalloc(&pdev->dev, sizeof(union nct6694_rtc_msg), 248 + GFP_KERNEL); 249 + if (!data->msg) 250 + return -ENOMEM; 251 + 252 + data->irq = irq_create_mapping(nct6694->domain, NCT6694_IRQ_RTC); 253 + if (!data->irq) 254 + return -EINVAL; 255 + 256 + ret = devm_add_action_or_reset(&pdev->dev, nct6694_irq_dispose_mapping, 257 + data); 258 + if (ret) 259 + return ret; 260 + 261 + ret = devm_device_init_wakeup(&pdev->dev); 262 + if (ret) 263 + return dev_err_probe(&pdev->dev, ret, "Failed to init wakeup\n"); 264 + 265 + data->rtc = devm_rtc_allocate_device(&pdev->dev); 266 + if (IS_ERR(data->rtc)) 267 + return PTR_ERR(data->rtc); 268 + 269 + data->nct6694 = nct6694; 270 + data->rtc->ops = &nct6694_rtc_ops; 271 + data->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; 272 + data->rtc->range_max = RTC_TIMESTAMP_END_2099; 273 + 274 + platform_set_drvdata(pdev, data); 275 + 276 + ret = devm_request_threaded_irq(&pdev->dev, data->irq, NULL, 277 + nct6694_irq, IRQF_ONESHOT, 278 + "rtc-nct6694", data); 279 + if (ret < 0) 280 + return dev_err_probe(&pdev->dev, ret, "Failed to request irq\n"); 281 + 282 + return devm_rtc_register_device(data->rtc); 283 + } 284 + 285 + static struct platform_driver nct6694_rtc_driver = { 286 + .driver = { 287 + .name = "nct6694-rtc", 288 + }, 289 + .probe = nct6694_rtc_probe, 290 + }; 291 + 292 + module_platform_driver(nct6694_rtc_driver); 293 + 294 + MODULE_DESCRIPTION("USB-RTC driver for NCT6694"); 295 + MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>"); 296 + MODULE_LICENSE("GPL"); 297 + MODULE_ALIAS("platform:nct6694-rtc");
+11
drivers/watchdog/Kconfig
··· 760 760 MAX77620 chips. To compile this driver as a module, 761 761 choose M here: the module will be called max77620_wdt. 762 762 763 + config NCT6694_WATCHDOG 764 + tristate "Nuvoton NCT6694 watchdog support" 765 + depends on MFD_NCT6694 766 + select WATCHDOG_CORE 767 + help 768 + Say Y here to support Nuvoton NCT6694 watchdog timer 769 + functionality. 770 + 771 + This driver can also be built as a module. If so, the module 772 + will be called nct6694_wdt. 773 + 763 774 config IMX2_WDT 764 775 tristate "IMX2+ Watchdog" 765 776 depends on ARCH_MXC || ARCH_LAYERSCAPE || COMPILE_TEST
+1
drivers/watchdog/Makefile
··· 235 235 obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o 236 236 obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o 237 237 obj-$(CONFIG_MAX77620_WATCHDOG) += max77620_wdt.o 238 + obj-$(CONFIG_NCT6694_WATCHDOG) += nct6694_wdt.o 238 239 obj-$(CONFIG_ZIIRAVE_WATCHDOG) += ziirave_wdt.o 239 240 obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o 240 241 obj-$(CONFIG_MENF21BMC_WATCHDOG) += menf21bmc_wdt.o
+307
drivers/watchdog/nct6694_wdt.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Nuvoton NCT6694 WDT driver based on USB interface. 4 + * 5 + * Copyright (C) 2025 Nuvoton Technology Corp. 6 + */ 7 + 8 + #include <linux/idr.h> 9 + #include <linux/kernel.h> 10 + #include <linux/mfd/nct6694.h> 11 + #include <linux/module.h> 12 + #include <linux/platform_device.h> 13 + #include <linux/slab.h> 14 + #include <linux/watchdog.h> 15 + 16 + #define DEVICE_NAME "nct6694-wdt" 17 + 18 + #define NCT6694_DEFAULT_TIMEOUT 10 19 + #define NCT6694_DEFAULT_PRETIMEOUT 0 20 + 21 + #define NCT6694_WDT_MAX_DEVS 2 22 + 23 + /* 24 + * USB command module type for NCT6694 WDT controller. 25 + * This defines the module type used for communication with the NCT6694 26 + * WDT controller over the USB interface. 27 + */ 28 + #define NCT6694_WDT_MOD 0x07 29 + 30 + /* Command 00h - WDT Setup */ 31 + #define NCT6694_WDT_SETUP 0x00 32 + #define NCT6694_WDT_SETUP_SEL(idx) (idx ? 0x01 : 0x00) 33 + 34 + /* Command 01h - WDT Command */ 35 + #define NCT6694_WDT_COMMAND 0x01 36 + #define NCT6694_WDT_COMMAND_SEL(idx) (idx ? 0x01 : 0x00) 37 + 38 + static unsigned int timeout[NCT6694_WDT_MAX_DEVS] = { 39 + [0 ... (NCT6694_WDT_MAX_DEVS - 1)] = NCT6694_DEFAULT_TIMEOUT 40 + }; 41 + module_param_array(timeout, int, NULL, 0644); 42 + MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds"); 43 + 44 + static unsigned int pretimeout[NCT6694_WDT_MAX_DEVS] = { 45 + [0 ... (NCT6694_WDT_MAX_DEVS - 1)] = NCT6694_DEFAULT_PRETIMEOUT 46 + }; 47 + module_param_array(pretimeout, int, NULL, 0644); 48 + MODULE_PARM_DESC(pretimeout, "Watchdog pre-timeout in seconds"); 49 + 50 + static bool nowayout = WATCHDOG_NOWAYOUT; 51 + module_param(nowayout, bool, 0); 52 + MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 53 + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 54 + 55 + enum { 56 + NCT6694_ACTION_NONE = 0, 57 + NCT6694_ACTION_SIRQ, 58 + NCT6694_ACTION_GPO, 59 + }; 60 + 61 + struct __packed nct6694_wdt_setup { 62 + __le32 pretimeout; 63 + __le32 timeout; 64 + u8 owner; 65 + u8 scratch; 66 + u8 control; 67 + u8 status; 68 + __le32 countdown; 69 + }; 70 + 71 + struct __packed nct6694_wdt_cmd { 72 + __le32 wdt_cmd; 73 + __le32 reserved; 74 + }; 75 + 76 + union __packed nct6694_wdt_msg { 77 + struct nct6694_wdt_setup setup; 78 + struct nct6694_wdt_cmd cmd; 79 + }; 80 + 81 + struct nct6694_wdt_data { 82 + struct watchdog_device wdev; 83 + struct device *dev; 84 + struct nct6694 *nct6694; 85 + union nct6694_wdt_msg *msg; 86 + unsigned char wdev_idx; 87 + }; 88 + 89 + static int nct6694_wdt_setting(struct watchdog_device *wdev, 90 + u32 timeout_val, u8 timeout_act, 91 + u32 pretimeout_val, u8 pretimeout_act) 92 + { 93 + struct nct6694_wdt_data *data = watchdog_get_drvdata(wdev); 94 + struct nct6694_wdt_setup *setup = &data->msg->setup; 95 + const struct nct6694_cmd_header cmd_hd = { 96 + .mod = NCT6694_WDT_MOD, 97 + .cmd = NCT6694_WDT_SETUP, 98 + .sel = NCT6694_WDT_SETUP_SEL(data->wdev_idx), 99 + .len = cpu_to_le16(sizeof(*setup)) 100 + }; 101 + unsigned int timeout_fmt, pretimeout_fmt; 102 + 103 + if (pretimeout_val == 0) 104 + pretimeout_act = NCT6694_ACTION_NONE; 105 + 106 + timeout_fmt = (timeout_val * 1000) | (timeout_act << 24); 107 + pretimeout_fmt = (pretimeout_val * 1000) | (pretimeout_act << 24); 108 + 109 + memset(setup, 0, sizeof(*setup)); 110 + setup->timeout = cpu_to_le32(timeout_fmt); 111 + setup->pretimeout = cpu_to_le32(pretimeout_fmt); 112 + 113 + return nct6694_write_msg(data->nct6694, &cmd_hd, setup); 114 + } 115 + 116 + static int nct6694_wdt_start(struct watchdog_device *wdev) 117 + { 118 + struct nct6694_wdt_data *data = watchdog_get_drvdata(wdev); 119 + int ret; 120 + 121 + ret = nct6694_wdt_setting(wdev, wdev->timeout, NCT6694_ACTION_GPO, 122 + wdev->pretimeout, NCT6694_ACTION_GPO); 123 + if (ret) 124 + return ret; 125 + 126 + dev_dbg(data->dev, "Setting WDT(%d): timeout = %d, pretimeout = %d\n", 127 + data->wdev_idx, wdev->timeout, wdev->pretimeout); 128 + 129 + return ret; 130 + } 131 + 132 + static int nct6694_wdt_stop(struct watchdog_device *wdev) 133 + { 134 + struct nct6694_wdt_data *data = watchdog_get_drvdata(wdev); 135 + struct nct6694_wdt_cmd *cmd = &data->msg->cmd; 136 + const struct nct6694_cmd_header cmd_hd = { 137 + .mod = NCT6694_WDT_MOD, 138 + .cmd = NCT6694_WDT_COMMAND, 139 + .sel = NCT6694_WDT_COMMAND_SEL(data->wdev_idx), 140 + .len = cpu_to_le16(sizeof(*cmd)) 141 + }; 142 + 143 + memcpy(&cmd->wdt_cmd, "WDTC", 4); 144 + cmd->reserved = 0; 145 + 146 + return nct6694_write_msg(data->nct6694, &cmd_hd, cmd); 147 + } 148 + 149 + static int nct6694_wdt_ping(struct watchdog_device *wdev) 150 + { 151 + struct nct6694_wdt_data *data = watchdog_get_drvdata(wdev); 152 + struct nct6694_wdt_cmd *cmd = &data->msg->cmd; 153 + const struct nct6694_cmd_header cmd_hd = { 154 + .mod = NCT6694_WDT_MOD, 155 + .cmd = NCT6694_WDT_COMMAND, 156 + .sel = NCT6694_WDT_COMMAND_SEL(data->wdev_idx), 157 + .len = cpu_to_le16(sizeof(*cmd)) 158 + }; 159 + 160 + memcpy(&cmd->wdt_cmd, "WDTS", 4); 161 + cmd->reserved = 0; 162 + 163 + return nct6694_write_msg(data->nct6694, &cmd_hd, cmd); 164 + } 165 + 166 + static int nct6694_wdt_set_timeout(struct watchdog_device *wdev, 167 + unsigned int new_timeout) 168 + { 169 + int ret; 170 + 171 + ret = nct6694_wdt_setting(wdev, new_timeout, NCT6694_ACTION_GPO, 172 + wdev->pretimeout, NCT6694_ACTION_GPO); 173 + if (ret) 174 + return ret; 175 + 176 + wdev->timeout = new_timeout; 177 + 178 + return 0; 179 + } 180 + 181 + static int nct6694_wdt_set_pretimeout(struct watchdog_device *wdev, 182 + unsigned int new_pretimeout) 183 + { 184 + int ret; 185 + 186 + ret = nct6694_wdt_setting(wdev, wdev->timeout, NCT6694_ACTION_GPO, 187 + new_pretimeout, NCT6694_ACTION_GPO); 188 + if (ret) 189 + return ret; 190 + 191 + wdev->pretimeout = new_pretimeout; 192 + 193 + return 0; 194 + } 195 + 196 + static unsigned int nct6694_wdt_get_time(struct watchdog_device *wdev) 197 + { 198 + struct nct6694_wdt_data *data = watchdog_get_drvdata(wdev); 199 + struct nct6694_wdt_setup *setup = &data->msg->setup; 200 + const struct nct6694_cmd_header cmd_hd = { 201 + .mod = NCT6694_WDT_MOD, 202 + .cmd = NCT6694_WDT_SETUP, 203 + .sel = NCT6694_WDT_SETUP_SEL(data->wdev_idx), 204 + .len = cpu_to_le16(sizeof(*setup)) 205 + }; 206 + unsigned int timeleft_ms; 207 + int ret; 208 + 209 + ret = nct6694_read_msg(data->nct6694, &cmd_hd, setup); 210 + if (ret) 211 + return 0; 212 + 213 + timeleft_ms = le32_to_cpu(setup->countdown); 214 + 215 + return timeleft_ms / 1000; 216 + } 217 + 218 + static const struct watchdog_info nct6694_wdt_info = { 219 + .options = WDIOF_SETTIMEOUT | 220 + WDIOF_KEEPALIVEPING | 221 + WDIOF_MAGICCLOSE | 222 + WDIOF_PRETIMEOUT, 223 + .identity = DEVICE_NAME, 224 + }; 225 + 226 + static const struct watchdog_ops nct6694_wdt_ops = { 227 + .owner = THIS_MODULE, 228 + .start = nct6694_wdt_start, 229 + .stop = nct6694_wdt_stop, 230 + .set_timeout = nct6694_wdt_set_timeout, 231 + .set_pretimeout = nct6694_wdt_set_pretimeout, 232 + .get_timeleft = nct6694_wdt_get_time, 233 + .ping = nct6694_wdt_ping, 234 + }; 235 + 236 + static void nct6694_wdt_ida_free(void *d) 237 + { 238 + struct nct6694_wdt_data *data = d; 239 + struct nct6694 *nct6694 = data->nct6694; 240 + 241 + ida_free(&nct6694->wdt_ida, data->wdev_idx); 242 + } 243 + 244 + static int nct6694_wdt_probe(struct platform_device *pdev) 245 + { 246 + struct device *dev = &pdev->dev; 247 + struct nct6694 *nct6694 = dev_get_drvdata(dev->parent); 248 + struct nct6694_wdt_data *data; 249 + struct watchdog_device *wdev; 250 + int ret; 251 + 252 + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 253 + if (!data) 254 + return -ENOMEM; 255 + 256 + data->msg = devm_kzalloc(dev, sizeof(union nct6694_wdt_msg), 257 + GFP_KERNEL); 258 + if (!data->msg) 259 + return -ENOMEM; 260 + 261 + data->dev = dev; 262 + data->nct6694 = nct6694; 263 + 264 + ret = ida_alloc(&nct6694->wdt_ida, GFP_KERNEL); 265 + if (ret < 0) 266 + return ret; 267 + data->wdev_idx = ret; 268 + 269 + ret = devm_add_action_or_reset(dev, nct6694_wdt_ida_free, data); 270 + if (ret) 271 + return ret; 272 + 273 + wdev = &data->wdev; 274 + wdev->info = &nct6694_wdt_info; 275 + wdev->ops = &nct6694_wdt_ops; 276 + wdev->timeout = timeout[data->wdev_idx]; 277 + wdev->pretimeout = pretimeout[data->wdev_idx]; 278 + if (timeout[data->wdev_idx] < pretimeout[data->wdev_idx]) { 279 + dev_warn(data->dev, "pretimeout < timeout. Setting to zero\n"); 280 + wdev->pretimeout = 0; 281 + } 282 + 283 + wdev->min_timeout = 1; 284 + wdev->max_timeout = 255; 285 + 286 + platform_set_drvdata(pdev, data); 287 + 288 + watchdog_set_drvdata(&data->wdev, data); 289 + watchdog_set_nowayout(&data->wdev, nowayout); 290 + watchdog_stop_on_reboot(&data->wdev); 291 + 292 + return devm_watchdog_register_device(dev, &data->wdev); 293 + } 294 + 295 + static struct platform_driver nct6694_wdt_driver = { 296 + .driver = { 297 + .name = DEVICE_NAME, 298 + }, 299 + .probe = nct6694_wdt_probe, 300 + }; 301 + 302 + module_platform_driver(nct6694_wdt_driver); 303 + 304 + MODULE_DESCRIPTION("USB-WDT driver for NCT6694"); 305 + MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>"); 306 + MODULE_LICENSE("GPL"); 307 + MODULE_ALIAS("platform:nct6694-wdt");
+102
include/linux/mfd/nct6694.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Copyright (C) 2025 Nuvoton Technology Corp. 4 + * 5 + * Nuvoton NCT6694 USB transaction and data structure. 6 + */ 7 + 8 + #ifndef __MFD_NCT6694_H 9 + #define __MFD_NCT6694_H 10 + 11 + #define NCT6694_VENDOR_ID 0x0416 12 + #define NCT6694_PRODUCT_ID 0x200B 13 + #define NCT6694_INT_IN_EP 0x81 14 + #define NCT6694_BULK_IN_EP 0x02 15 + #define NCT6694_BULK_OUT_EP 0x03 16 + 17 + #define NCT6694_HCTRL_SET 0x40 18 + #define NCT6694_HCTRL_GET 0x80 19 + 20 + #define NCT6694_URB_TIMEOUT 1000 21 + 22 + enum nct6694_irq_id { 23 + NCT6694_IRQ_GPIO0 = 0, 24 + NCT6694_IRQ_GPIO1, 25 + NCT6694_IRQ_GPIO2, 26 + NCT6694_IRQ_GPIO3, 27 + NCT6694_IRQ_GPIO4, 28 + NCT6694_IRQ_GPIO5, 29 + NCT6694_IRQ_GPIO6, 30 + NCT6694_IRQ_GPIO7, 31 + NCT6694_IRQ_GPIO8, 32 + NCT6694_IRQ_GPIO9, 33 + NCT6694_IRQ_GPIOA, 34 + NCT6694_IRQ_GPIOB, 35 + NCT6694_IRQ_GPIOC, 36 + NCT6694_IRQ_GPIOD, 37 + NCT6694_IRQ_GPIOE, 38 + NCT6694_IRQ_GPIOF, 39 + NCT6694_IRQ_CAN0, 40 + NCT6694_IRQ_CAN1, 41 + NCT6694_IRQ_RTC, 42 + NCT6694_NR_IRQS, 43 + }; 44 + 45 + enum nct6694_response_err_status { 46 + NCT6694_NO_ERROR = 0, 47 + NCT6694_FORMAT_ERROR, 48 + NCT6694_RESERVED1, 49 + NCT6694_RESERVED2, 50 + NCT6694_NOT_SUPPORT_ERROR, 51 + NCT6694_NO_RESPONSE_ERROR, 52 + NCT6694_TIMEOUT_ERROR, 53 + NCT6694_PENDING, 54 + }; 55 + 56 + struct __packed nct6694_cmd_header { 57 + u8 rsv1; 58 + u8 mod; 59 + union __packed { 60 + __le16 offset; 61 + struct __packed { 62 + u8 cmd; 63 + u8 sel; 64 + }; 65 + }; 66 + u8 hctrl; 67 + u8 rsv2; 68 + __le16 len; 69 + }; 70 + 71 + struct __packed nct6694_response_header { 72 + u8 sequence_id; 73 + u8 sts; 74 + u8 reserved[4]; 75 + __le16 len; 76 + }; 77 + 78 + union __packed nct6694_usb_msg { 79 + struct nct6694_cmd_header cmd_header; 80 + struct nct6694_response_header response_header; 81 + }; 82 + 83 + struct nct6694 { 84 + struct device *dev; 85 + struct ida gpio_ida; 86 + struct ida i2c_ida; 87 + struct ida canfd_ida; 88 + struct ida wdt_ida; 89 + struct irq_domain *domain; 90 + struct mutex access_lock; 91 + spinlock_t irq_lock; 92 + struct urb *int_in_urb; 93 + struct usb_device *udev; 94 + union nct6694_usb_msg *usb_msg; 95 + __le32 *int_buffer; 96 + unsigned int irq_enable; 97 + }; 98 + 99 + int nct6694_read_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *cmd_hd, void *buf); 100 + int nct6694_write_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *cmd_hd, void *buf); 101 + 102 + #endif