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 branches 'ib-mfd-char-crypto-6.18', 'ib-mfd-gpio-6.18', 'ib-mfd-gpio-hwmon-i2c-can-rtc-watchdog-6.18', 'ib-mfd-gpio-input-pinctrl-pwm-6.18', 'ib-mfd-input-6.18', 'ib-mfd-input-rtc-6.18' and 'ib-mfd-power-regulator-6.18' into ibs-for-mfd-merged

Lee Jones e8c81284 017bdcdb

+7042 -33
+83
Documentation/devicetree/bindings/gpio/maxim,max7360-gpio.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/gpio/maxim,max7360-gpio.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: Maxim MAX7360 GPIO controller 8 + 9 + maintainers: 10 + - Kamel Bouhara <kamel.bouhara@bootlin.com> 11 + - Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> 12 + 13 + description: | 14 + Maxim MAX7360 GPIO controller, in MAX7360 chipset 15 + https://www.analog.com/en/products/max7360.html 16 + 17 + The device provides two series of GPIOs, referred here as GPIOs and GPOs. 18 + 19 + PORT0 to PORT7 pins can be used as GPIOs, with support for interrupts and 20 + constant-current mode. These pins will also be used by the rotary encoder and 21 + PWM functionalities. 22 + 23 + COL2 to COL7 pins can be used as GPOs, there is no input capability. COL pins 24 + will be partitioned, with the first pins being affected to the keypad 25 + functionality and the last ones as GPOs. 26 + 27 + properties: 28 + compatible: 29 + enum: 30 + - maxim,max7360-gpio 31 + - maxim,max7360-gpo 32 + 33 + gpio-controller: true 34 + 35 + "#gpio-cells": 36 + const: 2 37 + 38 + interrupt-controller: true 39 + 40 + "#interrupt-cells": 41 + const: 2 42 + 43 + maxim,constant-current-disable: 44 + $ref: /schemas/types.yaml#/definitions/uint32 45 + description: 46 + Bit field, each bit disables constant-current output of the associated 47 + GPIO, starting from the least significant bit for the first GPIO. 48 + maximum: 0xff 49 + 50 + required: 51 + - compatible 52 + - gpio-controller 53 + 54 + allOf: 55 + - if: 56 + properties: 57 + compatible: 58 + contains: 59 + enum: 60 + - maxim,max7360-gpio 61 + ngpios: false 62 + then: 63 + required: 64 + - interrupt-controller 65 + else: 66 + properties: 67 + interrupt-controller: false 68 + maxim,constant-current-disable: false 69 + 70 + additionalProperties: false 71 + 72 + examples: 73 + - | 74 + gpio { 75 + compatible = "maxim,max7360-gpio"; 76 + 77 + gpio-controller; 78 + #gpio-cells = <2>; 79 + maxim,constant-current-disable = <0x06>; 80 + 81 + interrupt-controller; 82 + #interrupt-cells = <2>; 83 + };
+191
Documentation/devicetree/bindings/mfd/maxim,max7360.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/mfd/maxim,max7360.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: Maxim MAX7360 Keypad, Rotary encoder, PWM and GPIO controller 8 + 9 + maintainers: 10 + - Kamel Bouhara <kamel.bouhara@bootlin.com> 11 + - Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> 12 + 13 + description: | 14 + Maxim MAX7360 device, with following functions: 15 + - keypad controller 16 + - rotary controller 17 + - GPIO and GPO controller 18 + - PWM controller 19 + 20 + https://www.analog.com/en/products/max7360.html 21 + 22 + allOf: 23 + - $ref: /schemas/input/matrix-keymap.yaml# 24 + - $ref: /schemas/input/input.yaml# 25 + 26 + properties: 27 + compatible: 28 + enum: 29 + - maxim,max7360 30 + 31 + reg: 32 + maxItems: 1 33 + 34 + interrupts: 35 + maxItems: 2 36 + 37 + interrupt-names: 38 + items: 39 + - const: inti 40 + - const: intk 41 + 42 + keypad-debounce-delay-ms: 43 + description: Keypad debounce delay in ms 44 + minimum: 9 45 + maximum: 40 46 + default: 9 47 + 48 + rotary-debounce-delay-ms: 49 + description: Rotary encoder debounce delay in ms 50 + minimum: 0 51 + maximum: 15 52 + default: 0 53 + 54 + linux,axis: 55 + $ref: /schemas/input/rotary-encoder.yaml#/properties/linux,axis 56 + 57 + rotary-encoder,relative-axis: 58 + $ref: /schemas/types.yaml#/definitions/flag 59 + description: 60 + Register a relative axis rather than an absolute one. 61 + 62 + rotary-encoder,steps: 63 + $ref: /schemas/types.yaml#/definitions/uint32 64 + default: 24 65 + description: 66 + Number of steps in a full turnaround of the 67 + encoder. Only relevant for absolute axis. Defaults to 24 which is a 68 + typical value for such devices. 69 + 70 + rotary-encoder,rollover: 71 + $ref: /schemas/types.yaml#/definitions/flag 72 + description: 73 + Automatic rollover when the rotary value becomes 74 + greater than the specified steps or smaller than 0. For absolute axis only. 75 + 76 + "#pwm-cells": 77 + const: 3 78 + 79 + gpio: 80 + $ref: /schemas/gpio/maxim,max7360-gpio.yaml# 81 + description: 82 + PORT0 to PORT7 general purpose input/output pins configuration. 83 + 84 + gpo: 85 + $ref: /schemas/gpio/maxim,max7360-gpio.yaml# 86 + description: > 87 + COL2 to COL7 general purpose output pins configuration. Allows to use 88 + unused keypad columns as outputs. 89 + 90 + The MAX7360 has 8 column lines and 6 of them can be used as GPOs. GPIOs 91 + numbers used for this gpio-controller node do correspond to the column 92 + numbers: values 0 and 1 are never valid, values from 2 to 7 might be valid 93 + depending on the value of the keypad,num-column property. 94 + 95 + patternProperties: 96 + '-pins$': 97 + type: object 98 + description: 99 + Pinctrl node's client devices use subnodes for desired pin configuration. 100 + Client device subnodes use below standard properties. 101 + $ref: /schemas/pinctrl/pincfg-node.yaml 102 + 103 + properties: 104 + pins: 105 + description: 106 + List of gpio pins affected by the properties specified in this 107 + subnode. 108 + items: 109 + pattern: '^(PORT[0-7]|ROTARY)$' 110 + minItems: 1 111 + maxItems: 8 112 + 113 + function: 114 + description: 115 + Specify the alternative function to be configured for the specified 116 + pins. 117 + enum: [gpio, pwm, rotary] 118 + 119 + additionalProperties: false 120 + 121 + required: 122 + - compatible 123 + - reg 124 + - interrupts 125 + - interrupt-names 126 + - linux,keymap 127 + - linux,axis 128 + - "#pwm-cells" 129 + - gpio 130 + - gpo 131 + 132 + unevaluatedProperties: false 133 + 134 + examples: 135 + - | 136 + #include <dt-bindings/input/input.h> 137 + #include <dt-bindings/interrupt-controller/arm-gic.h> 138 + 139 + i2c { 140 + #address-cells = <1>; 141 + #size-cells = <0>; 142 + 143 + io-expander@38 { 144 + compatible = "maxim,max7360"; 145 + reg = <0x38>; 146 + 147 + interrupt-parent = <&gpio1>; 148 + interrupts = <23 IRQ_TYPE_LEVEL_LOW>, 149 + <24 IRQ_TYPE_LEVEL_LOW>; 150 + interrupt-names = "inti", "intk"; 151 + 152 + keypad,num-rows = <8>; 153 + keypad,num-columns = <4>; 154 + linux,keymap = < 155 + MATRIX_KEY(0x00, 0x00, KEY_F5) 156 + MATRIX_KEY(0x01, 0x00, KEY_F4) 157 + MATRIX_KEY(0x02, 0x01, KEY_F6) 158 + >; 159 + keypad-debounce-delay-ms = <10>; 160 + autorepeat; 161 + 162 + rotary-debounce-delay-ms = <2>; 163 + linux,axis = <0>; /* REL_X */ 164 + rotary-encoder,relative-axis; 165 + 166 + #pwm-cells = <3>; 167 + 168 + max7360_gpio: gpio { 169 + compatible = "maxim,max7360-gpio"; 170 + 171 + gpio-controller; 172 + #gpio-cells = <2>; 173 + maxim,constant-current-disable = <0x06>; 174 + 175 + interrupt-controller; 176 + #interrupt-cells = <0x2>; 177 + }; 178 + 179 + max7360_gpo: gpo { 180 + compatible = "maxim,max7360-gpo"; 181 + 182 + gpio-controller; 183 + #gpio-cells = <2>; 184 + }; 185 + 186 + backlight_pins: backlight-pins { 187 + pins = "PORT2"; 188 + function = "pwm"; 189 + }; 190 + }; 191 + };
+117
Documentation/devicetree/bindings/mfd/ti,bq25703a.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/mfd/ti,bq25703a.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: BQ25703A Charger Manager/Buck/Boost Converter 8 + 9 + maintainers: 10 + - Chris Morgan <macromorgan@hotmail.com> 11 + 12 + allOf: 13 + - $ref: /schemas/power/supply/power-supply.yaml# 14 + 15 + properties: 16 + compatible: 17 + const: ti,bq25703a 18 + 19 + reg: 20 + const: 0x6b 21 + 22 + input-current-limit-microamp: 23 + description: 24 + Maximum total input current allowed used for both charging and 25 + powering the device. 26 + minimum: 50000 27 + maximum: 6400000 28 + default: 3250000 29 + 30 + interrupts: 31 + maxItems: 1 32 + 33 + monitored-battery: 34 + description: 35 + A minimum of constant-charge-current-max-microamp, 36 + constant-charge-voltage-max-microvolt, and 37 + voltage-min-design-microvolt are required. 38 + 39 + regulators: 40 + type: object 41 + additionalProperties: false 42 + description: 43 + Boost converter regulator output of bq257xx. 44 + 45 + properties: 46 + vbus: 47 + type: object 48 + $ref: /schemas/regulator/regulator.yaml 49 + additionalProperties: false 50 + 51 + properties: 52 + regulator-name: true 53 + regulator-min-microamp: 54 + minimum: 0 55 + maximum: 6350000 56 + regulator-max-microamp: 57 + minimum: 0 58 + maximum: 6350000 59 + regulator-min-microvolt: 60 + minimum: 4480000 61 + maximum: 20800000 62 + regulator-max-microvolt: 63 + minimum: 4480000 64 + maximum: 20800000 65 + enable-gpios: 66 + description: 67 + The BQ25703 may require both a register write and a GPIO 68 + toggle to enable the boost regulator. 69 + 70 + required: 71 + - regulator-name 72 + - regulator-min-microamp 73 + - regulator-max-microamp 74 + - regulator-min-microvolt 75 + - regulator-max-microvolt 76 + 77 + unevaluatedProperties: false 78 + 79 + required: 80 + - compatible 81 + - reg 82 + - input-current-limit-microamp 83 + - monitored-battery 84 + - power-supplies 85 + 86 + examples: 87 + - | 88 + #include <dt-bindings/gpio/gpio.h> 89 + #include <dt-bindings/interrupt-controller/irq.h> 90 + #include <dt-bindings/pinctrl/rockchip.h> 91 + i2c { 92 + #address-cells = <1>; 93 + #size-cells = <0>; 94 + 95 + bq25703: charger@6b { 96 + compatible = "ti,bq25703a"; 97 + reg = <0x6b>; 98 + input-current-limit-microamp = <5000000>; 99 + interrupt-parent = <&gpio0>; 100 + interrupts = <RK_PD5 IRQ_TYPE_LEVEL_LOW>; 101 + monitored-battery = <&battery>; 102 + power-supplies = <&fusb302>; 103 + 104 + regulators { 105 + usb_otg_vbus: vbus { 106 + enable-gpios = <&gpio4 RK_PA6 GPIO_ACTIVE_HIGH>; 107 + regulator-max-microamp = <960000>; 108 + regulator-max-microvolt = <5088000>; 109 + regulator-min-microamp = <512000>; 110 + regulator-min-microvolt = <4992000>; 111 + regulator-name = "usb_otg_vbus"; 112 + }; 113 + }; 114 + }; 115 + }; 116 + 117 + ...
+25
MAINTAINERS
··· 15022 15022 S: Maintained 15023 15023 F: drivers/iio/temperature/max30208.c 15024 15024 15025 + MAXIM MAX7360 KEYPAD LED MFD DRIVER 15026 + M: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> 15027 + S: Maintained 15028 + F: Documentation/devicetree/bindings/gpio/maxim,max7360-gpio.yaml 15029 + F: Documentation/devicetree/bindings/mfd/maxim,max7360.yaml 15030 + F: drivers/gpio/gpio-max7360.c 15031 + F: drivers/input/keyboard/max7360-keypad.c 15032 + F: drivers/input/misc/max7360-rotary.c 15033 + F: drivers/mfd/max7360.c 15034 + F: drivers/pinctrl/pinctrl-max7360.c 15035 + F: drivers/pwm/pwm-max7360.c 15036 + F: include/linux/mfd/max7360.h 15037 + 15025 15038 MAXIM MAX77650 PMIC MFD DRIVER 15026 15039 M: Bartosz Golaszewski <brgl@bgdev.pl> 15027 15040 L: linux-kernel@vger.kernel.org ··· 18103 18090 F: drivers/nubus/ 18104 18091 F: include/linux/nubus.h 18105 18092 F: include/uapi/linux/nubus.h 18093 + 18094 + NUVOTON NCT6694 MFD DRIVER 18095 + M: Ming Yu <tmyu0@nuvoton.com> 18096 + S: Supported 18097 + F: drivers/gpio/gpio-nct6694.c 18098 + F: drivers/hwmon/nct6694-hwmon.c 18099 + F: drivers/i2c/busses/i2c-nct6694.c 18100 + F: drivers/mfd/nct6694.c 18101 + F: drivers/net/can/usb/nct6694_canfd.c 18102 + F: drivers/rtc/rtc-nct6694.c 18103 + F: drivers/watchdog/nct6694_wdt.c 18104 + F: include/linux/mfd/nct6694.h 18106 18105 18107 18106 NUVOTON NCT7201 IIO DRIVER 18108 18107 M: Eason Yang <j2anfernee@gmail.com>
+24
drivers/gpio/Kconfig
··· 1492 1492 help 1493 1493 Support for GPIOs on Cirrus Logic Madera class codecs. 1494 1494 1495 + config GPIO_MAX7360 1496 + tristate "MAX7360 GPIO support" 1497 + depends on MFD_MAX7360 1498 + select GPIO_REGMAP 1499 + select REGMAP_IRQ 1500 + help 1501 + Allows to use MAX7360 I/O Expander PWM lines as GPIO and keypad COL 1502 + lines as GPO. 1503 + 1504 + This driver can also be built as a module. If so, the module will be 1505 + called gpio-max7360. 1506 + 1495 1507 config GPIO_MAX77620 1496 1508 tristate "GPIO support for PMIC MAX77620 and MAX20024" 1497 1509 depends on MFD_MAX77620 ··· 1533 1521 1534 1522 This driver can also be built as a module. If so, the module will be 1535 1523 called gpio-max77759. 1524 + 1525 + config GPIO_NCT6694 1526 + tristate "Nuvoton NCT6694 GPIO controller support" 1527 + depends on MFD_NCT6694 1528 + select GENERIC_IRQ_CHIP 1529 + select GPIOLIB_IRQCHIP 1530 + help 1531 + This driver supports 8 GPIO pins per bank that can all be interrupt 1532 + sources. 1533 + 1534 + This driver can also be built as a module. If so, the module will be 1535 + called gpio-nct6694. 1536 1536 1537 1537 config GPIO_PALMAS 1538 1538 tristate "TI PALMAS series PMICs GPIO"
+2
drivers/gpio/Makefile
··· 106 106 obj-$(CONFIG_GPIO_MAX7301) += gpio-max7301.o 107 107 obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o 108 108 obj-$(CONFIG_GPIO_MAX732X) += gpio-max732x.o 109 + obj-$(CONFIG_GPIO_MAX7360) += gpio-max7360.o 109 110 obj-$(CONFIG_GPIO_MAX77620) += gpio-max77620.o 110 111 obj-$(CONFIG_GPIO_MAX77650) += gpio-max77650.o 111 112 obj-$(CONFIG_GPIO_MAX77759) += gpio-max77759.o ··· 129 128 obj-$(CONFIG_GPIO_MVEBU) += gpio-mvebu.o 130 129 obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o 131 130 obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o 131 + obj-$(CONFIG_GPIO_NCT6694) += gpio-nct6694.o 132 132 obj-$(CONFIG_GPIO_NOMADIK) += gpio-nomadik.o 133 133 obj-$(CONFIG_GPIO_NPCM_SGPIO) += gpio-npcm-sgpio.o 134 134 obj-$(CONFIG_GPIO_OCTEON) += gpio-octeon.o
+257
drivers/gpio/gpio-max7360.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright 2025 Bootlin 4 + * 5 + * Author: Kamel BOUHARA <kamel.bouhara@bootlin.com> 6 + * Author: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> 7 + */ 8 + 9 + #include <linux/bitfield.h> 10 + #include <linux/bitmap.h> 11 + #include <linux/err.h> 12 + #include <linux/gpio/driver.h> 13 + #include <linux/gpio/regmap.h> 14 + #include <linux/init.h> 15 + #include <linux/interrupt.h> 16 + #include <linux/mfd/max7360.h> 17 + #include <linux/minmax.h> 18 + #include <linux/mod_devicetable.h> 19 + #include <linux/module.h> 20 + #include <linux/platform_device.h> 21 + #include <linux/property.h> 22 + #include <linux/regmap.h> 23 + 24 + #define MAX7360_GPIO_PORT 1 25 + #define MAX7360_GPIO_COL 2 26 + 27 + struct max7360_gpio_plat_data { 28 + unsigned int function; 29 + }; 30 + 31 + static struct max7360_gpio_plat_data max7360_gpio_port_plat = { .function = MAX7360_GPIO_PORT }; 32 + static struct max7360_gpio_plat_data max7360_gpio_col_plat = { .function = MAX7360_GPIO_COL }; 33 + 34 + static int max7360_get_available_gpos(struct device *dev, unsigned int *available_gpios) 35 + { 36 + u32 columns; 37 + int ret; 38 + 39 + ret = device_property_read_u32(dev->parent, "keypad,num-columns", &columns); 40 + if (ret) { 41 + dev_err(dev, "Failed to read columns count\n"); 42 + return ret; 43 + } 44 + 45 + *available_gpios = min(MAX7360_MAX_GPO, MAX7360_MAX_KEY_COLS - columns); 46 + 47 + return 0; 48 + } 49 + 50 + static int max7360_gpo_init_valid_mask(struct gpio_chip *gc, 51 + unsigned long *valid_mask, 52 + unsigned int ngpios) 53 + { 54 + unsigned int available_gpios; 55 + int ret; 56 + 57 + ret = max7360_get_available_gpos(gc->parent, &available_gpios); 58 + if (ret) 59 + return ret; 60 + 61 + bitmap_clear(valid_mask, 0, MAX7360_MAX_KEY_COLS - available_gpios); 62 + 63 + return 0; 64 + } 65 + 66 + static int max7360_set_gpos_count(struct device *dev, struct regmap *regmap) 67 + { 68 + /* 69 + * MAX7360 COL0 to COL7 pins can be used either as keypad columns, 70 + * general purpose output or a mix of both. 71 + * By default, all pins are used as keypad, here we update this 72 + * configuration to allow to use some of them as GPIOs. 73 + */ 74 + unsigned int available_gpios; 75 + unsigned int val; 76 + int ret; 77 + 78 + ret = max7360_get_available_gpos(dev, &available_gpios); 79 + if (ret) 80 + return ret; 81 + 82 + /* 83 + * Configure which GPIOs will be used for keypad. 84 + * MAX7360_REG_DEBOUNCE contains configuration both for keypad debounce 85 + * timings and gpos/keypad columns repartition. Only the later is 86 + * modified here. 87 + */ 88 + val = FIELD_PREP(MAX7360_PORTS, available_gpios); 89 + ret = regmap_write_bits(regmap, MAX7360_REG_DEBOUNCE, MAX7360_PORTS, val); 90 + if (ret) 91 + dev_err(dev, "Failed to write max7360 columns/gpos configuration"); 92 + 93 + return ret; 94 + } 95 + 96 + static int max7360_gpio_reg_mask_xlate(struct gpio_regmap *gpio, 97 + unsigned int base, unsigned int offset, 98 + unsigned int *reg, unsigned int *mask) 99 + { 100 + if (base == MAX7360_REG_PWMBASE) { 101 + /* 102 + * GPIO output is using PWM duty cycle registers: one register 103 + * per line, with value being either 0 or 255. 104 + */ 105 + *reg = base + offset; 106 + *mask = GENMASK(7, 0); 107 + } else { 108 + *reg = base; 109 + *mask = BIT(offset); 110 + } 111 + 112 + return 0; 113 + } 114 + 115 + static const struct regmap_irq max7360_regmap_irqs[MAX7360_MAX_GPIO] = { 116 + REGMAP_IRQ_REG(0, 0, BIT(0)), 117 + REGMAP_IRQ_REG(1, 0, BIT(1)), 118 + REGMAP_IRQ_REG(2, 0, BIT(2)), 119 + REGMAP_IRQ_REG(3, 0, BIT(3)), 120 + REGMAP_IRQ_REG(4, 0, BIT(4)), 121 + REGMAP_IRQ_REG(5, 0, BIT(5)), 122 + REGMAP_IRQ_REG(6, 0, BIT(6)), 123 + REGMAP_IRQ_REG(7, 0, BIT(7)), 124 + }; 125 + 126 + static int max7360_handle_mask_sync(const int index, 127 + const unsigned int mask_buf_def, 128 + const unsigned int mask_buf, 129 + void *const irq_drv_data) 130 + { 131 + struct regmap *regmap = irq_drv_data; 132 + int ret; 133 + 134 + for (unsigned int i = 0; i < MAX7360_MAX_GPIO; i++) { 135 + ret = regmap_assign_bits(regmap, MAX7360_REG_PWMCFG(i), 136 + MAX7360_PORT_CFG_INTERRUPT_MASK, mask_buf & BIT(i)); 137 + if (ret) 138 + return ret; 139 + } 140 + 141 + return 0; 142 + } 143 + 144 + static int max7360_gpio_probe(struct platform_device *pdev) 145 + { 146 + const struct max7360_gpio_plat_data *plat_data; 147 + struct gpio_regmap_config gpio_config = { }; 148 + struct regmap_irq_chip *irq_chip; 149 + struct device *dev = &pdev->dev; 150 + struct regmap *regmap; 151 + unsigned int outconf; 152 + int ret; 153 + 154 + regmap = dev_get_regmap(dev->parent, NULL); 155 + if (!regmap) 156 + return dev_err_probe(dev, -ENODEV, "could not get parent regmap\n"); 157 + 158 + plat_data = device_get_match_data(dev); 159 + if (plat_data->function == MAX7360_GPIO_PORT) { 160 + if (device_property_read_bool(dev, "interrupt-controller")) { 161 + /* 162 + * Port GPIOs with interrupt-controller property: add IRQ 163 + * controller. 164 + */ 165 + gpio_config.regmap_irq_flags = IRQF_ONESHOT | IRQF_SHARED; 166 + gpio_config.regmap_irq_line = 167 + fwnode_irq_get_byname(dev_fwnode(dev->parent), "inti"); 168 + if (gpio_config.regmap_irq_line < 0) 169 + return dev_err_probe(dev, gpio_config.regmap_irq_line, 170 + "Failed to get IRQ\n"); 171 + 172 + /* Create custom IRQ configuration. */ 173 + irq_chip = devm_kzalloc(dev, sizeof(*irq_chip), GFP_KERNEL); 174 + gpio_config.regmap_irq_chip = irq_chip; 175 + if (!irq_chip) 176 + return -ENOMEM; 177 + 178 + irq_chip->name = dev_name(dev); 179 + irq_chip->status_base = MAX7360_REG_GPIOIN; 180 + irq_chip->status_is_level = true; 181 + irq_chip->num_regs = 1; 182 + irq_chip->num_irqs = MAX7360_MAX_GPIO; 183 + irq_chip->irqs = max7360_regmap_irqs; 184 + irq_chip->handle_mask_sync = max7360_handle_mask_sync; 185 + irq_chip->irq_drv_data = regmap; 186 + 187 + for (unsigned int i = 0; i < MAX7360_MAX_GPIO; i++) { 188 + ret = regmap_write_bits(regmap, MAX7360_REG_PWMCFG(i), 189 + MAX7360_PORT_CFG_INTERRUPT_EDGES, 190 + MAX7360_PORT_CFG_INTERRUPT_EDGES); 191 + if (ret) 192 + return dev_err_probe(dev, ret, 193 + "Failed to enable interrupts\n"); 194 + } 195 + } 196 + 197 + /* 198 + * Port GPIOs: set output mode configuration (constant-current or not). 199 + * This property is optional. 200 + */ 201 + ret = device_property_read_u32(dev, "maxim,constant-current-disable", &outconf); 202 + if (!ret) { 203 + ret = regmap_write(regmap, MAX7360_REG_GPIOOUTM, outconf); 204 + if (ret) 205 + return dev_err_probe(dev, ret, 206 + "Failed to set constant-current configuration\n"); 207 + } 208 + } 209 + 210 + /* Add gpio device. */ 211 + gpio_config.parent = dev; 212 + gpio_config.regmap = regmap; 213 + if (plat_data->function == MAX7360_GPIO_PORT) { 214 + gpio_config.ngpio = MAX7360_MAX_GPIO; 215 + gpio_config.reg_dat_base = GPIO_REGMAP_ADDR(MAX7360_REG_GPIOIN); 216 + gpio_config.reg_set_base = GPIO_REGMAP_ADDR(MAX7360_REG_PWMBASE); 217 + gpio_config.reg_dir_out_base = GPIO_REGMAP_ADDR(MAX7360_REG_GPIOCTRL); 218 + gpio_config.ngpio_per_reg = MAX7360_MAX_GPIO; 219 + gpio_config.reg_mask_xlate = max7360_gpio_reg_mask_xlate; 220 + } else { 221 + ret = max7360_set_gpos_count(dev, regmap); 222 + if (ret) 223 + return dev_err_probe(dev, ret, "Failed to set GPOS pin count\n"); 224 + 225 + gpio_config.reg_set_base = GPIO_REGMAP_ADDR(MAX7360_REG_PORTS); 226 + gpio_config.ngpio = MAX7360_MAX_KEY_COLS; 227 + gpio_config.init_valid_mask = max7360_gpo_init_valid_mask; 228 + } 229 + 230 + return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config)); 231 + } 232 + 233 + static const struct of_device_id max7360_gpio_of_match[] = { 234 + { 235 + .compatible = "maxim,max7360-gpo", 236 + .data = &max7360_gpio_col_plat 237 + }, { 238 + .compatible = "maxim,max7360-gpio", 239 + .data = &max7360_gpio_port_plat 240 + }, { 241 + } 242 + }; 243 + MODULE_DEVICE_TABLE(of, max7360_gpio_of_match); 244 + 245 + static struct platform_driver max7360_gpio_driver = { 246 + .driver = { 247 + .name = "max7360-gpio", 248 + .of_match_table = max7360_gpio_of_match, 249 + }, 250 + .probe = max7360_gpio_probe, 251 + }; 252 + module_platform_driver(max7360_gpio_driver); 253 + 254 + MODULE_DESCRIPTION("MAX7360 GPIO driver"); 255 + MODULE_AUTHOR("Kamel BOUHARA <kamel.bouhara@bootlin.com>"); 256 + MODULE_AUTHOR("Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>"); 257 + MODULE_LICENSE("GPL");
+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");
+28 -2
drivers/gpio/gpio-regmap.c
··· 32 32 unsigned int reg_dir_in_base; 33 33 unsigned int reg_dir_out_base; 34 34 35 + #ifdef CONFIG_REGMAP_IRQ 36 + int regmap_irq_line; 37 + struct regmap_irq_chip_data *irq_chip_data; 38 + #endif 39 + 35 40 int (*reg_mask_xlate)(struct gpio_regmap *gpio, unsigned int base, 36 41 unsigned int offset, unsigned int *reg, 37 42 unsigned int *mask); ··· 220 215 */ 221 216 struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config) 222 217 { 218 + struct irq_domain *irq_domain; 223 219 struct gpio_regmap *gpio; 224 220 struct gpio_chip *chip; 225 221 int ret; ··· 261 255 chip->names = config->names; 262 256 chip->label = config->label ?: dev_name(config->parent); 263 257 chip->can_sleep = regmap_might_sleep(config->regmap); 258 + chip->init_valid_mask = config->init_valid_mask; 264 259 265 260 chip->request = gpiochip_generic_request; 266 261 chip->free = gpiochip_generic_free; ··· 302 295 if (ret < 0) 303 296 goto err_free_gpio; 304 297 305 - if (config->irq_domain) { 306 - ret = gpiochip_irqchip_add_domain(chip, config->irq_domain); 298 + #ifdef CONFIG_REGMAP_IRQ 299 + if (config->regmap_irq_chip) { 300 + gpio->regmap_irq_line = config->regmap_irq_line; 301 + ret = regmap_add_irq_chip_fwnode(dev_fwnode(config->parent), config->regmap, 302 + config->regmap_irq_line, config->regmap_irq_flags, 303 + 0, config->regmap_irq_chip, &gpio->irq_chip_data); 304 + if (ret) 305 + goto err_free_gpio; 306 + 307 + irq_domain = regmap_irq_get_domain(gpio->irq_chip_data); 308 + } else 309 + #endif 310 + irq_domain = config->irq_domain; 311 + 312 + if (irq_domain) { 313 + ret = gpiochip_irqchip_add_domain(chip, irq_domain); 307 314 if (ret) 308 315 goto err_remove_gpiochip; 309 316 } ··· 338 317 */ 339 318 void gpio_regmap_unregister(struct gpio_regmap *gpio) 340 319 { 320 + #ifdef CONFIG_REGMAP_IRQ 321 + if (gpio->irq_chip_data) 322 + regmap_del_irq_chip(gpio->regmap_irq_line, gpio->irq_chip_data); 323 + #endif 324 + 341 325 gpiochip_remove(&gpio->gpio_chip); 342 326 kfree(gpio); 343 327 }
+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");
+12
drivers/input/keyboard/Kconfig
··· 422 422 To compile this driver as a module, choose M here: the 423 423 module will be called max7359_keypad. 424 424 425 + config KEYBOARD_MAX7360 426 + tristate "Maxim MAX7360 Key Switch Controller" 427 + select INPUT_MATRIXKMAP 428 + depends on I2C 429 + depends on MFD_MAX7360 430 + help 431 + If you say yes here you get support for the keypad controller on the 432 + Maxim MAX7360 I/O Expander. 433 + 434 + To compile this driver as a module, choose M here: the module will be 435 + called max7360_keypad. 436 + 425 437 config KEYBOARD_MPR121 426 438 tristate "Freescale MPR121 Touchkey" 427 439 depends on I2C
+1
drivers/input/keyboard/Makefile
··· 42 42 obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o 43 43 obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o 44 44 obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o 45 + obj-$(CONFIG_KEYBOARD_MAX7360) += max7360-keypad.o 45 46 obj-$(CONFIG_KEYBOARD_MPR121) += mpr121_touchkey.o 46 47 obj-$(CONFIG_KEYBOARD_MT6779) += mt6779-keypad.o 47 48 obj-$(CONFIG_KEYBOARD_MTK_PMIC) += mtk-pmic-keys.o
+308
drivers/input/keyboard/max7360-keypad.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright 2025 Bootlin 4 + * 5 + * Author: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> 6 + */ 7 + 8 + #include <linux/bitfield.h> 9 + #include <linux/bitops.h> 10 + #include <linux/dev_printk.h> 11 + #include <linux/device/devres.h> 12 + #include <linux/err.h> 13 + #include <linux/init.h> 14 + #include <linux/input.h> 15 + #include <linux/input/matrix_keypad.h> 16 + #include <linux/interrupt.h> 17 + #include <linux/mfd/max7360.h> 18 + #include <linux/mod_devicetable.h> 19 + #include <linux/minmax.h> 20 + #include <linux/module.h> 21 + #include <linux/property.h> 22 + #include <linux/platform_device.h> 23 + #include <linux/pm_wakeirq.h> 24 + #include <linux/regmap.h> 25 + 26 + struct max7360_keypad { 27 + struct input_dev *input; 28 + unsigned int rows; 29 + unsigned int cols; 30 + unsigned int debounce_ms; 31 + int irq; 32 + struct regmap *regmap; 33 + unsigned short keycodes[MAX7360_MAX_KEY_ROWS * MAX7360_MAX_KEY_COLS]; 34 + }; 35 + 36 + static irqreturn_t max7360_keypad_irq(int irq, void *data) 37 + { 38 + struct max7360_keypad *max7360_keypad = data; 39 + struct device *dev = max7360_keypad->input->dev.parent; 40 + unsigned int val; 41 + unsigned int row, col; 42 + unsigned int release; 43 + unsigned int code; 44 + int error; 45 + 46 + error = regmap_read(max7360_keypad->regmap, MAX7360_REG_KEYFIFO, &val); 47 + if (error) { 48 + dev_err(dev, "Failed to read MAX7360 FIFO"); 49 + return IRQ_NONE; 50 + } 51 + 52 + /* FIFO overflow: ignore it and get next event. */ 53 + if (val == MAX7360_FIFO_OVERFLOW) { 54 + dev_warn(dev, "max7360 FIFO overflow"); 55 + error = regmap_read_poll_timeout(max7360_keypad->regmap, MAX7360_REG_KEYFIFO, 56 + val, val != MAX7360_FIFO_OVERFLOW, 0, 1000); 57 + if (error) { 58 + dev_err(dev, "Failed to empty MAX7360 FIFO"); 59 + return IRQ_NONE; 60 + } 61 + } 62 + 63 + if (val == MAX7360_FIFO_EMPTY) { 64 + dev_dbg(dev, "Got a spurious interrupt"); 65 + 66 + return IRQ_NONE; 67 + } 68 + 69 + row = FIELD_GET(MAX7360_FIFO_ROW, val); 70 + col = FIELD_GET(MAX7360_FIFO_COL, val); 71 + release = val & MAX7360_FIFO_RELEASE; 72 + 73 + code = MATRIX_SCAN_CODE(row, col, get_count_order(max7360_keypad->cols)); 74 + 75 + dev_dbg(dev, "key[%d:%d] %s\n", row, col, release ? "release" : "press"); 76 + 77 + input_event(max7360_keypad->input, EV_MSC, MSC_SCAN, code); 78 + input_report_key(max7360_keypad->input, max7360_keypad->keycodes[code], !release); 79 + input_sync(max7360_keypad->input); 80 + 81 + return IRQ_HANDLED; 82 + } 83 + 84 + static int max7360_keypad_open(struct input_dev *pdev) 85 + { 86 + struct max7360_keypad *max7360_keypad = input_get_drvdata(pdev); 87 + struct device *dev = max7360_keypad->input->dev.parent; 88 + int error; 89 + 90 + /* Somebody is using the device: get out of sleep. */ 91 + error = regmap_write_bits(max7360_keypad->regmap, MAX7360_REG_CONFIG, 92 + MAX7360_CFG_SLEEP, MAX7360_CFG_SLEEP); 93 + if (error) 94 + dev_err(dev, "Failed to write max7360 configuration: %d\n", error); 95 + 96 + return error; 97 + } 98 + 99 + static void max7360_keypad_close(struct input_dev *pdev) 100 + { 101 + struct max7360_keypad *max7360_keypad = input_get_drvdata(pdev); 102 + struct device *dev = max7360_keypad->input->dev.parent; 103 + int error; 104 + 105 + /* Nobody is using the device anymore: go to sleep. */ 106 + error = regmap_write_bits(max7360_keypad->regmap, MAX7360_REG_CONFIG, MAX7360_CFG_SLEEP, 0); 107 + if (error) 108 + dev_err(dev, "Failed to write max7360 configuration: %d\n", error); 109 + } 110 + 111 + static int max7360_keypad_hw_init(struct max7360_keypad *max7360_keypad) 112 + { 113 + struct device *dev = max7360_keypad->input->dev.parent; 114 + unsigned int val; 115 + int error; 116 + 117 + val = max7360_keypad->debounce_ms - MAX7360_DEBOUNCE_MIN; 118 + error = regmap_write_bits(max7360_keypad->regmap, MAX7360_REG_DEBOUNCE, 119 + MAX7360_DEBOUNCE, 120 + FIELD_PREP(MAX7360_DEBOUNCE, val)); 121 + if (error) 122 + return dev_err_probe(dev, error, 123 + "Failed to write max7360 debounce configuration\n"); 124 + 125 + error = regmap_write_bits(max7360_keypad->regmap, MAX7360_REG_INTERRUPT, 126 + MAX7360_INTERRUPT_TIME_MASK, 127 + FIELD_PREP(MAX7360_INTERRUPT_TIME_MASK, 1)); 128 + if (error) 129 + return dev_err_probe(dev, error, 130 + "Failed to write max7360 keypad interrupt configuration\n"); 131 + 132 + return 0; 133 + } 134 + 135 + static int max7360_keypad_build_keymap(struct max7360_keypad *max7360_keypad) 136 + { 137 + struct input_dev *input_dev = max7360_keypad->input; 138 + struct device *dev = input_dev->dev.parent->parent; 139 + struct matrix_keymap_data keymap_data; 140 + const char *propname = "linux,keymap"; 141 + unsigned int max_keys; 142 + int error; 143 + int size; 144 + 145 + size = device_property_count_u32(dev, propname); 146 + if (size <= 0) { 147 + dev_err(dev, "missing or malformed property %s: %d\n", propname, size); 148 + return size < 0 ? size : -EINVAL; 149 + } 150 + 151 + max_keys = max7360_keypad->cols * max7360_keypad->rows; 152 + if (size > max_keys) { 153 + dev_err(dev, "%s size overflow (%d vs max %u)\n", propname, size, max_keys); 154 + return -EINVAL; 155 + } 156 + 157 + u32 *keys __free(kfree) = kmalloc_array(size, sizeof(*keys), GFP_KERNEL); 158 + if (!keys) 159 + return -ENOMEM; 160 + 161 + error = device_property_read_u32_array(dev, propname, keys, size); 162 + if (error) { 163 + dev_err(dev, "failed to read %s property: %d\n", propname, error); 164 + return error; 165 + } 166 + 167 + keymap_data.keymap = keys; 168 + keymap_data.keymap_size = size; 169 + error = matrix_keypad_build_keymap(&keymap_data, NULL, 170 + max7360_keypad->rows, max7360_keypad->cols, 171 + max7360_keypad->keycodes, max7360_keypad->input); 172 + if (error) 173 + return error; 174 + 175 + return 0; 176 + } 177 + 178 + static int max7360_keypad_parse_fw(struct device *dev, 179 + struct max7360_keypad *max7360_keypad, 180 + bool *autorepeat) 181 + { 182 + int error; 183 + 184 + error = matrix_keypad_parse_properties(dev->parent, &max7360_keypad->rows, 185 + &max7360_keypad->cols); 186 + if (error) 187 + return error; 188 + 189 + if (!max7360_keypad->rows || !max7360_keypad->cols || 190 + max7360_keypad->rows > MAX7360_MAX_KEY_ROWS || 191 + max7360_keypad->cols > MAX7360_MAX_KEY_COLS) { 192 + dev_err(dev, "Invalid number of columns or rows (%ux%u)\n", 193 + max7360_keypad->cols, max7360_keypad->rows); 194 + return -EINVAL; 195 + } 196 + 197 + *autorepeat = device_property_read_bool(dev->parent, "autorepeat"); 198 + 199 + max7360_keypad->debounce_ms = MAX7360_DEBOUNCE_MIN; 200 + error = device_property_read_u32(dev->parent, "keypad-debounce-delay-ms", 201 + &max7360_keypad->debounce_ms); 202 + if (error == -EINVAL) { 203 + dev_info(dev, "Using default keypad-debounce-delay-ms: %u\n", 204 + max7360_keypad->debounce_ms); 205 + } else if (error < 0) { 206 + dev_err(dev, "Failed to read keypad-debounce-delay-ms property\n"); 207 + return error; 208 + } 209 + 210 + if (!in_range(max7360_keypad->debounce_ms, MAX7360_DEBOUNCE_MIN, 211 + MAX7360_DEBOUNCE_MAX - MAX7360_DEBOUNCE_MIN + 1)) { 212 + dev_err(dev, "Invalid keypad-debounce-delay-ms: %u, should be between %u and %u.\n", 213 + max7360_keypad->debounce_ms, MAX7360_DEBOUNCE_MIN, MAX7360_DEBOUNCE_MAX); 214 + return -EINVAL; 215 + } 216 + 217 + return 0; 218 + } 219 + 220 + static int max7360_keypad_probe(struct platform_device *pdev) 221 + { 222 + struct max7360_keypad *max7360_keypad; 223 + struct device *dev = &pdev->dev; 224 + struct input_dev *input; 225 + struct regmap *regmap; 226 + bool autorepeat; 227 + int error; 228 + int irq; 229 + 230 + regmap = dev_get_regmap(dev->parent, NULL); 231 + if (!regmap) 232 + return dev_err_probe(dev, -ENODEV, "Could not get parent regmap\n"); 233 + 234 + irq = fwnode_irq_get_byname(dev_fwnode(dev->parent), "intk"); 235 + if (irq < 0) 236 + return dev_err_probe(dev, irq, "Failed to get IRQ\n"); 237 + 238 + max7360_keypad = devm_kzalloc(dev, sizeof(*max7360_keypad), GFP_KERNEL); 239 + if (!max7360_keypad) 240 + return -ENOMEM; 241 + 242 + max7360_keypad->regmap = regmap; 243 + 244 + error = max7360_keypad_parse_fw(dev, max7360_keypad, &autorepeat); 245 + if (error) 246 + return error; 247 + 248 + input = devm_input_allocate_device(dev); 249 + if (!input) 250 + return -ENOMEM; 251 + 252 + max7360_keypad->input = input; 253 + 254 + input->id.bustype = BUS_I2C; 255 + input->name = pdev->name; 256 + input->open = max7360_keypad_open; 257 + input->close = max7360_keypad_close; 258 + 259 + error = max7360_keypad_build_keymap(max7360_keypad); 260 + if (error) 261 + return dev_err_probe(dev, error, "Failed to build keymap\n"); 262 + 263 + input_set_capability(input, EV_MSC, MSC_SCAN); 264 + if (autorepeat) 265 + __set_bit(EV_REP, input->evbit); 266 + 267 + input_set_drvdata(input, max7360_keypad); 268 + 269 + error = devm_request_threaded_irq(dev, irq, NULL, max7360_keypad_irq, 270 + IRQF_ONESHOT, 271 + "max7360-keypad", max7360_keypad); 272 + if (error) 273 + return dev_err_probe(dev, error, "Failed to register interrupt\n"); 274 + 275 + error = input_register_device(input); 276 + if (error) 277 + return dev_err_probe(dev, error, "Could not register input device\n"); 278 + 279 + error = max7360_keypad_hw_init(max7360_keypad); 280 + if (error) 281 + return dev_err_probe(dev, error, "Failed to initialize max7360 keypad\n"); 282 + 283 + device_init_wakeup(dev, true); 284 + error = dev_pm_set_wake_irq(dev, irq); 285 + if (error) 286 + dev_warn(dev, "Failed to set up wakeup irq: %d\n", error); 287 + 288 + return 0; 289 + } 290 + 291 + static void max7360_keypad_remove(struct platform_device *pdev) 292 + { 293 + dev_pm_clear_wake_irq(&pdev->dev); 294 + device_init_wakeup(&pdev->dev, false); 295 + } 296 + 297 + static struct platform_driver max7360_keypad_driver = { 298 + .driver = { 299 + .name = "max7360-keypad", 300 + }, 301 + .probe = max7360_keypad_probe, 302 + .remove = max7360_keypad_remove, 303 + }; 304 + module_platform_driver(max7360_keypad_driver); 305 + 306 + MODULE_DESCRIPTION("MAX7360 Keypad driver"); 307 + MODULE_AUTHOR("Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>"); 308 + MODULE_LICENSE("GPL");
+20
drivers/input/misc/Kconfig
··· 230 230 tristate "M68k Beeper support" 231 231 depends on M68K 232 232 233 + config INPUT_MAX7360_ROTARY 234 + tristate "Maxim MAX7360 Rotary Encoder" 235 + depends on MFD_MAX7360 236 + help 237 + If you say yes here you get support for the rotary encoder on the 238 + Maxim MAX7360 I/O Expander. 239 + 240 + To compile this driver as a module, choose M here: the module will be 241 + called max7360_rotary. 242 + 233 243 config INPUT_MAX77650_ONKEY 234 244 tristate "Maxim MAX77650 ONKEY support" 235 245 depends on MFD_MAX77650 ··· 515 505 516 506 To compile this driver as a module, choose M here. The module will 517 507 be called tps65219-pwrbutton. 508 + 509 + config INPUT_TPS6594_PWRBUTTON 510 + tristate "TPS6594 Power button driver" 511 + depends on MFD_TPS6594 512 + help 513 + Say Y here if you want to enable power button reporting for 514 + TPS6594 Power Management IC devices. 515 + 516 + To compile this driver as a module, choose M here. The module will 517 + be called tps6594-pwrbutton. 518 518 519 519 config INPUT_AXP20X_PEK 520 520 tristate "X-Powers AXP20X power button driver"
+2
drivers/input/misc/Makefile
··· 51 51 obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o 52 52 obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o 53 53 obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o 54 + obj-$(CONFIG_INPUT_MAX7360_ROTARY) += max7360-rotary.o 54 55 obj-$(CONFIG_INPUT_MAX77650_ONKEY) += max77650-onkey.o 55 56 obj-$(CONFIG_INPUT_MAX77693_HAPTIC) += max77693-haptic.o 56 57 obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o ··· 84 83 obj-$(CONFIG_INPUT_STPMIC1_ONKEY) += stpmic1_onkey.o 85 84 obj-$(CONFIG_INPUT_TPS65218_PWRBUTTON) += tps65218-pwrbutton.o 86 85 obj-$(CONFIG_INPUT_TPS65219_PWRBUTTON) += tps65219-pwrbutton.o 86 + obj-$(CONFIG_INPUT_TPS6594_PWRBUTTON) += tps6594-pwrbutton.o 87 87 obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o 88 88 obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o 89 89 obj-$(CONFIG_INPUT_TWL6040_VIBRA) += twl6040-vibra.o
+192
drivers/input/misc/max7360-rotary.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright 2025 Bootlin 4 + * 5 + * Author: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> 6 + */ 7 + 8 + #include <linux/bitfield.h> 9 + #include <linux/device/devres.h> 10 + #include <linux/dev_printk.h> 11 + #include <linux/init.h> 12 + #include <linux/input.h> 13 + #include <linux/interrupt.h> 14 + #include <linux/mfd/max7360.h> 15 + #include <linux/property.h> 16 + #include <linux/platform_device.h> 17 + #include <linux/pm_wakeirq.h> 18 + #include <linux/regmap.h> 19 + #include <linux/types.h> 20 + 21 + #define MAX7360_ROTARY_DEFAULT_STEPS 24 22 + 23 + struct max7360_rotary { 24 + struct input_dev *input; 25 + struct regmap *regmap; 26 + unsigned int debounce_ms; 27 + 28 + unsigned int pos; 29 + 30 + u32 steps; 31 + u32 axis; 32 + bool relative_axis; 33 + bool rollover; 34 + }; 35 + 36 + static void max7360_rotary_report_event(struct max7360_rotary *max7360_rotary, int steps) 37 + { 38 + if (max7360_rotary->relative_axis) { 39 + input_report_rel(max7360_rotary->input, max7360_rotary->axis, steps); 40 + } else { 41 + int pos = max7360_rotary->pos; 42 + int maxval = max7360_rotary->steps; 43 + 44 + /* 45 + * Add steps to the position. 46 + * Make sure added steps are always in ]-maxval; maxval[ 47 + * interval, so (pos + maxval) is always >= 0. 48 + * Then set back pos to the [0; maxval[ interval. 49 + */ 50 + pos += steps % maxval; 51 + if (max7360_rotary->rollover) 52 + pos = (pos + maxval) % maxval; 53 + else 54 + pos = clamp(pos, 0, maxval - 1); 55 + 56 + max7360_rotary->pos = pos; 57 + input_report_abs(max7360_rotary->input, max7360_rotary->axis, max7360_rotary->pos); 58 + } 59 + 60 + input_sync(max7360_rotary->input); 61 + } 62 + 63 + static irqreturn_t max7360_rotary_irq(int irq, void *data) 64 + { 65 + struct max7360_rotary *max7360_rotary = data; 66 + struct device *dev = max7360_rotary->input->dev.parent; 67 + unsigned int val; 68 + int error; 69 + 70 + error = regmap_read(max7360_rotary->regmap, MAX7360_REG_RTR_CNT, &val); 71 + if (error < 0) { 72 + dev_err(dev, "Failed to read rotary counter\n"); 73 + return IRQ_NONE; 74 + } 75 + 76 + if (val == 0) 77 + return IRQ_NONE; 78 + 79 + max7360_rotary_report_event(max7360_rotary, sign_extend32(val, 7)); 80 + 81 + return IRQ_HANDLED; 82 + } 83 + 84 + static int max7360_rotary_hw_init(struct max7360_rotary *max7360_rotary) 85 + { 86 + struct device *dev = max7360_rotary->input->dev.parent; 87 + int val; 88 + int error; 89 + 90 + val = FIELD_PREP(MAX7360_ROT_DEBOUNCE, max7360_rotary->debounce_ms) | 91 + FIELD_PREP(MAX7360_ROT_INTCNT, 1) | MAX7360_ROT_INTCNT_DLY; 92 + error = regmap_write(max7360_rotary->regmap, MAX7360_REG_RTRCFG, val); 93 + if (error) 94 + dev_err(dev, "Failed to set max7360 rotary encoder configuration\n"); 95 + 96 + return error; 97 + } 98 + 99 + static int max7360_rotary_probe(struct platform_device *pdev) 100 + { 101 + struct max7360_rotary *max7360_rotary; 102 + struct device *dev = &pdev->dev; 103 + struct input_dev *input; 104 + struct regmap *regmap; 105 + int irq; 106 + int error; 107 + 108 + regmap = dev_get_regmap(dev->parent, NULL); 109 + if (!regmap) 110 + return dev_err_probe(dev, -ENODEV, "Could not get parent regmap\n"); 111 + 112 + irq = fwnode_irq_get_byname(dev_fwnode(dev->parent), "inti"); 113 + if (irq < 0) 114 + return dev_err_probe(dev, irq, "Failed to get IRQ\n"); 115 + 116 + max7360_rotary = devm_kzalloc(dev, sizeof(*max7360_rotary), GFP_KERNEL); 117 + if (!max7360_rotary) 118 + return -ENOMEM; 119 + 120 + max7360_rotary->regmap = regmap; 121 + 122 + device_property_read_u32(dev->parent, "linux,axis", &max7360_rotary->axis); 123 + max7360_rotary->rollover = device_property_read_bool(dev->parent, 124 + "rotary-encoder,rollover"); 125 + max7360_rotary->relative_axis = 126 + device_property_read_bool(dev->parent, "rotary-encoder,relative-axis"); 127 + 128 + error = device_property_read_u32(dev->parent, "rotary-encoder,steps", 129 + &max7360_rotary->steps); 130 + if (error) 131 + max7360_rotary->steps = MAX7360_ROTARY_DEFAULT_STEPS; 132 + 133 + device_property_read_u32(dev->parent, "rotary-debounce-delay-ms", 134 + &max7360_rotary->debounce_ms); 135 + if (max7360_rotary->debounce_ms > MAX7360_ROT_DEBOUNCE_MAX) 136 + return dev_err_probe(dev, -EINVAL, "Invalid debounce timing: %u\n", 137 + max7360_rotary->debounce_ms); 138 + 139 + input = devm_input_allocate_device(dev); 140 + if (!input) 141 + return -ENOMEM; 142 + 143 + max7360_rotary->input = input; 144 + 145 + input->id.bustype = BUS_I2C; 146 + input->name = pdev->name; 147 + 148 + if (max7360_rotary->relative_axis) 149 + input_set_capability(input, EV_REL, max7360_rotary->axis); 150 + else 151 + input_set_abs_params(input, max7360_rotary->axis, 0, max7360_rotary->steps, 0, 1); 152 + 153 + error = devm_request_threaded_irq(dev, irq, NULL, max7360_rotary_irq, 154 + IRQF_ONESHOT | IRQF_SHARED, 155 + "max7360-rotary", max7360_rotary); 156 + if (error) 157 + return dev_err_probe(dev, error, "Failed to register interrupt\n"); 158 + 159 + error = input_register_device(input); 160 + if (error) 161 + return dev_err_probe(dev, error, "Could not register input device\n"); 162 + 163 + error = max7360_rotary_hw_init(max7360_rotary); 164 + if (error) 165 + return dev_err_probe(dev, error, "Failed to initialize max7360 rotary\n"); 166 + 167 + device_init_wakeup(dev, true); 168 + error = dev_pm_set_wake_irq(dev, irq); 169 + if (error) 170 + dev_warn(dev, "Failed to set up wakeup irq: %d\n", error); 171 + 172 + return 0; 173 + } 174 + 175 + static void max7360_rotary_remove(struct platform_device *pdev) 176 + { 177 + dev_pm_clear_wake_irq(&pdev->dev); 178 + device_init_wakeup(&pdev->dev, false); 179 + } 180 + 181 + static struct platform_driver max7360_rotary_driver = { 182 + .driver = { 183 + .name = "max7360-rotary", 184 + }, 185 + .probe = max7360_rotary_probe, 186 + .remove = max7360_rotary_remove, 187 + }; 188 + module_platform_driver(max7360_rotary_driver); 189 + 190 + MODULE_DESCRIPTION("MAX7360 Rotary driver"); 191 + MODULE_AUTHOR("Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>"); 192 + MODULE_LICENSE("GPL");
-1
drivers/input/misc/mc13783-pwrbutton.c
··· 57 57 struct mc13783_pwrb *priv = _priv; 58 58 int val; 59 59 60 - mc13xxx_irq_ack(priv->mc13783, irq); 61 60 mc13xxx_reg_read(priv->mc13783, MC13783_REG_INTERRUPT_SENSE_1, &val); 62 61 63 62 switch (irq) {
+126
drivers/input/misc/tps6594-pwrbutton.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * power button driver for TI TPS6594 PMICs 4 + * 5 + * Copyright (C) 2025 Critical Link LLC - https://www.criticallink.com/ 6 + */ 7 + #include <linux/init.h> 8 + #include <linux/input.h> 9 + #include <linux/interrupt.h> 10 + #include <linux/kernel.h> 11 + #include <linux/mfd/tps6594.h> 12 + #include <linux/module.h> 13 + #include <linux/platform_device.h> 14 + #include <linux/regmap.h> 15 + #include <linux/slab.h> 16 + 17 + struct tps6594_pwrbutton { 18 + struct device *dev; 19 + struct input_dev *idev; 20 + char phys[32]; 21 + }; 22 + 23 + static irqreturn_t tps6594_pb_push_irq(int irq, void *_pwr) 24 + { 25 + struct tps6594_pwrbutton *pwr = _pwr; 26 + 27 + input_report_key(pwr->idev, KEY_POWER, 1); 28 + pm_wakeup_event(pwr->dev, 0); 29 + input_sync(pwr->idev); 30 + 31 + return IRQ_HANDLED; 32 + } 33 + 34 + static irqreturn_t tps6594_pb_release_irq(int irq, void *_pwr) 35 + { 36 + struct tps6594_pwrbutton *pwr = _pwr; 37 + 38 + input_report_key(pwr->idev, KEY_POWER, 0); 39 + input_sync(pwr->idev); 40 + 41 + return IRQ_HANDLED; 42 + } 43 + 44 + static int tps6594_pb_probe(struct platform_device *pdev) 45 + { 46 + struct device *dev = &pdev->dev; 47 + struct tps6594_pwrbutton *pwr; 48 + struct input_dev *idev; 49 + int error; 50 + int push_irq; 51 + int release_irq; 52 + 53 + pwr = devm_kzalloc(dev, sizeof(*pwr), GFP_KERNEL); 54 + if (!pwr) 55 + return -ENOMEM; 56 + 57 + idev = devm_input_allocate_device(dev); 58 + if (!idev) 59 + return -ENOMEM; 60 + 61 + idev->name = pdev->name; 62 + snprintf(pwr->phys, sizeof(pwr->phys), "%s/input0", 63 + pdev->name); 64 + idev->phys = pwr->phys; 65 + idev->id.bustype = BUS_I2C; 66 + 67 + input_set_capability(idev, EV_KEY, KEY_POWER); 68 + 69 + pwr->dev = dev; 70 + pwr->idev = idev; 71 + device_init_wakeup(dev, true); 72 + 73 + push_irq = platform_get_irq(pdev, 0); 74 + if (push_irq < 0) 75 + return -EINVAL; 76 + 77 + release_irq = platform_get_irq(pdev, 1); 78 + if (release_irq < 0) 79 + return -EINVAL; 80 + 81 + error = devm_request_threaded_irq(dev, push_irq, NULL, 82 + tps6594_pb_push_irq, 83 + IRQF_ONESHOT, 84 + pdev->resource[0].name, pwr); 85 + if (error) { 86 + dev_err(dev, "failed to request push IRQ #%d: %d\n", push_irq, 87 + error); 88 + return error; 89 + } 90 + 91 + error = devm_request_threaded_irq(dev, release_irq, NULL, 92 + tps6594_pb_release_irq, 93 + IRQF_ONESHOT, 94 + pdev->resource[1].name, pwr); 95 + if (error) { 96 + dev_err(dev, "failed to request release IRQ #%d: %d\n", 97 + release_irq, error); 98 + return error; 99 + } 100 + 101 + error = input_register_device(idev); 102 + if (error) { 103 + dev_err(dev, "Can't register power button: %d\n", error); 104 + return error; 105 + } 106 + 107 + return 0; 108 + } 109 + 110 + static const struct platform_device_id tps6594_pwrbtn_id_table[] = { 111 + { "tps6594-pwrbutton", }, 112 + { /* sentinel */ } 113 + }; 114 + MODULE_DEVICE_TABLE(platform, tps6594_pwrbtn_id_table); 115 + 116 + static struct platform_driver tps6594_pb_driver = { 117 + .probe = tps6594_pb_probe, 118 + .driver = { 119 + .name = "tps6594_pwrbutton", 120 + }, 121 + .id_table = tps6594_pwrbtn_id_table, 122 + }; 123 + module_platform_driver(tps6594_pb_driver); 124 + 125 + MODULE_DESCRIPTION("TPS6594 Power Button"); 126 + MODULE_LICENSE("GPL");
-4
drivers/input/touchscreen/mc13783_ts.c
··· 42 42 { 43 43 struct mc13783_ts_priv *priv = data; 44 44 45 - mc13xxx_irq_ack(priv->mc13xxx, irq); 46 - 47 45 /* 48 46 * Kick off reading coordinates. Note that if work happens already 49 47 * be queued for future execution (it rearms itself) it will not ··· 134 136 int ret; 135 137 136 138 mc13xxx_lock(priv->mc13xxx); 137 - 138 - mc13xxx_irq_ack(priv->mc13xxx, MC13XXX_IRQ_TS); 139 139 140 140 ret = mc13xxx_irq_request(priv->mc13xxx, MC13XXX_IRQ_TS, 141 141 mc13783_ts_handler, MC13783_TS_NAME, priv);
+40
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 ··· 1655 1640 TI LMU MFD supports LM3532, LM3631, LM3632, LM3633, LM3695 and 1656 1641 LM36274. It consists of backlight, LED and regulator driver. 1657 1642 It provides consistent device controls for lighting functions. 1643 + 1644 + config MFD_BQ257XX 1645 + tristate "TI BQ257XX Buck/Boost Charge Controller" 1646 + depends on I2C 1647 + select MFD_CORE 1648 + select REGMAP_I2C 1649 + help 1650 + Support Texas Instruments BQ25703 Buck/Boost converter with 1651 + charge controller. It consists of regulators that provide 1652 + system voltage and OTG voltage, and a charger manager for 1653 + batteries containing one or more cells. 1658 1654 1659 1655 config MFD_OMAP_USB_HOST 1660 1656 bool "TI OMAP USBHS core and TLL driver" ··· 2517 2491 2518 2492 To compile this driver as a module, choose M here: the module will be 2519 2493 called upboard-fpga. 2494 + 2495 + config MFD_MAX7360 2496 + tristate "Maxim MAX7360 I2C IO Expander" 2497 + depends on I2C 2498 + select MFD_CORE 2499 + select REGMAP_I2C 2500 + select REGMAP_IRQ 2501 + help 2502 + Say yes here to add support for Maxim MAX7360 device, embedding 2503 + keypad, rotary encoder, PWM and GPIO features. 2504 + 2505 + This driver provides common support for accessing the device; 2506 + additional drivers must be enabled in order to use the functionality 2507 + of the device. 2520 2508 2521 2509 endmenu 2522 2510 endif
+4
drivers/mfd/Makefile
··· 13 13 obj-$(CONFIG_ARCH_BCM2835) += bcm2835-pm.o 14 14 obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o 15 15 obj-$(CONFIG_MFD_BD9571MWV) += bd9571mwv.o 16 + obj-$(CONFIG_MFD_BQ257XX) += bq257xx.o 16 17 obj-$(CONFIG_MFD_CGBC) += cgbc-core.o 17 18 obj-$(CONFIG_MFD_CROS_EC_DEV) += cros_ec_dev.o 18 19 obj-$(CONFIG_MFD_CS42L43) += cs42l43.o ··· 122 121 obj-$(CONFIG_MFD_MC13XXX_SPI) += mc13xxx-spi.o 123 122 obj-$(CONFIG_MFD_MC13XXX_I2C) += mc13xxx-i2c.o 124 123 124 + obj-$(CONFIG_MFD_NCT6694) += nct6694.o 125 + 125 126 obj-$(CONFIG_MFD_CORE) += mfd-core.o 126 127 127 128 ocelot-soc-objs := ocelot-core.o ocelot-spi.o ··· 166 163 obj-$(CONFIG_MFD_DA9150) += da9150-core.o 167 164 168 165 obj-$(CONFIG_MFD_MAX14577) += max14577.o 166 + obj-$(CONFIG_MFD_MAX7360) += max7360.o 169 167 obj-$(CONFIG_MFD_MAX77541) += max77541.o 170 168 obj-$(CONFIG_MFD_MAX77620) += max77620.o 171 169 obj-$(CONFIG_MFD_MAX77650) += max77650.o
+99
drivers/mfd/bq257xx.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * BQ257XX Core Driver 4 + * Copyright (C) 2025 Chris Morgan <macromorgan@hotmail.com> 5 + */ 6 + 7 + #include <linux/device.h> 8 + #include <linux/i2c.h> 9 + #include <linux/mfd/bq257xx.h> 10 + #include <linux/mfd/core.h> 11 + #include <linux/regmap.h> 12 + 13 + static const struct regmap_range bq25703_readonly_reg_ranges[] = { 14 + regmap_reg_range(BQ25703_CHARGER_STATUS, BQ25703_MANUFACT_DEV_ID), 15 + }; 16 + 17 + static const struct regmap_access_table bq25703_writeable_regs = { 18 + .no_ranges = bq25703_readonly_reg_ranges, 19 + .n_no_ranges = ARRAY_SIZE(bq25703_readonly_reg_ranges), 20 + }; 21 + 22 + static const struct regmap_range bq25703_volatile_reg_ranges[] = { 23 + regmap_reg_range(BQ25703_CHARGE_OPTION_0, BQ25703_IIN_HOST), 24 + regmap_reg_range(BQ25703_CHARGER_STATUS, BQ25703_ADC_OPTION), 25 + }; 26 + 27 + static const struct regmap_access_table bq25703_volatile_regs = { 28 + .yes_ranges = bq25703_volatile_reg_ranges, 29 + .n_yes_ranges = ARRAY_SIZE(bq25703_volatile_reg_ranges), 30 + }; 31 + 32 + static const struct regmap_config bq25703_regmap_config = { 33 + .reg_bits = 8, 34 + .val_bits = 16, 35 + .max_register = BQ25703_ADC_OPTION, 36 + .cache_type = REGCACHE_MAPLE, 37 + .wr_table = &bq25703_writeable_regs, 38 + .volatile_table = &bq25703_volatile_regs, 39 + .val_format_endian = REGMAP_ENDIAN_LITTLE, 40 + }; 41 + 42 + static const struct mfd_cell cells[] = { 43 + MFD_CELL_NAME("bq257xx-regulator"), 44 + MFD_CELL_NAME("bq257xx-charger"), 45 + }; 46 + 47 + static int bq257xx_probe(struct i2c_client *client) 48 + { 49 + struct bq257xx_device *ddata; 50 + int ret; 51 + 52 + ddata = devm_kzalloc(&client->dev, sizeof(*ddata), GFP_KERNEL); 53 + if (!ddata) 54 + return -ENOMEM; 55 + 56 + ddata->client = client; 57 + 58 + ddata->regmap = devm_regmap_init_i2c(client, &bq25703_regmap_config); 59 + if (IS_ERR(ddata->regmap)) { 60 + return dev_err_probe(&client->dev, PTR_ERR(ddata->regmap), 61 + "Failed to allocate register map\n"); 62 + } 63 + 64 + i2c_set_clientdata(client, ddata); 65 + 66 + ret = devm_mfd_add_devices(&client->dev, PLATFORM_DEVID_AUTO, 67 + cells, ARRAY_SIZE(cells), NULL, 0, NULL); 68 + if (ret) 69 + return dev_err_probe(&client->dev, ret, 70 + "Failed to register child devices\n"); 71 + 72 + return ret; 73 + } 74 + 75 + static const struct i2c_device_id bq257xx_i2c_ids[] = { 76 + { "bq25703a" }, 77 + {} 78 + }; 79 + MODULE_DEVICE_TABLE(i2c, bq257xx_i2c_ids); 80 + 81 + static const struct of_device_id bq257xx_of_match[] = { 82 + { .compatible = "ti,bq25703a" }, 83 + {} 84 + }; 85 + MODULE_DEVICE_TABLE(of, bq257xx_of_match); 86 + 87 + static struct i2c_driver bq257xx_driver = { 88 + .driver = { 89 + .name = "bq257xx", 90 + .of_match_table = bq257xx_of_match, 91 + }, 92 + .probe = bq257xx_probe, 93 + .id_table = bq257xx_i2c_ids, 94 + }; 95 + module_i2c_driver(bq257xx_driver); 96 + 97 + MODULE_DESCRIPTION("bq257xx buck/boost/charger driver"); 98 + MODULE_AUTHOR("Chris Morgan <macromorgan@hotmail.com>"); 99 + MODULE_LICENSE("GPL");
+171
drivers/mfd/max7360.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Maxim MAX7360 Core Driver 4 + * 5 + * Copyright 2025 Bootlin 6 + * 7 + * Authors: 8 + * Kamel Bouhara <kamel.bouhara@bootlin.com> 9 + * Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> 10 + */ 11 + 12 + #include <linux/array_size.h> 13 + #include <linux/bits.h> 14 + #include <linux/delay.h> 15 + #include <linux/device/devres.h> 16 + #include <linux/dev_printk.h> 17 + #include <linux/err.h> 18 + #include <linux/i2c.h> 19 + #include <linux/interrupt.h> 20 + #include <linux/mfd/core.h> 21 + #include <linux/mfd/max7360.h> 22 + #include <linux/mod_devicetable.h> 23 + #include <linux/module.h> 24 + #include <linux/regmap.h> 25 + #include <linux/types.h> 26 + 27 + static const struct mfd_cell max7360_cells[] = { 28 + { .name = "max7360-pinctrl" }, 29 + { .name = "max7360-pwm" }, 30 + { .name = "max7360-keypad" }, 31 + { .name = "max7360-rotary" }, 32 + { 33 + .name = "max7360-gpo", 34 + .of_compatible = "maxim,max7360-gpo", 35 + }, 36 + { 37 + .name = "max7360-gpio", 38 + .of_compatible = "maxim,max7360-gpio", 39 + }, 40 + }; 41 + 42 + static const struct regmap_range max7360_volatile_ranges[] = { 43 + regmap_reg_range(MAX7360_REG_KEYFIFO, MAX7360_REG_KEYFIFO), 44 + regmap_reg_range(MAX7360_REG_I2C_TIMEOUT, MAX7360_REG_RTR_CNT), 45 + }; 46 + 47 + static const struct regmap_access_table max7360_volatile_table = { 48 + .yes_ranges = max7360_volatile_ranges, 49 + .n_yes_ranges = ARRAY_SIZE(max7360_volatile_ranges), 50 + }; 51 + 52 + static const struct regmap_config max7360_regmap_config = { 53 + .reg_bits = 8, 54 + .val_bits = 8, 55 + .max_register = MAX7360_REG_PWMCFG(MAX7360_PORT_PWM_COUNT - 1), 56 + .volatile_table = &max7360_volatile_table, 57 + .cache_type = REGCACHE_MAPLE, 58 + }; 59 + 60 + static int max7360_mask_irqs(struct regmap *regmap) 61 + { 62 + struct device *dev = regmap_get_device(regmap); 63 + unsigned int val; 64 + int ret; 65 + 66 + /* 67 + * GPIO/PWM interrupts are not masked on reset: as the MAX7360 "INTI" 68 + * interrupt line is shared between GPIOs and rotary encoder, this could 69 + * result in repeated spurious interrupts on the rotary encoder driver 70 + * if the GPIO driver is not loaded. Mask them now to avoid this 71 + * situation. 72 + */ 73 + for (unsigned int i = 0; i < MAX7360_PORT_PWM_COUNT; i++) { 74 + ret = regmap_write_bits(regmap, MAX7360_REG_PWMCFG(i), 75 + MAX7360_PORT_CFG_INTERRUPT_MASK, 76 + MAX7360_PORT_CFG_INTERRUPT_MASK); 77 + if (ret) 78 + return dev_err_probe(dev, ret, 79 + "Failed to write MAX7360 port configuration\n"); 80 + } 81 + 82 + /* Read GPIO in register, to ACK any pending IRQ. */ 83 + ret = regmap_read(regmap, MAX7360_REG_GPIOIN, &val); 84 + if (ret) 85 + return dev_err_probe(dev, ret, "Failed to read GPIO values\n"); 86 + 87 + return 0; 88 + } 89 + 90 + static int max7360_reset(struct regmap *regmap) 91 + { 92 + struct device *dev = regmap_get_device(regmap); 93 + int ret; 94 + 95 + ret = regmap_write(regmap, MAX7360_REG_GPIOCFG, MAX7360_GPIO_CFG_GPIO_RST); 96 + if (ret) { 97 + dev_err(dev, "Failed to reset GPIO configuration: %x\n", ret); 98 + return ret; 99 + } 100 + 101 + ret = regcache_drop_region(regmap, MAX7360_REG_GPIOCFG, MAX7360_REG_GPIO_LAST); 102 + if (ret) { 103 + dev_err(dev, "Failed to drop regmap cache: %x\n", ret); 104 + return ret; 105 + } 106 + 107 + ret = regmap_write(regmap, MAX7360_REG_SLEEP, 0); 108 + if (ret) { 109 + dev_err(dev, "Failed to reset autosleep configuration: %x\n", ret); 110 + return ret; 111 + } 112 + 113 + ret = regmap_write(regmap, MAX7360_REG_DEBOUNCE, 0); 114 + if (ret) 115 + dev_err(dev, "Failed to reset GPO port count: %x\n", ret); 116 + 117 + return ret; 118 + } 119 + 120 + static int max7360_probe(struct i2c_client *client) 121 + { 122 + struct device *dev = &client->dev; 123 + struct regmap *regmap; 124 + int ret; 125 + 126 + regmap = devm_regmap_init_i2c(client, &max7360_regmap_config); 127 + if (IS_ERR(regmap)) 128 + return dev_err_probe(dev, PTR_ERR(regmap), "Failed to initialise regmap\n"); 129 + 130 + ret = max7360_reset(regmap); 131 + if (ret) 132 + return dev_err_probe(dev, ret, "Failed to reset device\n"); 133 + 134 + /* Get the device out of shutdown mode. */ 135 + ret = regmap_write_bits(regmap, MAX7360_REG_GPIOCFG, 136 + MAX7360_GPIO_CFG_GPIO_EN, 137 + MAX7360_GPIO_CFG_GPIO_EN); 138 + if (ret) 139 + return dev_err_probe(dev, ret, "Failed to enable GPIO and PWM module\n"); 140 + 141 + ret = max7360_mask_irqs(regmap); 142 + if (ret) 143 + return dev_err_probe(dev, ret, "Could not mask interrupts\n"); 144 + 145 + ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, 146 + max7360_cells, ARRAY_SIZE(max7360_cells), 147 + NULL, 0, NULL); 148 + if (ret) 149 + return dev_err_probe(dev, ret, "Failed to register child devices\n"); 150 + 151 + return 0; 152 + } 153 + 154 + static const struct of_device_id max7360_dt_match[] = { 155 + { .compatible = "maxim,max7360" }, 156 + {} 157 + }; 158 + MODULE_DEVICE_TABLE(of, max7360_dt_match); 159 + 160 + static struct i2c_driver max7360_driver = { 161 + .driver = { 162 + .name = "max7360", 163 + .of_match_table = max7360_dt_match, 164 + }, 165 + .probe = max7360_probe, 166 + }; 167 + module_i2c_driver(max7360_driver); 168 + 169 + MODULE_DESCRIPTION("Maxim MAX7360 I2C IO Expander core driver"); 170 + MODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>"); 171 + MODULE_LICENSE("GPL");
+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");
+57 -2
drivers/mfd/tps6594-core.c
··· 10 10 * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/ 11 11 */ 12 12 13 + #include <linux/bitfield.h> 13 14 #include <linux/completion.h> 14 15 #include <linux/delay.h> 15 16 #include <linux/interrupt.h> 16 17 #include <linux/module.h> 17 18 #include <linux/of.h> 19 + #include <linux/reboot.h> 18 20 19 21 #include <linux/mfd/core.h> 20 22 #include <linux/mfd/tps6594.h> 21 23 22 24 #define TPS6594_CRC_SYNC_TIMEOUT_MS 150 25 + #define TPS65224_EN_SEL_PB 1 26 + #define TPS65224_GPIO3_SEL_PB 3 23 27 24 28 /* Completion to synchronize CRC feature enabling on all PMICs */ 25 29 static DECLARE_COMPLETION(tps6594_crc_comp); ··· 130 126 DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_TIMER, TPS6594_IRQ_NAME_TIMER), 131 127 DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_ALARM, TPS6594_IRQ_NAME_ALARM), 132 128 DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_POWER_UP, TPS6594_IRQ_NAME_POWERUP), 129 + }; 130 + 131 + static const struct resource tps6594_pwrbutton_resources[] = { 132 + DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_PB_FALL, TPS65224_IRQ_NAME_PB_FALL), 133 + DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_PB_RISE, TPS65224_IRQ_NAME_PB_RISE), 134 + DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_PB_SHORT, TPS65224_IRQ_NAME_PB_SHORT), 133 135 }; 134 136 135 137 static const struct mfd_cell tps6594_common_cells[] = { ··· 328 318 DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_REG_UNLOCK, TPS65224_IRQ_NAME_REG_UNLOCK), 329 319 DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_TWARN, TPS65224_IRQ_NAME_TWARN), 330 320 DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_PB_LONG, TPS65224_IRQ_NAME_PB_LONG), 331 - DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_PB_FALL, TPS65224_IRQ_NAME_PB_FALL), 332 - DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_PB_RISE, TPS65224_IRQ_NAME_PB_RISE), 333 321 DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_TSD_ORD, TPS65224_IRQ_NAME_TSD_ORD), 334 322 DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_BIST_FAIL, TPS65224_IRQ_NAME_BIST_FAIL), 335 323 DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_REG_CRC_ERR, TPS65224_IRQ_NAME_REG_CRC_ERR), ··· 353 345 MFD_CELL_RES("tps6594-pfsm", tps65224_pfsm_resources), 354 346 MFD_CELL_RES("tps6594-pinctrl", tps65224_pinctrl_resources), 355 347 MFD_CELL_RES("tps6594-regulator", tps65224_regulator_resources), 348 + }; 349 + 350 + static const struct mfd_cell tps6594_pwrbutton_cell = { 351 + .name = "tps6594-pwrbutton", 352 + .resources = tps6594_pwrbutton_resources, 353 + .num_resources = ARRAY_SIZE(tps6594_pwrbutton_resources), 356 354 }; 357 355 358 356 static const struct regmap_irq tps65224_irqs[] = { ··· 690 676 return ret; 691 677 } 692 678 679 + static int tps6594_power_off_handler(struct sys_off_data *data) 680 + { 681 + struct tps6594 *tps = data->cb_data; 682 + int ret; 683 + 684 + ret = regmap_update_bits(tps->regmap, TPS6594_REG_FSM_I2C_TRIGGERS, 685 + TPS6594_BIT_TRIGGER_I2C(0), TPS6594_BIT_TRIGGER_I2C(0)); 686 + if (ret) 687 + return notifier_from_errno(ret); 688 + 689 + return NOTIFY_DONE; 690 + } 691 + 693 692 int tps6594_device_init(struct tps6594 *tps, bool enable_crc) 694 693 { 695 694 struct device *dev = tps->dev; 696 695 int ret; 697 696 struct regmap_irq_chip *irq_chip; 697 + unsigned int pwr_on, gpio3_cfg; 698 698 const struct mfd_cell *cells; 699 699 int n_cells; 700 700 ··· 755 727 if (ret) 756 728 return dev_err_probe(dev, ret, "Failed to add common child devices\n"); 757 729 730 + /* If either the PB/EN/VSENSE or GPIO3 is configured as PB, register a driver for it */ 731 + if (tps->chip_id == TPS65224 || tps->chip_id == TPS652G1) { 732 + ret = regmap_read(tps->regmap, TPS6594_REG_NPWRON_CONF, &pwr_on); 733 + if (ret) 734 + return dev_err_probe(dev, ret, "Failed to read PB/EN/VSENSE config\n"); 735 + 736 + ret = regmap_read(tps->regmap, TPS6594_REG_GPIOX_CONF(2), &gpio3_cfg); 737 + if (ret) 738 + return dev_err_probe(dev, ret, "Failed to read GPIO3 config\n"); 739 + 740 + if (FIELD_GET(TPS65224_MASK_EN_PB_VSENSE_CONFIG, pwr_on) == TPS65224_EN_SEL_PB || 741 + FIELD_GET(TPS65224_MASK_GPIO_SEL, gpio3_cfg) == TPS65224_GPIO3_SEL_PB) { 742 + ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, 743 + &tps6594_pwrbutton_cell, 1, NULL, 0, 744 + regmap_irq_get_domain(tps->irq_data)); 745 + if (ret) 746 + return dev_err_probe(dev, ret, 747 + "Failed to add power button device.\n"); 748 + } 749 + } 750 + 758 751 /* No RTC for LP8764, TPS65224 and TPS652G1 */ 759 752 if (tps->chip_id != LP8764 && tps->chip_id != TPS65224 && tps->chip_id != TPS652G1) { 760 753 ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, tps6594_rtc_cells, ··· 783 734 regmap_irq_get_domain(tps->irq_data)); 784 735 if (ret) 785 736 return dev_err_probe(dev, ret, "Failed to add RTC child device\n"); 737 + } 738 + 739 + if (of_device_is_system_power_controller(dev->of_node)) { 740 + ret = devm_register_power_off_handler(tps->dev, tps6594_power_off_handler, tps); 741 + if (ret) 742 + return dev_err_probe(dev, ret, "Failed to register power-off handler\n"); 786 743 } 787 744 788 745 return 0;
+20 -5
drivers/mfd/vexpress-sysreg.c
··· 5 5 */ 6 6 7 7 #include <linux/gpio/driver.h> 8 + #include <linux/gpio/generic.h> 8 9 #include <linux/err.h> 9 10 #include <linux/io.h> 10 11 #include <linux/mfd/core.h> ··· 97 96 98 97 static int vexpress_sysreg_probe(struct platform_device *pdev) 99 98 { 99 + struct gpio_generic_chip *mmc_gpio_chip; 100 + struct gpio_generic_chip_config config; 100 101 struct resource *mem; 101 102 void __iomem *base; 102 - struct gpio_chip *mmc_gpio_chip; 103 + int ret; 103 104 104 105 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 105 106 if (!mem) ··· 119 116 GFP_KERNEL); 120 117 if (!mmc_gpio_chip) 121 118 return -ENOMEM; 122 - bgpio_init(mmc_gpio_chip, &pdev->dev, 0x4, base + SYS_MCI, 123 - NULL, NULL, NULL, NULL, 0); 124 - mmc_gpio_chip->ngpio = 2; 125 - devm_gpiochip_add_data(&pdev->dev, mmc_gpio_chip, NULL); 119 + 120 + config = (typeof(config)){ 121 + .dev = &pdev->dev, 122 + .sz = 4, 123 + .dat = base + SYS_MCI, 124 + }; 125 + 126 + ret = gpio_generic_chip_init(mmc_gpio_chip, &config); 127 + if (ret) 128 + return ret; 129 + 130 + mmc_gpio_chip->gc.ngpio = 2; 131 + 132 + ret = devm_gpiochip_add_data(&pdev->dev, &mmc_gpio_chip->gc, NULL); 133 + if (ret) 134 + return ret; 126 135 127 136 return devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO, 128 137 vexpress_sysreg_cells,
+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");
+11
drivers/pinctrl/Kconfig
··· 358 358 help 359 359 Pinctrl driver for NXP LPC18xx/43xx System Control Unit (SCU). 360 360 361 + config PINCTRL_MAX7360 362 + tristate "MAX7360 Pincontrol support" 363 + depends on MFD_MAX7360 364 + select PINMUX 365 + select GENERIC_PINCONF 366 + help 367 + Say Y here to enable pin control support for Maxim MAX7360 keypad 368 + controller. 369 + This keypad controller has 8 GPIO pins that may work as GPIO, or PWM, 370 + or rotary encoder alternate modes. 371 + 361 372 config PINCTRL_MAX77620 362 373 tristate "MAX77620/MAX20024 Pincontrol support" 363 374 depends on MFD_MAX77620 && OF
+1
drivers/pinctrl/Makefile
··· 37 37 obj-$(CONFIG_PINCTRL_LOONGSON2) += pinctrl-loongson2.o 38 38 obj-$(CONFIG_PINCTRL_XWAY) += pinctrl-xway.o 39 39 obj-$(CONFIG_PINCTRL_LPC18XX) += pinctrl-lpc18xx.o 40 + obj-$(CONFIG_PINCTRL_MAX7360) += pinctrl-max7360.o 40 41 obj-$(CONFIG_PINCTRL_MAX77620) += pinctrl-max77620.o 41 42 obj-$(CONFIG_PINCTRL_MCP23S08_I2C) += pinctrl-mcp23s08_i2c.o 42 43 obj-$(CONFIG_PINCTRL_MCP23S08_SPI) += pinctrl-mcp23s08_spi.o
+215
drivers/pinctrl/pinctrl-max7360.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright 2025 Bootlin 4 + * 5 + * Author: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> 6 + */ 7 + 8 + #include <linux/array_size.h> 9 + #include <linux/dev_printk.h> 10 + #include <linux/device.h> 11 + #include <linux/device/devres.h> 12 + #include <linux/err.h> 13 + #include <linux/init.h> 14 + #include <linux/mfd/max7360.h> 15 + #include <linux/module.h> 16 + #include <linux/platform_device.h> 17 + #include <linux/regmap.h> 18 + #include <linux/stddef.h> 19 + 20 + #include <linux/pinctrl/pinctrl.h> 21 + #include <linux/pinctrl/pinconf-generic.h> 22 + #include <linux/pinctrl/pinmux.h> 23 + 24 + #include "core.h" 25 + #include "pinmux.h" 26 + 27 + struct max7360_pinctrl { 28 + struct pinctrl_dev *pctldev; 29 + struct pinctrl_desc pinctrl_desc; 30 + }; 31 + 32 + static const struct pinctrl_pin_desc max7360_pins[] = { 33 + PINCTRL_PIN(0, "PORT0"), 34 + PINCTRL_PIN(1, "PORT1"), 35 + PINCTRL_PIN(2, "PORT2"), 36 + PINCTRL_PIN(3, "PORT3"), 37 + PINCTRL_PIN(4, "PORT4"), 38 + PINCTRL_PIN(5, "PORT5"), 39 + PINCTRL_PIN(6, "PORT6"), 40 + PINCTRL_PIN(7, "PORT7"), 41 + }; 42 + 43 + static const unsigned int port0_pins[] = {0}; 44 + static const unsigned int port1_pins[] = {1}; 45 + static const unsigned int port2_pins[] = {2}; 46 + static const unsigned int port3_pins[] = {3}; 47 + static const unsigned int port4_pins[] = {4}; 48 + static const unsigned int port5_pins[] = {5}; 49 + static const unsigned int port6_pins[] = {6}; 50 + static const unsigned int port7_pins[] = {7}; 51 + static const unsigned int rotary_pins[] = {6, 7}; 52 + 53 + static const struct pingroup max7360_groups[] = { 54 + PINCTRL_PINGROUP("PORT0", port0_pins, ARRAY_SIZE(port0_pins)), 55 + PINCTRL_PINGROUP("PORT1", port1_pins, ARRAY_SIZE(port1_pins)), 56 + PINCTRL_PINGROUP("PORT2", port2_pins, ARRAY_SIZE(port2_pins)), 57 + PINCTRL_PINGROUP("PORT3", port3_pins, ARRAY_SIZE(port3_pins)), 58 + PINCTRL_PINGROUP("PORT4", port4_pins, ARRAY_SIZE(port4_pins)), 59 + PINCTRL_PINGROUP("PORT5", port5_pins, ARRAY_SIZE(port5_pins)), 60 + PINCTRL_PINGROUP("PORT6", port6_pins, ARRAY_SIZE(port6_pins)), 61 + PINCTRL_PINGROUP("PORT7", port7_pins, ARRAY_SIZE(port7_pins)), 62 + PINCTRL_PINGROUP("ROTARY", rotary_pins, ARRAY_SIZE(rotary_pins)), 63 + }; 64 + 65 + static int max7360_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) 66 + { 67 + return ARRAY_SIZE(max7360_groups); 68 + } 69 + 70 + static const char *max7360_pinctrl_get_group_name(struct pinctrl_dev *pctldev, 71 + unsigned int group) 72 + { 73 + return max7360_groups[group].name; 74 + } 75 + 76 + static int max7360_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, 77 + unsigned int group, 78 + const unsigned int **pins, 79 + unsigned int *num_pins) 80 + { 81 + *pins = max7360_groups[group].pins; 82 + *num_pins = max7360_groups[group].npins; 83 + return 0; 84 + } 85 + 86 + static const struct pinctrl_ops max7360_pinctrl_ops = { 87 + .get_groups_count = max7360_pinctrl_get_groups_count, 88 + .get_group_name = max7360_pinctrl_get_group_name, 89 + .get_group_pins = max7360_pinctrl_get_group_pins, 90 + #ifdef CONFIG_OF 91 + .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, 92 + .dt_free_map = pinconf_generic_dt_free_map, 93 + #endif 94 + }; 95 + 96 + static const char * const simple_groups[] = { 97 + "PORT0", "PORT1", "PORT2", "PORT3", 98 + "PORT4", "PORT5", "PORT6", "PORT7", 99 + }; 100 + 101 + static const char * const rotary_groups[] = { "ROTARY" }; 102 + 103 + #define MAX7360_PINCTRL_FN_GPIO 0 104 + #define MAX7360_PINCTRL_FN_PWM 1 105 + #define MAX7360_PINCTRL_FN_ROTARY 2 106 + static const struct pinfunction max7360_functions[] = { 107 + [MAX7360_PINCTRL_FN_GPIO] = PINCTRL_PINFUNCTION("gpio", simple_groups, 108 + ARRAY_SIZE(simple_groups)), 109 + [MAX7360_PINCTRL_FN_PWM] = PINCTRL_PINFUNCTION("pwm", simple_groups, 110 + ARRAY_SIZE(simple_groups)), 111 + [MAX7360_PINCTRL_FN_ROTARY] = PINCTRL_PINFUNCTION("rotary", rotary_groups, 112 + ARRAY_SIZE(rotary_groups)), 113 + }; 114 + 115 + static int max7360_get_functions_count(struct pinctrl_dev *pctldev) 116 + { 117 + return ARRAY_SIZE(max7360_functions); 118 + } 119 + 120 + static const char *max7360_get_function_name(struct pinctrl_dev *pctldev, unsigned int selector) 121 + { 122 + return max7360_functions[selector].name; 123 + } 124 + 125 + static int max7360_get_function_groups(struct pinctrl_dev *pctldev, unsigned int selector, 126 + const char * const **groups, 127 + unsigned int * const num_groups) 128 + { 129 + *groups = max7360_functions[selector].groups; 130 + *num_groups = max7360_functions[selector].ngroups; 131 + 132 + return 0; 133 + } 134 + 135 + static int max7360_set_mux(struct pinctrl_dev *pctldev, unsigned int selector, 136 + unsigned int group) 137 + { 138 + struct regmap *regmap = dev_get_regmap(pctldev->dev->parent, NULL); 139 + int val; 140 + 141 + /* 142 + * GPIO and PWM functions are the same: we only need to handle the 143 + * rotary encoder function, on pins 6 and 7. 144 + */ 145 + if (max7360_groups[group].pins[0] >= 6) { 146 + if (selector == MAX7360_PINCTRL_FN_ROTARY) 147 + val = MAX7360_GPIO_CFG_RTR_EN; 148 + else 149 + val = 0; 150 + 151 + return regmap_write_bits(regmap, MAX7360_REG_GPIOCFG, MAX7360_GPIO_CFG_RTR_EN, val); 152 + } 153 + 154 + return 0; 155 + } 156 + 157 + static const struct pinmux_ops max7360_pmxops = { 158 + .get_functions_count = max7360_get_functions_count, 159 + .get_function_name = max7360_get_function_name, 160 + .get_function_groups = max7360_get_function_groups, 161 + .set_mux = max7360_set_mux, 162 + .strict = true, 163 + }; 164 + 165 + static int max7360_pinctrl_probe(struct platform_device *pdev) 166 + { 167 + struct regmap *regmap; 168 + struct pinctrl_desc *pd; 169 + struct max7360_pinctrl *chip; 170 + struct device *dev = &pdev->dev; 171 + 172 + regmap = dev_get_regmap(dev->parent, NULL); 173 + if (!regmap) 174 + return dev_err_probe(dev, -ENODEV, "Could not get parent regmap\n"); 175 + 176 + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); 177 + if (!chip) 178 + return -ENOMEM; 179 + 180 + pd = &chip->pinctrl_desc; 181 + 182 + pd->pctlops = &max7360_pinctrl_ops; 183 + pd->pmxops = &max7360_pmxops; 184 + pd->name = dev_name(dev); 185 + pd->pins = max7360_pins; 186 + pd->npins = MAX7360_MAX_GPIO; 187 + pd->owner = THIS_MODULE; 188 + 189 + /* 190 + * This MFD sub-device does not have any associated device tree node: 191 + * properties are stored in the device node of the parent (MFD) device 192 + * and this same node is used in phandles of client devices. 193 + * Reuse this device tree node here, as otherwise the pinctrl subsystem 194 + * would be confused by this topology. 195 + */ 196 + device_set_of_node_from_dev(dev, dev->parent); 197 + 198 + chip->pctldev = devm_pinctrl_register(dev, pd, chip); 199 + if (IS_ERR(chip->pctldev)) 200 + return dev_err_probe(dev, PTR_ERR(chip->pctldev), "can't register controller\n"); 201 + 202 + return 0; 203 + } 204 + 205 + static struct platform_driver max7360_pinctrl_driver = { 206 + .driver = { 207 + .name = "max7360-pinctrl", 208 + }, 209 + .probe = max7360_pinctrl_probe, 210 + }; 211 + module_platform_driver(max7360_pinctrl_driver); 212 + 213 + MODULE_DESCRIPTION("MAX7360 pinctrl driver"); 214 + MODULE_AUTHOR("Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>"); 215 + MODULE_LICENSE("GPL");
+7
drivers/power/supply/Kconfig
··· 767 767 rail, ADC for battery and system monitoring, and push-button 768 768 controller. 769 769 770 + config CHARGER_BQ257XX 771 + tristate "TI BQ257XX battery charger family" 772 + depends on MFD_BQ257XX 773 + help 774 + Say Y to enable support for the TI BQ257XX family of battery 775 + charging integrated circuits. 776 + 770 777 config CHARGER_BQ25890 771 778 tristate "TI BQ25890 battery charger driver" 772 779 depends on I2C
+1
drivers/power/supply/Makefile
··· 97 97 obj-$(CONFIG_CHARGER_BQ24257) += bq24257_charger.o 98 98 obj-$(CONFIG_CHARGER_BQ24735) += bq24735-charger.o 99 99 obj-$(CONFIG_CHARGER_BQ2515X) += bq2515x_charger.o 100 + obj-$(CONFIG_CHARGER_BQ257XX) += bq257xx_charger.o 100 101 obj-$(CONFIG_CHARGER_BQ25890) += bq25890_charger.o 101 102 obj-$(CONFIG_CHARGER_BQ25980) += bq25980_charger.o 102 103 obj-$(CONFIG_CHARGER_BQ256XX) += bq256xx_charger.o
+755
drivers/power/supply/bq257xx_charger.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * BQ257XX Battery Charger Driver 4 + * Copyright (C) 2025 Chris Morgan <macromorgan@hotmail.com> 5 + */ 6 + 7 + #include <linux/bitfield.h> 8 + #include <linux/i2c.h> 9 + #include <linux/interrupt.h> 10 + #include <linux/mfd/bq257xx.h> 11 + #include <linux/platform_device.h> 12 + #include <linux/power_supply.h> 13 + #include <linux/property.h> 14 + #include <linux/regmap.h> 15 + 16 + /* Forward declaration of driver data. */ 17 + struct bq257xx_chg; 18 + 19 + /** 20 + * struct bq257xx_chip_info - chip specific routines 21 + * @bq257xx_hw_init: init function for hw 22 + * @bq257xx_hw_shutdown: shutdown function for hw 23 + * @bq257xx_get_state: get and update state of hardware 24 + * @bq257xx_set_ichg: set maximum charge current (in uA) 25 + * @bq257xx_set_vbatreg: set maximum charge voltage (in uV) 26 + * @bq257xx_set_iindpm: set maximum input current (in uA) 27 + */ 28 + struct bq257xx_chip_info { 29 + int (*bq257xx_hw_init)(struct bq257xx_chg *pdata); 30 + void (*bq257xx_hw_shutdown)(struct bq257xx_chg *pdata); 31 + int (*bq257xx_get_state)(struct bq257xx_chg *pdata); 32 + int (*bq257xx_set_ichg)(struct bq257xx_chg *pdata, int ichg); 33 + int (*bq257xx_set_vbatreg)(struct bq257xx_chg *pdata, int vbatreg); 34 + int (*bq257xx_set_iindpm)(struct bq257xx_chg *pdata, int iindpm); 35 + }; 36 + 37 + /** 38 + * struct bq257xx_chg - driver data for charger 39 + * @chip: hw specific functions 40 + * @bq: parent MFD device 41 + * @charger: power supply device 42 + * @online: charger input is present 43 + * @fast_charge: charger is in fast charge mode 44 + * @pre_charge: charger is in pre-charge mode 45 + * @ov_fault: charger reports over voltage fault 46 + * @batoc_fault: charger reports battery over current fault 47 + * @oc_fault: charger reports over current fault 48 + * @usb_type: USB type reported from parent power supply 49 + * @supplied: Status of parent power supply 50 + * @iindpm_max: maximum input current limit (uA) 51 + * @vbat_max: maximum charge voltage (uV) 52 + * @ichg_max: maximum charge current (uA) 53 + * @vsys_min: minimum system voltage (uV) 54 + */ 55 + struct bq257xx_chg { 56 + const struct bq257xx_chip_info *chip; 57 + struct bq257xx_device *bq; 58 + struct power_supply *charger; 59 + bool online; 60 + bool fast_charge; 61 + bool pre_charge; 62 + bool ov_fault; 63 + bool batoc_fault; 64 + bool oc_fault; 65 + int usb_type; 66 + int supplied; 67 + u32 iindpm_max; 68 + u32 vbat_max; 69 + u32 ichg_max; 70 + u32 vsys_min; 71 + }; 72 + 73 + /** 74 + * bq25703_get_state() - Get the current state of the device 75 + * @pdata: driver platform data 76 + * 77 + * Get the current state of the charger. Check if the charger is 78 + * powered, what kind of charge state (if any) the device is in, 79 + * and if there are any active faults. 80 + * 81 + * Return: Returns 0 on success, or error on failure to read device. 82 + */ 83 + static int bq25703_get_state(struct bq257xx_chg *pdata) 84 + { 85 + unsigned int reg; 86 + int ret; 87 + 88 + ret = regmap_read(pdata->bq->regmap, BQ25703_CHARGER_STATUS, &reg); 89 + if (ret) 90 + return ret; 91 + 92 + pdata->online = reg & BQ25703_STS_AC_STAT; 93 + pdata->fast_charge = reg & BQ25703_STS_IN_FCHRG; 94 + pdata->pre_charge = reg & BQ25703_STS_IN_PCHRG; 95 + pdata->ov_fault = reg & BQ25703_STS_FAULT_ACOV; 96 + pdata->batoc_fault = reg & BQ25703_STS_FAULT_BATOC; 97 + pdata->oc_fault = reg & BQ25703_STS_FAULT_ACOC; 98 + 99 + return 0; 100 + } 101 + 102 + /** 103 + * bq25703_get_min_vsys() - Get the minimum system voltage 104 + * @pdata: driver platform data 105 + * @intval: value for minimum voltage 106 + * 107 + * Return: Returns 0 on success or error on failure to read. 108 + */ 109 + static int bq25703_get_min_vsys(struct bq257xx_chg *pdata, int *intval) 110 + { 111 + unsigned int reg; 112 + int ret; 113 + 114 + ret = regmap_read(pdata->bq->regmap, BQ25703_MIN_VSYS, 115 + &reg); 116 + if (ret) 117 + return ret; 118 + 119 + reg = FIELD_GET(BQ25703_MINVSYS_MASK, reg); 120 + *intval = (reg * BQ25703_MINVSYS_STEP_UV) + BQ25703_MINVSYS_MIN_UV; 121 + 122 + return ret; 123 + } 124 + 125 + /** 126 + * bq25703_set_min_vsys() - Set the minimum system voltage 127 + * @pdata: driver platform data 128 + * @vsys: voltage value to set in uV. 129 + * 130 + * This function takes a requested minimum system voltage value, clamps 131 + * it between the minimum supported value by the charger and a user 132 + * defined minimum system value, and then writes the value to the 133 + * appropriate register. 134 + * 135 + * Return: Returns 0 on success or error if an error occurs. 136 + */ 137 + static int bq25703_set_min_vsys(struct bq257xx_chg *pdata, int vsys) 138 + { 139 + unsigned int reg; 140 + int vsys_min = pdata->vsys_min; 141 + 142 + vsys = clamp(vsys, BQ25703_MINVSYS_MIN_UV, vsys_min); 143 + reg = ((vsys - BQ25703_MINVSYS_MIN_UV) / BQ25703_MINVSYS_STEP_UV); 144 + reg = FIELD_PREP(BQ25703_MINVSYS_MASK, reg); 145 + 146 + return regmap_write(pdata->bq->regmap, BQ25703_MIN_VSYS, 147 + reg); 148 + } 149 + 150 + /** 151 + * bq25703_get_cur() - Get the reported current from the battery 152 + * @pdata: driver platform data 153 + * @intval: value of reported battery current 154 + * 155 + * Read the reported current from the battery. Since value is always 156 + * positive set sign to negative if discharging. 157 + * 158 + * Return: Returns 0 on success or error if unable to read value. 159 + */ 160 + static int bq25703_get_cur(struct bq257xx_chg *pdata, int *intval) 161 + { 162 + unsigned int reg; 163 + int ret; 164 + 165 + ret = regmap_read(pdata->bq->regmap, BQ25703_ADCIBAT_CHG, &reg); 166 + if (ret < 0) 167 + return ret; 168 + 169 + if (pdata->online) 170 + *intval = FIELD_GET(BQ25703_ADCIBAT_CHG_MASK, reg) * 171 + BQ25703_ADCIBAT_CHG_STEP_UA; 172 + else 173 + *intval = -(FIELD_GET(BQ25703_ADCIBAT_DISCHG_MASK, reg) * 174 + BQ25703_ADCIBAT_DIS_STEP_UA); 175 + 176 + return ret; 177 + } 178 + 179 + /** 180 + * bq25703_get_ichg_cur() - Get the maximum reported charge current 181 + * @pdata: driver platform data 182 + * @intval: value of maximum reported charge current 183 + * 184 + * Get the maximum reported charge current from the battery. 185 + * 186 + * Return: Returns 0 on success or error if unable to read value. 187 + */ 188 + static int bq25703_get_ichg_cur(struct bq257xx_chg *pdata, int *intval) 189 + { 190 + unsigned int reg; 191 + int ret; 192 + 193 + ret = regmap_read(pdata->bq->regmap, BQ25703_CHARGE_CURRENT, &reg); 194 + if (ret) 195 + return ret; 196 + 197 + *intval = FIELD_GET(BQ25703_ICHG_MASK, reg) * BQ25703_ICHG_STEP_UA; 198 + 199 + return ret; 200 + } 201 + 202 + /** 203 + * bq25703_set_ichg_cur() - Set the maximum charge current 204 + * @pdata: driver platform data 205 + * @ichg: current value to set in uA. 206 + * 207 + * This function takes a requested maximum charge current value, clamps 208 + * it between the minimum supported value by the charger and a user 209 + * defined maximum charging value, and then writes the value to the 210 + * appropriate register. 211 + * 212 + * Return: Returns 0 on success or error if an error occurs. 213 + */ 214 + static int bq25703_set_ichg_cur(struct bq257xx_chg *pdata, int ichg) 215 + { 216 + unsigned int reg; 217 + int ichg_max = pdata->ichg_max; 218 + 219 + ichg = clamp(ichg, BQ25703_ICHG_MIN_UA, ichg_max); 220 + reg = FIELD_PREP(BQ25703_ICHG_MASK, (ichg / BQ25703_ICHG_STEP_UA)); 221 + 222 + return regmap_write(pdata->bq->regmap, BQ25703_CHARGE_CURRENT, 223 + reg); 224 + } 225 + 226 + /** 227 + * bq25703_get_chrg_volt() - Get the maximum set charge voltage 228 + * @pdata: driver platform data 229 + * @intval: maximum charge voltage value 230 + * 231 + * Return: Returns 0 on success or error if unable to read value. 232 + */ 233 + static int bq25703_get_chrg_volt(struct bq257xx_chg *pdata, int *intval) 234 + { 235 + unsigned int reg; 236 + int ret; 237 + 238 + ret = regmap_read(pdata->bq->regmap, BQ25703_MAX_CHARGE_VOLT, 239 + &reg); 240 + if (ret) 241 + return ret; 242 + 243 + *intval = FIELD_GET(BQ25703_MAX_CHARGE_VOLT_MASK, reg) * 244 + BQ25703_VBATREG_STEP_UV; 245 + 246 + return ret; 247 + } 248 + 249 + /** 250 + * bq25703_set_chrg_volt() - Set the maximum charge voltage 251 + * @pdata: driver platform data 252 + * @vbat: voltage value to set in uV. 253 + * 254 + * This function takes a requested maximum charge voltage value, clamps 255 + * it between the minimum supported value by the charger and a user 256 + * defined maximum charging value, and then writes the value to the 257 + * appropriate register. 258 + * 259 + * Return: Returns 0 on success or error if an error occurs. 260 + */ 261 + static int bq25703_set_chrg_volt(struct bq257xx_chg *pdata, int vbat) 262 + { 263 + unsigned int reg; 264 + int vbat_max = pdata->vbat_max; 265 + 266 + vbat = clamp(vbat, BQ25703_VBATREG_MIN_UV, vbat_max); 267 + 268 + reg = FIELD_PREP(BQ25703_MAX_CHARGE_VOLT_MASK, 269 + (vbat / BQ25703_VBATREG_STEP_UV)); 270 + 271 + return regmap_write(pdata->bq->regmap, BQ25703_MAX_CHARGE_VOLT, 272 + reg); 273 + } 274 + 275 + /** 276 + * bq25703_get_iindpm() - Get the maximum set input current 277 + * @pdata: driver platform data 278 + * @intval: maximum input current value 279 + * 280 + * Read the actual input current limit from the device into intval. 281 + * This can differ from the value programmed due to some autonomous 282 + * functions that may be enabled (but are not currently). This is why 283 + * there is a different register used. 284 + * 285 + * Return: Returns 0 on success or error if unable to read register 286 + * value. 287 + */ 288 + static int bq25703_get_iindpm(struct bq257xx_chg *pdata, int *intval) 289 + { 290 + unsigned int reg; 291 + int ret; 292 + 293 + ret = regmap_read(pdata->bq->regmap, BQ25703_IIN_DPM, &reg); 294 + if (ret) 295 + return ret; 296 + 297 + reg = FIELD_GET(BQ25703_IINDPM_MASK, reg); 298 + *intval = (reg * BQ25703_IINDPM_STEP_UA) + BQ25703_IINDPM_OFFSET_UA; 299 + 300 + return ret; 301 + } 302 + 303 + /** 304 + * bq25703_set_iindpm() - Set the maximum input current 305 + * @pdata: driver platform data 306 + * @iindpm: current value in uA. 307 + * 308 + * This function takes a requested maximum input current value, clamps 309 + * it between the minimum supported value by the charger and a user 310 + * defined maximum input value, and then writes the value to the 311 + * appropriate register. 312 + * 313 + * Return: Returns 0 on success or error if an error occurs. 314 + */ 315 + static int bq25703_set_iindpm(struct bq257xx_chg *pdata, int iindpm) 316 + { 317 + unsigned int reg; 318 + int iindpm_max = pdata->iindpm_max; 319 + 320 + iindpm = clamp(iindpm, BQ25703_IINDPM_MIN_UA, iindpm_max); 321 + 322 + reg = ((iindpm - BQ25703_IINDPM_OFFSET_UA) / BQ25703_IINDPM_STEP_UA); 323 + 324 + return regmap_write(pdata->bq->regmap, BQ25703_IIN_HOST, 325 + FIELD_PREP(BQ25703_IINDPM_MASK, reg)); 326 + } 327 + 328 + /** 329 + * bq25703_get_vbat() - Get the reported voltage from the battery 330 + * @pdata: driver platform data 331 + * @intval: value of reported battery voltage 332 + * 333 + * Read value of battery voltage into intval. 334 + * 335 + * Return: Returns 0 on success or error if unable to read value. 336 + */ 337 + static int bq25703_get_vbat(struct bq257xx_chg *pdata, int *intval) 338 + { 339 + unsigned int reg; 340 + int ret; 341 + 342 + ret = regmap_read(pdata->bq->regmap, BQ25703_ADCVSYSVBAT, &reg); 343 + if (ret) 344 + return ret; 345 + 346 + reg = FIELD_GET(BQ25703_ADCVBAT_MASK, reg); 347 + *intval = (reg * BQ25703_ADCVSYSVBAT_STEP) + BQ25703_ADCVSYSVBAT_OFFSET_UV; 348 + 349 + return ret; 350 + } 351 + 352 + /** 353 + * bq25703_hw_init() - Set all the required registers to init the charger 354 + * @pdata: driver platform data 355 + * 356 + * Initialize the BQ25703 by first disabling the watchdog timer (which 357 + * shuts off the charger in the absence of periodic writes). Then, set 358 + * the charge current, charge voltage, minimum system voltage, and 359 + * input current limit. Disable low power mode to allow ADCs and 360 + * interrupts. Enable the ADC, start the ADC, set the ADC scale to 361 + * full, and enable each individual ADC channel. 362 + * 363 + * Return: Returns 0 on success or error code on error. 364 + */ 365 + static int bq25703_hw_init(struct bq257xx_chg *pdata) 366 + { 367 + struct regmap *regmap = pdata->bq->regmap; 368 + int ret = 0; 369 + 370 + regmap_update_bits(regmap, BQ25703_CHARGE_OPTION_0, 371 + BQ25703_WDTMR_ADJ_MASK, 372 + FIELD_PREP(BQ25703_WDTMR_ADJ_MASK, 373 + BQ25703_WDTMR_DISABLE)); 374 + 375 + ret = pdata->chip->bq257xx_set_ichg(pdata, pdata->ichg_max); 376 + if (ret) 377 + return ret; 378 + 379 + ret = pdata->chip->bq257xx_set_vbatreg(pdata, pdata->vbat_max); 380 + if (ret) 381 + return ret; 382 + 383 + ret = bq25703_set_min_vsys(pdata, pdata->vsys_min); 384 + if (ret) 385 + return ret; 386 + 387 + ret = pdata->chip->bq257xx_set_iindpm(pdata, pdata->iindpm_max); 388 + if (ret) 389 + return ret; 390 + 391 + /* Disable low power mode by writing 0 to the register. */ 392 + regmap_update_bits(regmap, BQ25703_CHARGE_OPTION_0, 393 + BQ25703_EN_LWPWR, 0); 394 + 395 + /* Enable the ADC. */ 396 + regmap_update_bits(regmap, BQ25703_ADC_OPTION, 397 + BQ25703_ADC_CONV_EN, BQ25703_ADC_CONV_EN); 398 + 399 + /* Start the ADC. */ 400 + regmap_update_bits(regmap, BQ25703_ADC_OPTION, 401 + BQ25703_ADC_START, BQ25703_ADC_START); 402 + 403 + /* Set the scale of the ADC. */ 404 + regmap_update_bits(regmap, BQ25703_ADC_OPTION, 405 + BQ25703_ADC_FULL_SCALE, BQ25703_ADC_FULL_SCALE); 406 + 407 + /* Enable each of the ADC channels available. */ 408 + regmap_update_bits(regmap, BQ25703_ADC_OPTION, 409 + BQ25703_ADC_CH_MASK, 410 + (BQ25703_ADC_CMPIN_EN | BQ25703_ADC_VBUS_EN | 411 + BQ25703_ADC_PSYS_EN | BQ25703_ADC_IIN_EN | 412 + BQ25703_ADC_IDCHG_EN | BQ25703_ADC_ICHG_EN | 413 + BQ25703_ADC_VSYS_EN | BQ25703_ADC_VBAT_EN)); 414 + 415 + return ret; 416 + } 417 + 418 + /** 419 + * bq25703_hw_shutdown() - Set registers for shutdown 420 + * @pdata: driver platform data 421 + * 422 + * Enable low power mode for the device while in shutdown. 423 + */ 424 + static void bq25703_hw_shutdown(struct bq257xx_chg *pdata) 425 + { 426 + regmap_update_bits(pdata->bq->regmap, BQ25703_CHARGE_OPTION_0, 427 + BQ25703_EN_LWPWR, BQ25703_EN_LWPWR); 428 + } 429 + 430 + static int bq257xx_set_charger_property(struct power_supply *psy, 431 + enum power_supply_property prop, 432 + const union power_supply_propval *val) 433 + { 434 + struct bq257xx_chg *pdata = power_supply_get_drvdata(psy); 435 + 436 + switch (prop) { 437 + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 438 + return pdata->chip->bq257xx_set_iindpm(pdata, val->intval); 439 + 440 + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: 441 + return pdata->chip->bq257xx_set_vbatreg(pdata, val->intval); 442 + 443 + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: 444 + return pdata->chip->bq257xx_set_ichg(pdata, val->intval); 445 + 446 + default: 447 + break; 448 + } 449 + 450 + return -EINVAL; 451 + } 452 + 453 + static int bq257xx_get_charger_property(struct power_supply *psy, 454 + enum power_supply_property psp, 455 + union power_supply_propval *val) 456 + { 457 + struct bq257xx_chg *pdata = power_supply_get_drvdata(psy); 458 + int ret = 0; 459 + 460 + ret = pdata->chip->bq257xx_get_state(pdata); 461 + if (ret) 462 + return ret; 463 + 464 + switch (psp) { 465 + case POWER_SUPPLY_PROP_STATUS: 466 + if (!pdata->online) 467 + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 468 + else if (pdata->fast_charge || pdata->pre_charge) 469 + val->intval = POWER_SUPPLY_STATUS_CHARGING; 470 + else 471 + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 472 + break; 473 + 474 + case POWER_SUPPLY_PROP_HEALTH: 475 + if (pdata->ov_fault || pdata->batoc_fault) 476 + val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; 477 + else if (pdata->oc_fault) 478 + val->intval = POWER_SUPPLY_HEALTH_OVERCURRENT; 479 + else 480 + val->intval = POWER_SUPPLY_HEALTH_GOOD; 481 + break; 482 + 483 + case POWER_SUPPLY_PROP_MANUFACTURER: 484 + val->strval = "Texas Instruments"; 485 + break; 486 + 487 + case POWER_SUPPLY_PROP_ONLINE: 488 + val->intval = pdata->online; 489 + break; 490 + 491 + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 492 + return bq25703_get_iindpm(pdata, &val->intval); 493 + 494 + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: 495 + return bq25703_get_chrg_volt(pdata, &val->intval); 496 + 497 + case POWER_SUPPLY_PROP_CURRENT_NOW: 498 + return bq25703_get_cur(pdata, &val->intval); 499 + 500 + case POWER_SUPPLY_PROP_VOLTAGE_NOW: 501 + return bq25703_get_vbat(pdata, &val->intval); 502 + 503 + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: 504 + return bq25703_get_ichg_cur(pdata, &val->intval); 505 + 506 + case POWER_SUPPLY_PROP_VOLTAGE_MIN: 507 + return bq25703_get_min_vsys(pdata, &val->intval); 508 + 509 + case POWER_SUPPLY_PROP_USB_TYPE: 510 + val->intval = pdata->usb_type; 511 + break; 512 + 513 + default: 514 + return -EINVAL; 515 + } 516 + 517 + return ret; 518 + } 519 + 520 + static enum power_supply_property bq257xx_power_supply_props[] = { 521 + POWER_SUPPLY_PROP_MANUFACTURER, 522 + POWER_SUPPLY_PROP_STATUS, 523 + POWER_SUPPLY_PROP_ONLINE, 524 + POWER_SUPPLY_PROP_HEALTH, 525 + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, 526 + POWER_SUPPLY_PROP_CURRENT_NOW, 527 + POWER_SUPPLY_PROP_VOLTAGE_NOW, 528 + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, 529 + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, 530 + POWER_SUPPLY_PROP_VOLTAGE_MIN, 531 + POWER_SUPPLY_PROP_USB_TYPE, 532 + }; 533 + 534 + static int bq257xx_property_is_writeable(struct power_supply *psy, 535 + enum power_supply_property prop) 536 + { 537 + switch (prop) { 538 + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: 539 + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: 540 + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 541 + return true; 542 + default: 543 + return false; 544 + } 545 + } 546 + 547 + /** 548 + * bq257xx_external_power_changed() - Handler for external power change 549 + * @psy: Power supply data 550 + * 551 + * When the external power into the charger is changed, check the USB 552 + * type so that it can be reported. Additionally, update the max input 553 + * current and max charging current to the value reported if it is a 554 + * USB PD charger, otherwise use the default value. Note that each time 555 + * a charger is removed the max charge current register is erased, so 556 + * it must be set again each time the input changes or the device will 557 + * not charge. 558 + */ 559 + static void bq257xx_external_power_changed(struct power_supply *psy) 560 + { 561 + struct bq257xx_chg *pdata = power_supply_get_drvdata(psy); 562 + union power_supply_propval val; 563 + int ret; 564 + int imax = pdata->iindpm_max; 565 + 566 + pdata->chip->bq257xx_get_state(pdata); 567 + 568 + pdata->supplied = power_supply_am_i_supplied(pdata->charger); 569 + if (pdata->supplied < 0) 570 + return; 571 + 572 + if (pdata->supplied == 0) 573 + goto out; 574 + 575 + ret = power_supply_get_property_from_supplier(psy, 576 + POWER_SUPPLY_PROP_USB_TYPE, 577 + &val); 578 + if (ret) 579 + return; 580 + 581 + pdata->usb_type = val.intval; 582 + 583 + if ((pdata->usb_type == POWER_SUPPLY_USB_TYPE_PD) || 584 + (pdata->usb_type == POWER_SUPPLY_USB_TYPE_PD_DRP) || 585 + (pdata->usb_type == POWER_SUPPLY_USB_TYPE_PD_PPS)) { 586 + ret = power_supply_get_property_from_supplier(psy, 587 + POWER_SUPPLY_PROP_CURRENT_MAX, 588 + &val); 589 + if (ret) 590 + return; 591 + 592 + if (val.intval) 593 + imax = val.intval; 594 + } 595 + 596 + if (pdata->supplied) { 597 + pdata->chip->bq257xx_set_ichg(pdata, pdata->ichg_max); 598 + pdata->chip->bq257xx_set_iindpm(pdata, imax); 599 + pdata->chip->bq257xx_set_vbatreg(pdata, pdata->vbat_max); 600 + } 601 + 602 + out: 603 + power_supply_changed(psy); 604 + } 605 + 606 + static irqreturn_t bq257xx_irq_handler_thread(int irq, void *private) 607 + { 608 + struct bq257xx_chg *pdata = private; 609 + 610 + bq257xx_external_power_changed(pdata->charger); 611 + return IRQ_HANDLED; 612 + } 613 + 614 + static const struct power_supply_desc bq257xx_power_supply_desc = { 615 + .name = "bq257xx-charger", 616 + .type = POWER_SUPPLY_TYPE_USB, 617 + .usb_types = BIT(POWER_SUPPLY_USB_TYPE_C) | 618 + BIT(POWER_SUPPLY_USB_TYPE_PD) | 619 + BIT(POWER_SUPPLY_USB_TYPE_PD_DRP) | 620 + BIT(POWER_SUPPLY_USB_TYPE_PD_PPS) | 621 + BIT(POWER_SUPPLY_USB_TYPE_UNKNOWN), 622 + .properties = bq257xx_power_supply_props, 623 + .num_properties = ARRAY_SIZE(bq257xx_power_supply_props), 624 + .get_property = bq257xx_get_charger_property, 625 + .set_property = bq257xx_set_charger_property, 626 + .property_is_writeable = bq257xx_property_is_writeable, 627 + .external_power_changed = bq257xx_external_power_changed, 628 + }; 629 + 630 + static const struct bq257xx_chip_info bq25703_chip_info = { 631 + .bq257xx_hw_init = &bq25703_hw_init, 632 + .bq257xx_hw_shutdown = &bq25703_hw_shutdown, 633 + .bq257xx_get_state = &bq25703_get_state, 634 + .bq257xx_set_ichg = &bq25703_set_ichg_cur, 635 + .bq257xx_set_vbatreg = &bq25703_set_chrg_volt, 636 + .bq257xx_set_iindpm = &bq25703_set_iindpm, 637 + }; 638 + 639 + /** 640 + * bq257xx_parse_dt() - Parse the device tree for required properties 641 + * @pdata: driver platform data 642 + * @psy_cfg: power supply config data 643 + * @dev: device struct 644 + * 645 + * Read the device tree to identify the minimum system voltage, the 646 + * maximum charge current, the maximum charge voltage, and the maximum 647 + * input current. 648 + * 649 + * Return: Returns 0 on success or error code on error. 650 + */ 651 + static int bq257xx_parse_dt(struct bq257xx_chg *pdata, 652 + struct power_supply_config *psy_cfg, struct device *dev) 653 + { 654 + struct power_supply_battery_info *bat_info; 655 + int ret; 656 + 657 + ret = power_supply_get_battery_info(pdata->charger, 658 + &bat_info); 659 + if (ret) 660 + return dev_err_probe(dev, ret, 661 + "Unable to get battery info\n"); 662 + 663 + if ((bat_info->voltage_min_design_uv <= 0) || 664 + (bat_info->constant_charge_voltage_max_uv <= 0) || 665 + (bat_info->constant_charge_current_max_ua <= 0)) 666 + return dev_err_probe(dev, -EINVAL, 667 + "Required bat info missing or invalid\n"); 668 + 669 + pdata->vsys_min = bat_info->voltage_min_design_uv; 670 + pdata->vbat_max = bat_info->constant_charge_voltage_max_uv; 671 + pdata->ichg_max = bat_info->constant_charge_current_max_ua; 672 + 673 + power_supply_put_battery_info(pdata->charger, bat_info); 674 + 675 + ret = device_property_read_u32(dev, 676 + "input-current-limit-microamp", 677 + &pdata->iindpm_max); 678 + if (ret) 679 + pdata->iindpm_max = BQ25703_IINDPM_DEFAULT_UA; 680 + 681 + return 0; 682 + } 683 + 684 + static int bq257xx_charger_probe(struct platform_device *pdev) 685 + { 686 + struct device *dev = &pdev->dev; 687 + struct bq257xx_device *bq = dev_get_drvdata(pdev->dev.parent); 688 + struct bq257xx_chg *pdata; 689 + struct power_supply_config psy_cfg = { }; 690 + int ret; 691 + 692 + device_set_of_node_from_dev(dev, pdev->dev.parent); 693 + 694 + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); 695 + if (!pdata) 696 + return -ENOMEM; 697 + 698 + pdata->bq = bq; 699 + pdata->chip = &bq25703_chip_info; 700 + 701 + platform_set_drvdata(pdev, pdata); 702 + 703 + psy_cfg.drv_data = pdata; 704 + psy_cfg.fwnode = dev_fwnode(dev); 705 + 706 + pdata->charger = devm_power_supply_register(dev, 707 + &bq257xx_power_supply_desc, 708 + &psy_cfg); 709 + if (IS_ERR(pdata->charger)) 710 + return dev_err_probe(dev, PTR_ERR(pdata->charger), 711 + "Power supply register charger failed\n"); 712 + 713 + ret = bq257xx_parse_dt(pdata, &psy_cfg, dev); 714 + if (ret) 715 + return ret; 716 + 717 + ret = pdata->chip->bq257xx_hw_init(pdata); 718 + if (ret) 719 + return dev_err_probe(dev, ret, "Cannot initialize the charger\n"); 720 + 721 + platform_set_drvdata(pdev, pdata); 722 + 723 + if (bq->client->irq) { 724 + ret = devm_request_threaded_irq(dev, bq->client->irq, NULL, 725 + bq257xx_irq_handler_thread, 726 + IRQF_TRIGGER_RISING | 727 + IRQF_TRIGGER_FALLING | 728 + IRQF_ONESHOT, 729 + dev_name(&bq->client->dev), pdata); 730 + if (ret < 0) 731 + dev_err_probe(dev, ret, "Charger get irq failed\n"); 732 + } 733 + 734 + return ret; 735 + } 736 + 737 + static void bq257xx_charger_shutdown(struct platform_device *pdev) 738 + { 739 + struct bq257xx_chg *pdata = platform_get_drvdata(pdev); 740 + 741 + pdata->chip->bq257xx_hw_shutdown(pdata); 742 + } 743 + 744 + static struct platform_driver bq257xx_chg_driver = { 745 + .driver = { 746 + .name = "bq257xx-charger", 747 + }, 748 + .probe = bq257xx_charger_probe, 749 + .shutdown = bq257xx_charger_shutdown, 750 + }; 751 + module_platform_driver(bq257xx_chg_driver); 752 + 753 + MODULE_DESCRIPTION("bq257xx charger driver"); 754 + MODULE_AUTHOR("Chris Morgan <macromorgan@hotmail.com>"); 755 + MODULE_LICENSE("GPL");
+10
drivers/pwm/Kconfig
··· 432 432 To compile this driver as a module, choose M here: the module 433 433 will be called pwm-lpss-platform. 434 434 435 + config PWM_MAX7360 436 + tristate "MAX7360 PWMs" 437 + depends on MFD_MAX7360 438 + help 439 + PWM driver for Maxim Integrated MAX7360 multifunction device, with 440 + support for up to 8 PWM outputs. 441 + 442 + To compile this driver as a module, choose M here: the module 443 + will be called pwm-max7360. 444 + 435 445 config PWM_MC33XS2410 436 446 tristate "MC33XS2410 PWM support" 437 447 depends on OF
+1
drivers/pwm/Makefile
··· 38 38 obj-$(CONFIG_PWM_LPSS) += pwm-lpss.o 39 39 obj-$(CONFIG_PWM_LPSS_PCI) += pwm-lpss-pci.o 40 40 obj-$(CONFIG_PWM_LPSS_PLATFORM) += pwm-lpss-platform.o 41 + obj-$(CONFIG_PWM_MAX7360) += pwm-max7360.o 41 42 obj-$(CONFIG_PWM_MC33XS2410) += pwm-mc33xs2410.o 42 43 obj-$(CONFIG_PWM_MEDIATEK) += pwm-mediatek.o 43 44 obj-$(CONFIG_PWM_MESON) += pwm-meson.o
+209
drivers/pwm/pwm-max7360.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright 2025 Bootlin 4 + * 5 + * Author: Kamel BOUHARA <kamel.bouhara@bootlin.com> 6 + * Author: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> 7 + * 8 + * PWM functionality of the MAX7360 multi-function device. 9 + * https://www.analog.com/media/en/technical-documentation/data-sheets/MAX7360.pdf 10 + * 11 + * Limitations: 12 + * - Only supports normal polarity. 13 + * - The period is fixed to 2 ms. 14 + * - Only the duty cycle can be changed, new values are applied at the beginning 15 + * of the next cycle. 16 + * - When disabled, the output is put in Hi-Z immediately. 17 + */ 18 + #include <linux/bits.h> 19 + #include <linux/dev_printk.h> 20 + #include <linux/err.h> 21 + #include <linux/math64.h> 22 + #include <linux/mfd/max7360.h> 23 + #include <linux/minmax.h> 24 + #include <linux/mod_devicetable.h> 25 + #include <linux/module.h> 26 + #include <linux/platform_device.h> 27 + #include <linux/pwm.h> 28 + #include <linux/regmap.h> 29 + #include <linux/time.h> 30 + #include <linux/types.h> 31 + 32 + #define MAX7360_NUM_PWMS 8 33 + #define MAX7360_PWM_MAX 255 34 + #define MAX7360_PWM_STEPS 256 35 + #define MAX7360_PWM_PERIOD_NS (2 * NSEC_PER_MSEC) 36 + 37 + struct max7360_pwm_waveform { 38 + u8 duty_steps; 39 + bool enabled; 40 + }; 41 + 42 + static int max7360_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) 43 + { 44 + struct regmap *regmap = pwmchip_get_drvdata(chip); 45 + 46 + /* 47 + * Make sure we use the individual PWM configuration register and not 48 + * the global one. 49 + * We never need to use the global one, so there is no need to revert 50 + * that in the .free() callback. 51 + */ 52 + return regmap_write_bits(regmap, MAX7360_REG_PWMCFG(pwm->hwpwm), 53 + MAX7360_PORT_CFG_COMMON_PWM, 0); 54 + } 55 + 56 + static int max7360_pwm_round_waveform_tohw(struct pwm_chip *chip, 57 + struct pwm_device *pwm, 58 + const struct pwm_waveform *wf, 59 + void *_wfhw) 60 + { 61 + struct max7360_pwm_waveform *wfhw = _wfhw; 62 + u64 duty_steps; 63 + 64 + /* 65 + * Ignore user provided values for period_length_ns and duty_offset_ns: 66 + * we only support fixed period of MAX7360_PWM_PERIOD_NS and offset of 0. 67 + * Values from 0 to 254 as duty_steps will provide duty cycles of 0/256 68 + * to 254/256, while value 255 will provide a duty cycle of 100%. 69 + */ 70 + if (wf->duty_length_ns >= MAX7360_PWM_PERIOD_NS) { 71 + duty_steps = MAX7360_PWM_MAX; 72 + } else { 73 + duty_steps = (u32)wf->duty_length_ns * MAX7360_PWM_STEPS / MAX7360_PWM_PERIOD_NS; 74 + if (duty_steps == MAX7360_PWM_MAX) 75 + duty_steps = MAX7360_PWM_MAX - 1; 76 + } 77 + 78 + wfhw->duty_steps = min(MAX7360_PWM_MAX, duty_steps); 79 + wfhw->enabled = !!wf->period_length_ns; 80 + 81 + if (wf->period_length_ns && wf->period_length_ns < MAX7360_PWM_PERIOD_NS) 82 + return 1; 83 + else 84 + return 0; 85 + } 86 + 87 + static int max7360_pwm_round_waveform_fromhw(struct pwm_chip *chip, struct pwm_device *pwm, 88 + const void *_wfhw, struct pwm_waveform *wf) 89 + { 90 + const struct max7360_pwm_waveform *wfhw = _wfhw; 91 + 92 + wf->period_length_ns = wfhw->enabled ? MAX7360_PWM_PERIOD_NS : 0; 93 + wf->duty_offset_ns = 0; 94 + 95 + if (wfhw->enabled) { 96 + if (wfhw->duty_steps == MAX7360_PWM_MAX) 97 + wf->duty_length_ns = MAX7360_PWM_PERIOD_NS; 98 + else 99 + wf->duty_length_ns = DIV_ROUND_UP(wfhw->duty_steps * MAX7360_PWM_PERIOD_NS, 100 + MAX7360_PWM_STEPS); 101 + } else { 102 + wf->duty_length_ns = 0; 103 + } 104 + 105 + return 0; 106 + } 107 + 108 + static int max7360_pwm_write_waveform(struct pwm_chip *chip, 109 + struct pwm_device *pwm, 110 + const void *_wfhw) 111 + { 112 + struct regmap *regmap = pwmchip_get_drvdata(chip); 113 + const struct max7360_pwm_waveform *wfhw = _wfhw; 114 + unsigned int val; 115 + int ret; 116 + 117 + if (wfhw->enabled) { 118 + ret = regmap_write(regmap, MAX7360_REG_PWM(pwm->hwpwm), wfhw->duty_steps); 119 + if (ret) 120 + return ret; 121 + } 122 + 123 + val = wfhw->enabled ? BIT(pwm->hwpwm) : 0; 124 + return regmap_write_bits(regmap, MAX7360_REG_GPIOCTRL, BIT(pwm->hwpwm), val); 125 + } 126 + 127 + static int max7360_pwm_read_waveform(struct pwm_chip *chip, 128 + struct pwm_device *pwm, 129 + void *_wfhw) 130 + { 131 + struct regmap *regmap = pwmchip_get_drvdata(chip); 132 + struct max7360_pwm_waveform *wfhw = _wfhw; 133 + unsigned int val; 134 + int ret; 135 + 136 + ret = regmap_read(regmap, MAX7360_REG_GPIOCTRL, &val); 137 + if (ret) 138 + return ret; 139 + 140 + if (val & BIT(pwm->hwpwm)) { 141 + wfhw->enabled = true; 142 + ret = regmap_read(regmap, MAX7360_REG_PWM(pwm->hwpwm), &val); 143 + if (ret) 144 + return ret; 145 + 146 + wfhw->duty_steps = val; 147 + } else { 148 + wfhw->enabled = false; 149 + wfhw->duty_steps = 0; 150 + } 151 + 152 + return 0; 153 + } 154 + 155 + static const struct pwm_ops max7360_pwm_ops = { 156 + .request = max7360_pwm_request, 157 + .round_waveform_tohw = max7360_pwm_round_waveform_tohw, 158 + .round_waveform_fromhw = max7360_pwm_round_waveform_fromhw, 159 + .read_waveform = max7360_pwm_read_waveform, 160 + .write_waveform = max7360_pwm_write_waveform, 161 + }; 162 + 163 + static int max7360_pwm_probe(struct platform_device *pdev) 164 + { 165 + struct device *dev = &pdev->dev; 166 + struct pwm_chip *chip; 167 + struct regmap *regmap; 168 + int ret; 169 + 170 + regmap = dev_get_regmap(dev->parent, NULL); 171 + if (!regmap) 172 + return dev_err_probe(dev, -ENODEV, "Could not get parent regmap\n"); 173 + 174 + /* 175 + * This MFD sub-device does not have any associated device tree node: 176 + * properties are stored in the device node of the parent (MFD) device 177 + * and this same node is used in phandles of client devices. 178 + * Reuse this device tree node here, as otherwise the PWM subsystem 179 + * would be confused by this topology. 180 + */ 181 + device_set_of_node_from_dev(dev, dev->parent); 182 + 183 + chip = devm_pwmchip_alloc(dev, MAX7360_NUM_PWMS, 0); 184 + if (IS_ERR(chip)) 185 + return PTR_ERR(chip); 186 + chip->ops = &max7360_pwm_ops; 187 + 188 + pwmchip_set_drvdata(chip, regmap); 189 + 190 + ret = devm_pwmchip_add(dev, chip); 191 + if (ret) 192 + return dev_err_probe(dev, ret, "Failed to add PWM chip\n"); 193 + 194 + return 0; 195 + } 196 + 197 + static struct platform_driver max7360_pwm_driver = { 198 + .driver = { 199 + .name = "max7360-pwm", 200 + .probe_type = PROBE_PREFER_ASYNCHRONOUS, 201 + }, 202 + .probe = max7360_pwm_probe, 203 + }; 204 + module_platform_driver(max7360_pwm_driver); 205 + 206 + MODULE_DESCRIPTION("MAX7360 PWM driver"); 207 + MODULE_AUTHOR("Kamel BOUHARA <kamel.bouhara@bootlin.com>"); 208 + MODULE_AUTHOR("Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>"); 209 + MODULE_LICENSE("GPL");
+8
drivers/regulator/Kconfig
··· 297 297 This driver can also be built as a module. If so, the module 298 298 will be called bd96801-regulator. 299 299 300 + config REGULATOR_BQ257XX 301 + tristate "TI BQ257XX regulator family" 302 + depends on MFD_BQ257XX 303 + depends on GPIOLIB || COMPILE_TEST 304 + help 305 + Say Y to enable support for the boost regulator function of 306 + the BQ257XX family of charger circuits. 307 + 300 308 config REGULATOR_CPCAP 301 309 tristate "Motorola CPCAP regulator" 302 310 depends on MFD_CPCAP
+1
drivers/regulator/Makefile
··· 38 38 obj-$(CONFIG_REGULATOR_BD718XX) += bd718x7-regulator.o 39 39 obj-$(CONFIG_REGULATOR_BD9571MWV) += bd9571mwv-regulator.o 40 40 obj-$(CONFIG_REGULATOR_BD957XMUF) += bd9576-regulator.o 41 + obj-$(CONFIG_REGULATOR_BQ257XX) += bq257xx-regulator.o 41 42 obj-$(CONFIG_REGULATOR_DA903X) += da903x-regulator.o 42 43 obj-$(CONFIG_REGULATOR_BD96801) += bd96801-regulator.o 43 44 obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o
+186
drivers/regulator/bq257xx-regulator.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * BQ257XX Battery Charger Driver 4 + * Copyright (C) 2025 Chris Morgan <macromorgan@hotmail.com> 5 + */ 6 + 7 + #include <linux/bitfield.h> 8 + #include <linux/err.h> 9 + #include <linux/gpio/consumer.h> 10 + #include <linux/mfd/bq257xx.h> 11 + #include <linux/of.h> 12 + #include <linux/platform_device.h> 13 + #include <linux/regmap.h> 14 + #include <linux/regulator/driver.h> 15 + #include <linux/regulator/of_regulator.h> 16 + 17 + struct bq257xx_reg_data { 18 + struct bq257xx_device *bq; 19 + struct regulator_dev *bq257xx_reg; 20 + struct gpio_desc *otg_en_gpio; 21 + struct regulator_desc desc; 22 + }; 23 + 24 + static int bq25703_vbus_get_cur_limit(struct regulator_dev *rdev) 25 + { 26 + struct bq257xx_reg_data *pdata = rdev_get_drvdata(rdev); 27 + int ret; 28 + unsigned int reg; 29 + 30 + ret = regmap_read(pdata->bq->regmap, BQ25703_OTG_CURRENT, &reg); 31 + if (ret) 32 + return ret; 33 + return FIELD_GET(BQ25703_OTG_CUR_MASK, reg) * BQ25703_OTG_CUR_STEP_UA; 34 + } 35 + 36 + /* 37 + * Check if the minimum current and maximum current requested are 38 + * sane values, then set the register accordingly. 39 + */ 40 + static int bq25703_vbus_set_cur_limit(struct regulator_dev *rdev, 41 + int min_uA, int max_uA) 42 + { 43 + struct bq257xx_reg_data *pdata = rdev_get_drvdata(rdev); 44 + unsigned int reg; 45 + 46 + if ((min_uA > BQ25703_OTG_CUR_MAX_UA) || (max_uA < 0)) 47 + return -EINVAL; 48 + 49 + reg = (max_uA / BQ25703_OTG_CUR_STEP_UA); 50 + 51 + /* Catch rounding errors since our step is 50000uA. */ 52 + if ((reg * BQ25703_OTG_CUR_STEP_UA) < min_uA) 53 + return -EINVAL; 54 + 55 + return regmap_write(pdata->bq->regmap, BQ25703_OTG_CURRENT, 56 + FIELD_PREP(BQ25703_OTG_CUR_MASK, reg)); 57 + } 58 + 59 + static int bq25703_vbus_enable(struct regulator_dev *rdev) 60 + { 61 + struct bq257xx_reg_data *pdata = rdev_get_drvdata(rdev); 62 + 63 + if (pdata->otg_en_gpio) 64 + gpiod_set_value_cansleep(pdata->otg_en_gpio, 1); 65 + return regulator_enable_regmap(rdev); 66 + } 67 + 68 + static int bq25703_vbus_disable(struct regulator_dev *rdev) 69 + { 70 + struct bq257xx_reg_data *pdata = rdev_get_drvdata(rdev); 71 + 72 + if (pdata->otg_en_gpio) 73 + gpiod_set_value_cansleep(pdata->otg_en_gpio, 0); 74 + return regulator_disable_regmap(rdev); 75 + } 76 + 77 + static const struct regulator_ops bq25703_vbus_ops = { 78 + .enable = bq25703_vbus_enable, 79 + .disable = bq25703_vbus_disable, 80 + .is_enabled = regulator_is_enabled_regmap, 81 + .list_voltage = regulator_list_voltage_linear, 82 + .get_voltage_sel = regulator_get_voltage_sel_regmap, 83 + .set_voltage_sel = regulator_set_voltage_sel_regmap, 84 + .get_current_limit = bq25703_vbus_get_cur_limit, 85 + .set_current_limit = bq25703_vbus_set_cur_limit, 86 + }; 87 + 88 + static const struct regulator_desc bq25703_vbus_desc = { 89 + .name = "vbus", 90 + .of_match = of_match_ptr("vbus"), 91 + .regulators_node = of_match_ptr("regulators"), 92 + .type = REGULATOR_VOLTAGE, 93 + .owner = THIS_MODULE, 94 + .ops = &bq25703_vbus_ops, 95 + .min_uV = BQ25703_OTG_VOLT_MIN_UV, 96 + .uV_step = BQ25703_OTG_VOLT_STEP_UV, 97 + .n_voltages = BQ25703_OTG_VOLT_NUM_VOLT, 98 + .enable_mask = BQ25703_EN_OTG_MASK, 99 + .enable_reg = BQ25703_CHARGE_OPTION_3, 100 + .enable_val = BQ25703_EN_OTG_MASK, 101 + .disable_val = 0, 102 + .vsel_reg = BQ25703_OTG_VOLT, 103 + .vsel_mask = BQ25703_OTG_VOLT_MASK, 104 + }; 105 + 106 + /* Get optional GPIO for OTG regulator enable. */ 107 + static void bq257xx_reg_dt_parse_gpio(struct platform_device *pdev) 108 + { 109 + struct device_node *child, *subchild; 110 + struct bq257xx_reg_data *pdata = platform_get_drvdata(pdev); 111 + 112 + child = of_get_child_by_name(pdev->dev.of_node, 113 + pdata->desc.regulators_node); 114 + if (!child) 115 + return; 116 + 117 + subchild = of_get_child_by_name(child, pdata->desc.of_match); 118 + if (!subchild) 119 + return; 120 + 121 + of_node_put(child); 122 + 123 + pdata->otg_en_gpio = devm_fwnode_gpiod_get_index(&pdev->dev, 124 + of_fwnode_handle(subchild), 125 + "enable", 0, 126 + GPIOD_OUT_LOW, 127 + pdata->desc.of_match); 128 + 129 + of_node_put(subchild); 130 + 131 + if (IS_ERR(pdata->otg_en_gpio)) { 132 + dev_err(&pdev->dev, "Error getting enable gpio: %ld\n", 133 + PTR_ERR(pdata->otg_en_gpio)); 134 + return; 135 + } 136 + } 137 + 138 + static int bq257xx_regulator_probe(struct platform_device *pdev) 139 + { 140 + struct device *dev = &pdev->dev; 141 + struct bq257xx_device *bq = dev_get_drvdata(pdev->dev.parent); 142 + struct bq257xx_reg_data *pdata; 143 + struct device_node *np = dev->of_node; 144 + struct regulator_config cfg = {}; 145 + 146 + pdev->dev.of_node = pdev->dev.parent->of_node; 147 + pdev->dev.of_node_reused = true; 148 + 149 + pdata = devm_kzalloc(&pdev->dev, sizeof(struct bq257xx_reg_data), GFP_KERNEL); 150 + if (!pdata) 151 + return -ENOMEM; 152 + 153 + pdata->bq = bq; 154 + pdata->desc = bq25703_vbus_desc; 155 + 156 + platform_set_drvdata(pdev, pdata); 157 + bq257xx_reg_dt_parse_gpio(pdev); 158 + 159 + cfg.dev = &pdev->dev; 160 + cfg.driver_data = pdata; 161 + cfg.of_node = np; 162 + cfg.regmap = dev_get_regmap(pdev->dev.parent, NULL); 163 + if (!cfg.regmap) 164 + return -ENODEV; 165 + 166 + pdata->bq257xx_reg = devm_regulator_register(dev, &pdata->desc, &cfg); 167 + if (IS_ERR(pdata->bq257xx_reg)) { 168 + return dev_err_probe(&pdev->dev, PTR_ERR(pdata->bq257xx_reg), 169 + "error registering bq257xx regulator"); 170 + } 171 + 172 + return 0; 173 + } 174 + 175 + static struct platform_driver bq257xx_reg_driver = { 176 + .driver = { 177 + .name = "bq257xx-regulator", 178 + }, 179 + .probe = bq257xx_regulator_probe, 180 + }; 181 + 182 + module_platform_driver(bq257xx_reg_driver); 183 + 184 + MODULE_DESCRIPTION("bq257xx regulator driver"); 185 + MODULE_AUTHOR("Chris Morgan <macromorgan@hotmail.com>"); 186 + 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
-13
drivers/rtc/rtc-mc13xxx.c
··· 137 137 } 138 138 139 139 if (!priv->valid) { 140 - ret = mc13xxx_irq_ack(priv->mc13xxx, MC13XXX_IRQ_RTCRST); 141 - if (unlikely(ret)) 142 - goto out; 143 - 144 140 ret = mc13xxx_irq_unmask(priv->mc13xxx, MC13XXX_IRQ_RTCRST); 145 141 } 146 142 ··· 204 208 if (unlikely(ret)) 205 209 goto out; 206 210 207 - ret = mc13xxx_irq_ack(priv->mc13xxx, MC13XXX_IRQ_TODA); 208 - if (unlikely(ret)) 209 - goto out; 210 - 211 211 s1970 = rtc_tm_to_time64(&alarm->time); 212 212 213 213 dev_dbg(dev, "%s: %s %lld\n", __func__, alarm->enabled ? "on" : "off", ··· 231 239 static irqreturn_t mc13xxx_rtc_alarm_handler(int irq, void *dev) 232 240 { 233 241 struct mc13xxx_rtc *priv = dev; 234 - struct mc13xxx *mc13xxx = priv->mc13xxx; 235 242 236 243 rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_AF); 237 - 238 - mc13xxx_irq_ack(mc13xxx, irq); 239 244 240 245 return IRQ_HANDLED; 241 246 } ··· 281 292 priv->rtc->range_max = (timeu64_t)(1 << 15) * SEC_PER_DAY - 1; 282 293 283 294 mc13xxx_lock(mc13xxx); 284 - 285 - mc13xxx_irq_ack(mc13xxx, MC13XXX_IRQ_RTCRST); 286 295 287 296 ret = mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_RTCRST, 288 297 mc13xxx_rtc_reset_handler, DRIVER_NAME, priv);
+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");
+18
include/linux/gpio/regmap.h
··· 6 6 struct device; 7 7 struct fwnode_handle; 8 8 struct gpio_regmap; 9 + struct gpio_chip; 9 10 struct irq_domain; 10 11 struct regmap; 11 12 ··· 41 40 * @drvdata: (Optional) Pointer to driver specific data which is 42 41 * not used by gpio-remap but is provided "as is" to the 43 42 * driver callback(s). 43 + * @init_valid_mask: (Optional) Routine to initialize @valid_mask, to be used 44 + * if not all GPIOs are valid. 45 + * @regmap_irq_chip: (Optional) Pointer on an regmap_irq_chip structure. If 46 + * set, a regmap-irq device will be created and the IRQ 47 + * domain will be set accordingly. 48 + * @regmap_irq_line (Optional) The IRQ the device uses to signal interrupts. 49 + * @regmap_irq_flags (Optional) The IRQF_ flags to use for the interrupt. 44 50 * 45 51 * The ->reg_mask_xlate translates a given base address and GPIO offset to 46 52 * register and mask pair. The base address is one of the given register ··· 86 78 int ngpio_per_reg; 87 79 struct irq_domain *irq_domain; 88 80 81 + #ifdef CONFIG_REGMAP_IRQ 82 + struct regmap_irq_chip *regmap_irq_chip; 83 + int regmap_irq_line; 84 + unsigned long regmap_irq_flags; 85 + #endif 86 + 89 87 int (*reg_mask_xlate)(struct gpio_regmap *gpio, unsigned int base, 90 88 unsigned int offset, unsigned int *reg, 91 89 unsigned int *mask); 90 + 91 + int (*init_valid_mask)(struct gpio_chip *gc, 92 + unsigned long *valid_mask, 93 + unsigned int ngpios); 92 94 93 95 void *drvdata; 94 96 };
+104
include/linux/mfd/bq257xx.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Register definitions for TI BQ257XX 4 + * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ 5 + */ 6 + 7 + #define BQ25703_CHARGE_OPTION_0 0x00 8 + #define BQ25703_CHARGE_CURRENT 0x02 9 + #define BQ25703_MAX_CHARGE_VOLT 0x04 10 + #define BQ25703_OTG_VOLT 0x06 11 + #define BQ25703_OTG_CURRENT 0x08 12 + #define BQ25703_INPUT_VOLTAGE 0x0a 13 + #define BQ25703_MIN_VSYS 0x0c 14 + #define BQ25703_IIN_HOST 0x0e 15 + #define BQ25703_CHARGER_STATUS 0x20 16 + #define BQ25703_PROCHOT_STATUS 0x22 17 + #define BQ25703_IIN_DPM 0x24 18 + #define BQ25703_ADCIBAT_CHG 0x28 19 + #define BQ25703_ADCIINCMPIN 0x2a 20 + #define BQ25703_ADCVSYSVBAT 0x2c 21 + #define BQ25703_MANUFACT_DEV_ID 0x2e 22 + #define BQ25703_CHARGE_OPTION_1 0x30 23 + #define BQ25703_CHARGE_OPTION_2 0x32 24 + #define BQ25703_CHARGE_OPTION_3 0x34 25 + #define BQ25703_ADC_OPTION 0x3a 26 + 27 + #define BQ25703_EN_LWPWR BIT(15) 28 + #define BQ25703_WDTMR_ADJ_MASK GENMASK(14, 13) 29 + #define BQ25703_WDTMR_DISABLE 0 30 + #define BQ25703_WDTMR_5_SEC 1 31 + #define BQ25703_WDTMR_88_SEC 2 32 + #define BQ25703_WDTMR_175_SEC 3 33 + 34 + #define BQ25703_ICHG_MASK GENMASK(12, 6) 35 + #define BQ25703_ICHG_STEP_UA 64000 36 + #define BQ25703_ICHG_MIN_UA 64000 37 + #define BQ25703_ICHG_MAX_UA 8128000 38 + 39 + #define BQ25703_MAX_CHARGE_VOLT_MASK GENMASK(15, 4) 40 + #define BQ25703_VBATREG_STEP_UV 16000 41 + #define BQ25703_VBATREG_MIN_UV 1024000 42 + #define BQ25703_VBATREG_MAX_UV 19200000 43 + 44 + #define BQ25703_OTG_VOLT_MASK GENMASK(13, 6) 45 + #define BQ25703_OTG_VOLT_STEP_UV 64000 46 + #define BQ25703_OTG_VOLT_MIN_UV 4480000 47 + #define BQ25703_OTG_VOLT_MAX_UV 20800000 48 + #define BQ25703_OTG_VOLT_NUM_VOLT 256 49 + 50 + #define BQ25703_OTG_CUR_MASK GENMASK(14, 8) 51 + #define BQ25703_OTG_CUR_STEP_UA 50000 52 + #define BQ25703_OTG_CUR_MAX_UA 6350000 53 + 54 + #define BQ25703_MINVSYS_MASK GENMASK(13, 8) 55 + #define BQ25703_MINVSYS_STEP_UV 256000 56 + #define BQ25703_MINVSYS_MIN_UV 1024000 57 + #define BQ25703_MINVSYS_MAX_UV 16128000 58 + 59 + #define BQ25703_STS_AC_STAT BIT(15) 60 + #define BQ25703_STS_IN_FCHRG BIT(10) 61 + #define BQ25703_STS_IN_PCHRG BIT(9) 62 + #define BQ25703_STS_FAULT_ACOV BIT(7) 63 + #define BQ25703_STS_FAULT_BATOC BIT(6) 64 + #define BQ25703_STS_FAULT_ACOC BIT(5) 65 + 66 + #define BQ25703_IINDPM_MASK GENMASK(14, 8) 67 + #define BQ25703_IINDPM_STEP_UA 50000 68 + #define BQ25703_IINDPM_MIN_UA 50000 69 + #define BQ25703_IINDPM_MAX_UA 6400000 70 + #define BQ25703_IINDPM_DEFAULT_UA 3300000 71 + #define BQ25703_IINDPM_OFFSET_UA 50000 72 + 73 + #define BQ25703_ADCIBAT_DISCHG_MASK GENMASK(6, 0) 74 + #define BQ25703_ADCIBAT_CHG_MASK GENMASK(14, 8) 75 + #define BQ25703_ADCIBAT_CHG_STEP_UA 64000 76 + #define BQ25703_ADCIBAT_DIS_STEP_UA 256000 77 + 78 + #define BQ25703_ADCIIN GENMASK(15, 8) 79 + #define BQ25703_ADCIINCMPIN_STEP 50000 80 + 81 + #define BQ25703_ADCVSYS_MASK GENMASK(15, 8) 82 + #define BQ25703_ADCVBAT_MASK GENMASK(7, 0) 83 + #define BQ25703_ADCVSYSVBAT_OFFSET_UV 2880000 84 + #define BQ25703_ADCVSYSVBAT_STEP 64000 85 + 86 + #define BQ25703_ADC_CH_MASK GENMASK(7, 0) 87 + #define BQ25703_ADC_CONV_EN BIT(15) 88 + #define BQ25703_ADC_START BIT(14) 89 + #define BQ25703_ADC_FULL_SCALE BIT(13) 90 + #define BQ25703_ADC_CMPIN_EN BIT(7) 91 + #define BQ25703_ADC_VBUS_EN BIT(6) 92 + #define BQ25703_ADC_PSYS_EN BIT(5) 93 + #define BQ25703_ADC_IIN_EN BIT(4) 94 + #define BQ25703_ADC_IDCHG_EN BIT(3) 95 + #define BQ25703_ADC_ICHG_EN BIT(2) 96 + #define BQ25703_ADC_VSYS_EN BIT(1) 97 + #define BQ25703_ADC_VBAT_EN BIT(0) 98 + 99 + #define BQ25703_EN_OTG_MASK BIT(12) 100 + 101 + struct bq257xx_device { 102 + struct i2c_client *client; 103 + struct regmap *regmap; 104 + };
+109
include/linux/mfd/max7360.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + 3 + #ifndef __LINUX_MFD_MAX7360_H 4 + #define __LINUX_MFD_MAX7360_H 5 + 6 + #include <linux/bits.h> 7 + 8 + #define MAX7360_MAX_KEY_ROWS 8 9 + #define MAX7360_MAX_KEY_COLS 8 10 + #define MAX7360_MAX_KEY_NUM (MAX7360_MAX_KEY_ROWS * MAX7360_MAX_KEY_COLS) 11 + #define MAX7360_ROW_SHIFT 3 12 + 13 + #define MAX7360_MAX_GPIO 8 14 + #define MAX7360_MAX_GPO 6 15 + #define MAX7360_PORT_PWM_COUNT 8 16 + #define MAX7360_PORT_RTR_PIN (MAX7360_PORT_PWM_COUNT - 1) 17 + 18 + /* 19 + * MAX7360 registers 20 + */ 21 + #define MAX7360_REG_KEYFIFO 0x00 22 + #define MAX7360_REG_CONFIG 0x01 23 + #define MAX7360_REG_DEBOUNCE 0x02 24 + #define MAX7360_REG_INTERRUPT 0x03 25 + #define MAX7360_REG_PORTS 0x04 26 + #define MAX7360_REG_KEYREP 0x05 27 + #define MAX7360_REG_SLEEP 0x06 28 + 29 + /* 30 + * MAX7360 GPIO registers 31 + * 32 + * All these registers are reset together when writing bit 3 of 33 + * MAX7360_REG_GPIOCFG. 34 + */ 35 + #define MAX7360_REG_GPIOCFG 0x40 36 + #define MAX7360_REG_GPIOCTRL 0x41 37 + #define MAX7360_REG_GPIODEB 0x42 38 + #define MAX7360_REG_GPIOCURR 0x43 39 + #define MAX7360_REG_GPIOOUTM 0x44 40 + #define MAX7360_REG_PWMCOM 0x45 41 + #define MAX7360_REG_RTRCFG 0x46 42 + #define MAX7360_REG_I2C_TIMEOUT 0x48 43 + #define MAX7360_REG_GPIOIN 0x49 44 + #define MAX7360_REG_RTR_CNT 0x4A 45 + #define MAX7360_REG_PWMBASE 0x50 46 + #define MAX7360_REG_PWMCFGBASE 0x58 47 + 48 + #define MAX7360_REG_GPIO_LAST 0x5F 49 + 50 + #define MAX7360_REG_PWM(x) (MAX7360_REG_PWMBASE + (x)) 51 + #define MAX7360_REG_PWMCFG(x) (MAX7360_REG_PWMCFGBASE + (x)) 52 + 53 + /* 54 + * Configuration register bits 55 + */ 56 + #define MAX7360_FIFO_EMPTY 0x3F 57 + #define MAX7360_FIFO_OVERFLOW 0x7F 58 + #define MAX7360_FIFO_RELEASE BIT(6) 59 + #define MAX7360_FIFO_COL GENMASK(5, 3) 60 + #define MAX7360_FIFO_ROW GENMASK(2, 0) 61 + 62 + #define MAX7360_CFG_SLEEP BIT(7) 63 + #define MAX7360_CFG_INTERRUPT BIT(5) 64 + #define MAX7360_CFG_KEY_RELEASE BIT(3) 65 + #define MAX7360_CFG_WAKEUP BIT(1) 66 + #define MAX7360_CFG_TIMEOUT BIT(0) 67 + 68 + #define MAX7360_DEBOUNCE GENMASK(4, 0) 69 + #define MAX7360_DEBOUNCE_MIN 9 70 + #define MAX7360_DEBOUNCE_MAX 40 71 + #define MAX7360_PORTS GENMASK(8, 5) 72 + 73 + #define MAX7360_INTERRUPT_TIME_MASK GENMASK(4, 0) 74 + #define MAX7360_INTERRUPT_FIFO_MASK GENMASK(7, 5) 75 + 76 + #define MAX7360_PORT_CFG_INTERRUPT_MASK BIT(7) 77 + #define MAX7360_PORT_CFG_INTERRUPT_EDGES BIT(6) 78 + #define MAX7360_PORT_CFG_COMMON_PWM BIT(5) 79 + 80 + /* 81 + * Autosleep register values 82 + */ 83 + #define MAX7360_AUTOSLEEP_8192MS 0x01 84 + #define MAX7360_AUTOSLEEP_4096MS 0x02 85 + #define MAX7360_AUTOSLEEP_2048MS 0x03 86 + #define MAX7360_AUTOSLEEP_1024MS 0x04 87 + #define MAX7360_AUTOSLEEP_512MS 0x05 88 + #define MAX7360_AUTOSLEEP_256MS 0x06 89 + 90 + #define MAX7360_GPIO_CFG_RTR_EN BIT(7) 91 + #define MAX7360_GPIO_CFG_GPIO_EN BIT(4) 92 + #define MAX7360_GPIO_CFG_GPIO_RST BIT(3) 93 + 94 + #define MAX7360_ROT_DEBOUNCE GENMASK(3, 0) 95 + #define MAX7360_ROT_DEBOUNCE_MIN 0 96 + #define MAX7360_ROT_DEBOUNCE_MAX 15 97 + #define MAX7360_ROT_INTCNT GENMASK(6, 4) 98 + #define MAX7360_ROT_INTCNT_DLY BIT(7) 99 + 100 + #define MAX7360_INT_INTI 0 101 + #define MAX7360_INT_INTK 1 102 + 103 + #define MAX7360_INT_GPIO 0 104 + #define MAX7360_INT_KEYPAD 1 105 + #define MAX7360_INT_ROTARY 2 106 + 107 + #define MAX7360_NR_INTERNAL_IRQS 3 108 + 109 + #endif
-6
include/linux/mfd/mc13xxx.h
··· 31 31 unsigned int mode, unsigned int channel, 32 32 u8 ato, bool atox, unsigned int *sample); 33 33 34 - /* Deprecated calls */ 35 - static inline int mc13xxx_irq_ack(struct mc13xxx *mc13xxx, int irq) 36 - { 37 - return 0; 38 - } 39 - 40 34 static inline int mc13xxx_irq_request_nounmask(struct mc13xxx *mc13xxx, int irq, 41 35 irq_handler_t handler, 42 36 const char *name, void *dev)
+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