···11+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)22+%YAML 1.233+---44+$id: http://devicetree.org/schemas/gpio/maxim,max7360-gpio.yaml#55+$schema: http://devicetree.org/meta-schemas/core.yaml#66+77+title: Maxim MAX7360 GPIO controller88+99+maintainers:1010+ - Kamel Bouhara <kamel.bouhara@bootlin.com>1111+ - Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>1212+1313+description: |1414+ Maxim MAX7360 GPIO controller, in MAX7360 chipset1515+ https://www.analog.com/en/products/max7360.html1616+1717+ The device provides two series of GPIOs, referred here as GPIOs and GPOs.1818+1919+ PORT0 to PORT7 pins can be used as GPIOs, with support for interrupts and2020+ constant-current mode. These pins will also be used by the rotary encoder and2121+ PWM functionalities.2222+2323+ COL2 to COL7 pins can be used as GPOs, there is no input capability. COL pins2424+ will be partitioned, with the first pins being affected to the keypad2525+ functionality and the last ones as GPOs.2626+2727+properties:2828+ compatible:2929+ enum:3030+ - maxim,max7360-gpio3131+ - maxim,max7360-gpo3232+3333+ gpio-controller: true3434+3535+ "#gpio-cells":3636+ const: 23737+3838+ interrupt-controller: true3939+4040+ "#interrupt-cells":4141+ const: 24242+4343+ maxim,constant-current-disable:4444+ $ref: /schemas/types.yaml#/definitions/uint324545+ description:4646+ Bit field, each bit disables constant-current output of the associated4747+ GPIO, starting from the least significant bit for the first GPIO.4848+ maximum: 0xff4949+5050+required:5151+ - compatible5252+ - gpio-controller5353+5454+allOf:5555+ - if:5656+ properties:5757+ compatible:5858+ contains:5959+ enum:6060+ - maxim,max7360-gpio6161+ ngpios: false6262+ then:6363+ required:6464+ - interrupt-controller6565+ else:6666+ properties:6767+ interrupt-controller: false6868+ maxim,constant-current-disable: false6969+7070+additionalProperties: false7171+7272+examples:7373+ - |7474+ gpio {7575+ compatible = "maxim,max7360-gpio";7676+7777+ gpio-controller;7878+ #gpio-cells = <2>;7979+ maxim,constant-current-disable = <0x06>;8080+8181+ interrupt-controller;8282+ #interrupt-cells = <2>;8383+ };
···14921492 help14931493 Support for GPIOs on Cirrus Logic Madera class codecs.1494149414951495+config GPIO_MAX736014961496+ tristate "MAX7360 GPIO support"14971497+ depends on MFD_MAX736014981498+ select GPIO_REGMAP14991499+ select REGMAP_IRQ15001500+ help15011501+ Allows to use MAX7360 I/O Expander PWM lines as GPIO and keypad COL15021502+ lines as GPO.15031503+15041504+ This driver can also be built as a module. If so, the module will be15051505+ called gpio-max7360.15061506+14951507config GPIO_MAX7762014961508 tristate "GPIO support for PMIC MAX77620 and MAX20024"14971509 depends on MFD_MAX77620···1533152115341522 This driver can also be built as a module. If so, the module will be15351523 called gpio-max77759.15241524+15251525+config GPIO_NCT669415261526+ tristate "Nuvoton NCT6694 GPIO controller support"15271527+ depends on MFD_NCT669415281528+ select GENERIC_IRQ_CHIP15291529+ select GPIOLIB_IRQCHIP15301530+ help15311531+ This driver supports 8 GPIO pins per bank that can all be interrupt15321532+ sources.15331533+15341534+ This driver can also be built as a module. If so, the module will be15351535+ called gpio-nct6694.1536153615371537config GPIO_PALMAS15381538 tristate "TI PALMAS series PMICs GPIO"
···11+// SPDX-License-Identifier: GPL-2.0-only22+/*33+ * Copyright 2025 Bootlin44+ *55+ * Author: Kamel BOUHARA <kamel.bouhara@bootlin.com>66+ * Author: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>77+ */88+99+#include <linux/bitfield.h>1010+#include <linux/bitmap.h>1111+#include <linux/err.h>1212+#include <linux/gpio/driver.h>1313+#include <linux/gpio/regmap.h>1414+#include <linux/init.h>1515+#include <linux/interrupt.h>1616+#include <linux/mfd/max7360.h>1717+#include <linux/minmax.h>1818+#include <linux/mod_devicetable.h>1919+#include <linux/module.h>2020+#include <linux/platform_device.h>2121+#include <linux/property.h>2222+#include <linux/regmap.h>2323+2424+#define MAX7360_GPIO_PORT 12525+#define MAX7360_GPIO_COL 22626+2727+struct max7360_gpio_plat_data {2828+ unsigned int function;2929+};3030+3131+static struct max7360_gpio_plat_data max7360_gpio_port_plat = { .function = MAX7360_GPIO_PORT };3232+static struct max7360_gpio_plat_data max7360_gpio_col_plat = { .function = MAX7360_GPIO_COL };3333+3434+static int max7360_get_available_gpos(struct device *dev, unsigned int *available_gpios)3535+{3636+ u32 columns;3737+ int ret;3838+3939+ ret = device_property_read_u32(dev->parent, "keypad,num-columns", &columns);4040+ if (ret) {4141+ dev_err(dev, "Failed to read columns count\n");4242+ return ret;4343+ }4444+4545+ *available_gpios = min(MAX7360_MAX_GPO, MAX7360_MAX_KEY_COLS - columns);4646+4747+ return 0;4848+}4949+5050+static int max7360_gpo_init_valid_mask(struct gpio_chip *gc,5151+ unsigned long *valid_mask,5252+ unsigned int ngpios)5353+{5454+ unsigned int available_gpios;5555+ int ret;5656+5757+ ret = max7360_get_available_gpos(gc->parent, &available_gpios);5858+ if (ret)5959+ return ret;6060+6161+ bitmap_clear(valid_mask, 0, MAX7360_MAX_KEY_COLS - available_gpios);6262+6363+ return 0;6464+}6565+6666+static int max7360_set_gpos_count(struct device *dev, struct regmap *regmap)6767+{6868+ /*6969+ * MAX7360 COL0 to COL7 pins can be used either as keypad columns,7070+ * general purpose output or a mix of both.7171+ * By default, all pins are used as keypad, here we update this7272+ * configuration to allow to use some of them as GPIOs.7373+ */7474+ unsigned int available_gpios;7575+ unsigned int val;7676+ int ret;7777+7878+ ret = max7360_get_available_gpos(dev, &available_gpios);7979+ if (ret)8080+ return ret;8181+8282+ /*8383+ * Configure which GPIOs will be used for keypad.8484+ * MAX7360_REG_DEBOUNCE contains configuration both for keypad debounce8585+ * timings and gpos/keypad columns repartition. Only the later is8686+ * modified here.8787+ */8888+ val = FIELD_PREP(MAX7360_PORTS, available_gpios);8989+ ret = regmap_write_bits(regmap, MAX7360_REG_DEBOUNCE, MAX7360_PORTS, val);9090+ if (ret)9191+ dev_err(dev, "Failed to write max7360 columns/gpos configuration");9292+9393+ return ret;9494+}9595+9696+static int max7360_gpio_reg_mask_xlate(struct gpio_regmap *gpio,9797+ unsigned int base, unsigned int offset,9898+ unsigned int *reg, unsigned int *mask)9999+{100100+ if (base == MAX7360_REG_PWMBASE) {101101+ /*102102+ * GPIO output is using PWM duty cycle registers: one register103103+ * per line, with value being either 0 or 255.104104+ */105105+ *reg = base + offset;106106+ *mask = GENMASK(7, 0);107107+ } else {108108+ *reg = base;109109+ *mask = BIT(offset);110110+ }111111+112112+ return 0;113113+}114114+115115+static const struct regmap_irq max7360_regmap_irqs[MAX7360_MAX_GPIO] = {116116+ REGMAP_IRQ_REG(0, 0, BIT(0)),117117+ REGMAP_IRQ_REG(1, 0, BIT(1)),118118+ REGMAP_IRQ_REG(2, 0, BIT(2)),119119+ REGMAP_IRQ_REG(3, 0, BIT(3)),120120+ REGMAP_IRQ_REG(4, 0, BIT(4)),121121+ REGMAP_IRQ_REG(5, 0, BIT(5)),122122+ REGMAP_IRQ_REG(6, 0, BIT(6)),123123+ REGMAP_IRQ_REG(7, 0, BIT(7)),124124+};125125+126126+static int max7360_handle_mask_sync(const int index,127127+ const unsigned int mask_buf_def,128128+ const unsigned int mask_buf,129129+ void *const irq_drv_data)130130+{131131+ struct regmap *regmap = irq_drv_data;132132+ int ret;133133+134134+ for (unsigned int i = 0; i < MAX7360_MAX_GPIO; i++) {135135+ ret = regmap_assign_bits(regmap, MAX7360_REG_PWMCFG(i),136136+ MAX7360_PORT_CFG_INTERRUPT_MASK, mask_buf & BIT(i));137137+ if (ret)138138+ return ret;139139+ }140140+141141+ return 0;142142+}143143+144144+static int max7360_gpio_probe(struct platform_device *pdev)145145+{146146+ const struct max7360_gpio_plat_data *plat_data;147147+ struct gpio_regmap_config gpio_config = { };148148+ struct regmap_irq_chip *irq_chip;149149+ struct device *dev = &pdev->dev;150150+ struct regmap *regmap;151151+ unsigned int outconf;152152+ int ret;153153+154154+ regmap = dev_get_regmap(dev->parent, NULL);155155+ if (!regmap)156156+ return dev_err_probe(dev, -ENODEV, "could not get parent regmap\n");157157+158158+ plat_data = device_get_match_data(dev);159159+ if (plat_data->function == MAX7360_GPIO_PORT) {160160+ if (device_property_read_bool(dev, "interrupt-controller")) {161161+ /*162162+ * Port GPIOs with interrupt-controller property: add IRQ163163+ * controller.164164+ */165165+ gpio_config.regmap_irq_flags = IRQF_ONESHOT | IRQF_SHARED;166166+ gpio_config.regmap_irq_line =167167+ fwnode_irq_get_byname(dev_fwnode(dev->parent), "inti");168168+ if (gpio_config.regmap_irq_line < 0)169169+ return dev_err_probe(dev, gpio_config.regmap_irq_line,170170+ "Failed to get IRQ\n");171171+172172+ /* Create custom IRQ configuration. */173173+ irq_chip = devm_kzalloc(dev, sizeof(*irq_chip), GFP_KERNEL);174174+ gpio_config.regmap_irq_chip = irq_chip;175175+ if (!irq_chip)176176+ return -ENOMEM;177177+178178+ irq_chip->name = dev_name(dev);179179+ irq_chip->status_base = MAX7360_REG_GPIOIN;180180+ irq_chip->status_is_level = true;181181+ irq_chip->num_regs = 1;182182+ irq_chip->num_irqs = MAX7360_MAX_GPIO;183183+ irq_chip->irqs = max7360_regmap_irqs;184184+ irq_chip->handle_mask_sync = max7360_handle_mask_sync;185185+ irq_chip->irq_drv_data = regmap;186186+187187+ for (unsigned int i = 0; i < MAX7360_MAX_GPIO; i++) {188188+ ret = regmap_write_bits(regmap, MAX7360_REG_PWMCFG(i),189189+ MAX7360_PORT_CFG_INTERRUPT_EDGES,190190+ MAX7360_PORT_CFG_INTERRUPT_EDGES);191191+ if (ret)192192+ return dev_err_probe(dev, ret,193193+ "Failed to enable interrupts\n");194194+ }195195+ }196196+197197+ /*198198+ * Port GPIOs: set output mode configuration (constant-current or not).199199+ * This property is optional.200200+ */201201+ ret = device_property_read_u32(dev, "maxim,constant-current-disable", &outconf);202202+ if (!ret) {203203+ ret = regmap_write(regmap, MAX7360_REG_GPIOOUTM, outconf);204204+ if (ret)205205+ return dev_err_probe(dev, ret,206206+ "Failed to set constant-current configuration\n");207207+ }208208+ }209209+210210+ /* Add gpio device. */211211+ gpio_config.parent = dev;212212+ gpio_config.regmap = regmap;213213+ if (plat_data->function == MAX7360_GPIO_PORT) {214214+ gpio_config.ngpio = MAX7360_MAX_GPIO;215215+ gpio_config.reg_dat_base = GPIO_REGMAP_ADDR(MAX7360_REG_GPIOIN);216216+ gpio_config.reg_set_base = GPIO_REGMAP_ADDR(MAX7360_REG_PWMBASE);217217+ gpio_config.reg_dir_out_base = GPIO_REGMAP_ADDR(MAX7360_REG_GPIOCTRL);218218+ gpio_config.ngpio_per_reg = MAX7360_MAX_GPIO;219219+ gpio_config.reg_mask_xlate = max7360_gpio_reg_mask_xlate;220220+ } else {221221+ ret = max7360_set_gpos_count(dev, regmap);222222+ if (ret)223223+ return dev_err_probe(dev, ret, "Failed to set GPOS pin count\n");224224+225225+ gpio_config.reg_set_base = GPIO_REGMAP_ADDR(MAX7360_REG_PORTS);226226+ gpio_config.ngpio = MAX7360_MAX_KEY_COLS;227227+ gpio_config.init_valid_mask = max7360_gpo_init_valid_mask;228228+ }229229+230230+ return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config));231231+}232232+233233+static const struct of_device_id max7360_gpio_of_match[] = {234234+ {235235+ .compatible = "maxim,max7360-gpo",236236+ .data = &max7360_gpio_col_plat237237+ }, {238238+ .compatible = "maxim,max7360-gpio",239239+ .data = &max7360_gpio_port_plat240240+ }, {241241+ }242242+};243243+MODULE_DEVICE_TABLE(of, max7360_gpio_of_match);244244+245245+static struct platform_driver max7360_gpio_driver = {246246+ .driver = {247247+ .name = "max7360-gpio",248248+ .of_match_table = max7360_gpio_of_match,249249+ },250250+ .probe = max7360_gpio_probe,251251+};252252+module_platform_driver(max7360_gpio_driver);253253+254254+MODULE_DESCRIPTION("MAX7360 GPIO driver");255255+MODULE_AUTHOR("Kamel BOUHARA <kamel.bouhara@bootlin.com>");256256+MODULE_AUTHOR("Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>");257257+MODULE_LICENSE("GPL");
+499
drivers/gpio/gpio-nct6694.c
···11+// SPDX-License-Identifier: GPL-2.022+/*33+ * Nuvoton NCT6694 GPIO controller driver based on USB interface.44+ *55+ * Copyright (C) 2025 Nuvoton Technology Corp.66+ */77+88+#include <linux/bits.h>99+#include <linux/gpio/driver.h>1010+#include <linux/idr.h>1111+#include <linux/interrupt.h>1212+#include <linux/mfd/nct6694.h>1313+#include <linux/module.h>1414+#include <linux/platform_device.h>1515+1616+/*1717+ * USB command module type for NCT6694 GPIO controller.1818+ * This defines the module type used for communication with the NCT66941919+ * GPIO controller over the USB interface.2020+ */2121+#define NCT6694_GPIO_MOD 0xFF2222+2323+#define NCT6694_GPIO_VER 0x902424+#define NCT6694_GPIO_VALID 0x1102525+#define NCT6694_GPI_DATA 0x1202626+#define NCT6694_GPO_DIR 0x1702727+#define NCT6694_GPO_TYPE 0x1802828+#define NCT6694_GPO_DATA 0x1902929+3030+#define NCT6694_GPI_STS 0x1303131+#define NCT6694_GPI_CLR 0x1403232+#define NCT6694_GPI_FALLING 0x1503333+#define NCT6694_GPI_RISING 0x1603434+3535+#define NCT6694_NR_GPIO 83636+3737+struct nct6694_gpio_data {3838+ struct nct6694 *nct6694;3939+ struct gpio_chip gpio;4040+ struct mutex lock;4141+ /* Protect irq operation */4242+ struct mutex irq_lock;4343+4444+ unsigned char reg_val;4545+ unsigned char irq_trig_falling;4646+ unsigned char irq_trig_rising;4747+4848+ /* Current gpio group */4949+ unsigned char group;5050+ int irq;5151+};5252+5353+static int nct6694_get_direction(struct gpio_chip *gpio, unsigned int offset)5454+{5555+ struct nct6694_gpio_data *data = gpiochip_get_data(gpio);5656+ const struct nct6694_cmd_header cmd_hd = {5757+ .mod = NCT6694_GPIO_MOD,5858+ .offset = cpu_to_le16(NCT6694_GPO_DIR + data->group),5959+ .len = cpu_to_le16(sizeof(data->reg_val))6060+ };6161+ int ret;6262+6363+ guard(mutex)(&data->lock);6464+6565+ ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val);6666+ if (ret < 0)6767+ return ret;6868+6969+ return !(BIT(offset) & data->reg_val);7070+}7171+7272+static int nct6694_direction_input(struct gpio_chip *gpio, unsigned int offset)7373+{7474+ struct nct6694_gpio_data *data = gpiochip_get_data(gpio);7575+ const struct nct6694_cmd_header cmd_hd = {7676+ .mod = NCT6694_GPIO_MOD,7777+ .offset = cpu_to_le16(NCT6694_GPO_DIR + data->group),7878+ .len = cpu_to_le16(sizeof(data->reg_val))7979+ };8080+ int ret;8181+8282+ guard(mutex)(&data->lock);8383+8484+ ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val);8585+ if (ret < 0)8686+ return ret;8787+8888+ data->reg_val &= ~BIT(offset);8989+9090+ return nct6694_write_msg(data->nct6694, &cmd_hd, &data->reg_val);9191+}9292+9393+static int nct6694_direction_output(struct gpio_chip *gpio,9494+ unsigned int offset, int val)9595+{9696+ struct nct6694_gpio_data *data = gpiochip_get_data(gpio);9797+ struct nct6694_cmd_header cmd_hd = {9898+ .mod = NCT6694_GPIO_MOD,9999+ .offset = cpu_to_le16(NCT6694_GPO_DIR + data->group),100100+ .len = cpu_to_le16(sizeof(data->reg_val))101101+ };102102+ int ret;103103+104104+ guard(mutex)(&data->lock);105105+106106+ /* Set direction to output */107107+ ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val);108108+ if (ret < 0)109109+ return ret;110110+111111+ data->reg_val |= BIT(offset);112112+ ret = nct6694_write_msg(data->nct6694, &cmd_hd, &data->reg_val);113113+ if (ret < 0)114114+ return ret;115115+116116+ /* Then set output level */117117+ cmd_hd.offset = cpu_to_le16(NCT6694_GPO_DATA + data->group);118118+ ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val);119119+ if (ret < 0)120120+ return ret;121121+122122+ if (val)123123+ data->reg_val |= BIT(offset);124124+ else125125+ data->reg_val &= ~BIT(offset);126126+127127+ return nct6694_write_msg(data->nct6694, &cmd_hd, &data->reg_val);128128+}129129+130130+static int nct6694_get_value(struct gpio_chip *gpio, unsigned int offset)131131+{132132+ struct nct6694_gpio_data *data = gpiochip_get_data(gpio);133133+ struct nct6694_cmd_header cmd_hd = {134134+ .mod = NCT6694_GPIO_MOD,135135+ .offset = cpu_to_le16(NCT6694_GPO_DIR + data->group),136136+ .len = cpu_to_le16(sizeof(data->reg_val))137137+ };138138+ int ret;139139+140140+ guard(mutex)(&data->lock);141141+142142+ ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val);143143+ if (ret < 0)144144+ return ret;145145+146146+ if (BIT(offset) & data->reg_val) {147147+ cmd_hd.offset = cpu_to_le16(NCT6694_GPO_DATA + data->group);148148+ ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val);149149+ if (ret < 0)150150+ return ret;151151+152152+ return !!(BIT(offset) & data->reg_val);153153+ }154154+155155+ cmd_hd.offset = cpu_to_le16(NCT6694_GPI_DATA + data->group);156156+ ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val);157157+ if (ret < 0)158158+ return ret;159159+160160+ return !!(BIT(offset) & data->reg_val);161161+}162162+163163+static int nct6694_set_value(struct gpio_chip *gpio, unsigned int offset,164164+ int val)165165+{166166+ struct nct6694_gpio_data *data = gpiochip_get_data(gpio);167167+ const struct nct6694_cmd_header cmd_hd = {168168+ .mod = NCT6694_GPIO_MOD,169169+ .offset = cpu_to_le16(NCT6694_GPO_DATA + data->group),170170+ .len = cpu_to_le16(sizeof(data->reg_val))171171+ };172172+ int ret;173173+174174+ guard(mutex)(&data->lock);175175+176176+ ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val);177177+ if (ret < 0)178178+ return ret;179179+180180+ if (val)181181+ data->reg_val |= BIT(offset);182182+ else183183+ data->reg_val &= ~BIT(offset);184184+185185+ return nct6694_write_msg(data->nct6694, &cmd_hd, &data->reg_val);186186+}187187+188188+static int nct6694_set_config(struct gpio_chip *gpio, unsigned int offset,189189+ unsigned long config)190190+{191191+ struct nct6694_gpio_data *data = gpiochip_get_data(gpio);192192+ const struct nct6694_cmd_header cmd_hd = {193193+ .mod = NCT6694_GPIO_MOD,194194+ .offset = cpu_to_le16(NCT6694_GPO_TYPE + data->group),195195+ .len = cpu_to_le16(sizeof(data->reg_val))196196+ };197197+ int ret;198198+199199+ guard(mutex)(&data->lock);200200+201201+ ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val);202202+ if (ret < 0)203203+ return ret;204204+205205+ switch (pinconf_to_config_param(config)) {206206+ case PIN_CONFIG_DRIVE_OPEN_DRAIN:207207+ data->reg_val |= BIT(offset);208208+ break;209209+ case PIN_CONFIG_DRIVE_PUSH_PULL:210210+ data->reg_val &= ~BIT(offset);211211+ break;212212+ default:213213+ return -ENOTSUPP;214214+ }215215+216216+ return nct6694_write_msg(data->nct6694, &cmd_hd, &data->reg_val);217217+}218218+219219+static int nct6694_init_valid_mask(struct gpio_chip *gpio,220220+ unsigned long *valid_mask,221221+ unsigned int ngpios)222222+{223223+ struct nct6694_gpio_data *data = gpiochip_get_data(gpio);224224+ const struct nct6694_cmd_header cmd_hd = {225225+ .mod = NCT6694_GPIO_MOD,226226+ .offset = cpu_to_le16(NCT6694_GPIO_VALID + data->group),227227+ .len = cpu_to_le16(sizeof(data->reg_val))228228+ };229229+ int ret;230230+231231+ guard(mutex)(&data->lock);232232+233233+ ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val);234234+ if (ret < 0)235235+ return ret;236236+237237+ *valid_mask = data->reg_val;238238+239239+ return ret;240240+}241241+242242+static irqreturn_t nct6694_irq_handler(int irq, void *priv)243243+{244244+ struct nct6694_gpio_data *data = priv;245245+ struct nct6694_cmd_header cmd_hd = {246246+ .mod = NCT6694_GPIO_MOD,247247+ .offset = cpu_to_le16(NCT6694_GPI_STS + data->group),248248+ .len = cpu_to_le16(sizeof(data->reg_val))249249+ };250250+ unsigned char status;251251+ int ret;252252+253253+ guard(mutex)(&data->lock);254254+255255+ ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->reg_val);256256+ if (ret)257257+ return IRQ_NONE;258258+259259+ status = data->reg_val;260260+261261+ while (status) {262262+ int bit = __ffs(status);263263+264264+ data->reg_val = BIT(bit);265265+ handle_nested_irq(irq_find_mapping(data->gpio.irq.domain, bit));266266+ status &= ~BIT(bit);267267+ cmd_hd.offset = cpu_to_le16(NCT6694_GPI_CLR + data->group);268268+ nct6694_write_msg(data->nct6694, &cmd_hd, &data->reg_val);269269+ }270270+271271+ return IRQ_HANDLED;272272+}273273+274274+static int nct6694_get_irq_trig(struct nct6694_gpio_data *data)275275+{276276+ struct nct6694_cmd_header cmd_hd = {277277+ .mod = NCT6694_GPIO_MOD,278278+ .offset = cpu_to_le16(NCT6694_GPI_FALLING + data->group),279279+ .len = cpu_to_le16(sizeof(data->reg_val))280280+ };281281+ int ret;282282+283283+ guard(mutex)(&data->lock);284284+285285+ ret = nct6694_read_msg(data->nct6694, &cmd_hd, &data->irq_trig_falling);286286+ if (ret)287287+ return ret;288288+289289+ cmd_hd.offset = cpu_to_le16(NCT6694_GPI_RISING + data->group);290290+ return nct6694_read_msg(data->nct6694, &cmd_hd, &data->irq_trig_rising);291291+}292292+293293+static void nct6694_irq_mask(struct irq_data *d)294294+{295295+ struct gpio_chip *gpio = irq_data_get_irq_chip_data(d);296296+ irq_hw_number_t hwirq = irqd_to_hwirq(d);297297+298298+ gpiochip_disable_irq(gpio, hwirq);299299+}300300+301301+static void nct6694_irq_unmask(struct irq_data *d)302302+{303303+ struct gpio_chip *gpio = irq_data_get_irq_chip_data(d);304304+ irq_hw_number_t hwirq = irqd_to_hwirq(d);305305+306306+ gpiochip_enable_irq(gpio, hwirq);307307+}308308+309309+static int nct6694_irq_set_type(struct irq_data *d, unsigned int type)310310+{311311+ struct gpio_chip *gpio = irq_data_get_irq_chip_data(d);312312+ struct nct6694_gpio_data *data = gpiochip_get_data(gpio);313313+ irq_hw_number_t hwirq = irqd_to_hwirq(d);314314+315315+ guard(mutex)(&data->lock);316316+317317+ switch (type) {318318+ case IRQ_TYPE_EDGE_RISING:319319+ data->irq_trig_rising |= BIT(hwirq);320320+ break;321321+322322+ case IRQ_TYPE_EDGE_FALLING:323323+ data->irq_trig_falling |= BIT(hwirq);324324+ break;325325+326326+ case IRQ_TYPE_EDGE_BOTH:327327+ data->irq_trig_rising |= BIT(hwirq);328328+ data->irq_trig_falling |= BIT(hwirq);329329+ break;330330+331331+ default:332332+ return -ENOTSUPP;333333+ }334334+335335+ return 0;336336+}337337+338338+static void nct6694_irq_bus_lock(struct irq_data *d)339339+{340340+ struct gpio_chip *gpio = irq_data_get_irq_chip_data(d);341341+ struct nct6694_gpio_data *data = gpiochip_get_data(gpio);342342+343343+ mutex_lock(&data->irq_lock);344344+}345345+346346+static void nct6694_irq_bus_sync_unlock(struct irq_data *d)347347+{348348+ struct gpio_chip *gpio = irq_data_get_irq_chip_data(d);349349+ struct nct6694_gpio_data *data = gpiochip_get_data(gpio);350350+ struct nct6694_cmd_header cmd_hd = {351351+ .mod = NCT6694_GPIO_MOD,352352+ .offset = cpu_to_le16(NCT6694_GPI_FALLING + data->group),353353+ .len = cpu_to_le16(sizeof(data->reg_val))354354+ };355355+356356+ scoped_guard(mutex, &data->lock) {357357+ nct6694_write_msg(data->nct6694, &cmd_hd, &data->irq_trig_falling);358358+359359+ cmd_hd.offset = cpu_to_le16(NCT6694_GPI_RISING + data->group);360360+ nct6694_write_msg(data->nct6694, &cmd_hd, &data->irq_trig_rising);361361+ }362362+363363+ mutex_unlock(&data->irq_lock);364364+}365365+366366+static const struct irq_chip nct6694_irq_chip = {367367+ .name = "gpio-nct6694",368368+ .irq_mask = nct6694_irq_mask,369369+ .irq_unmask = nct6694_irq_unmask,370370+ .irq_set_type = nct6694_irq_set_type,371371+ .irq_bus_lock = nct6694_irq_bus_lock,372372+ .irq_bus_sync_unlock = nct6694_irq_bus_sync_unlock,373373+ .flags = IRQCHIP_IMMUTABLE,374374+ GPIOCHIP_IRQ_RESOURCE_HELPERS,375375+};376376+377377+static void nct6694_irq_dispose_mapping(void *d)378378+{379379+ struct nct6694_gpio_data *data = d;380380+381381+ irq_dispose_mapping(data->irq);382382+}383383+384384+static void nct6694_gpio_ida_free(void *d)385385+{386386+ struct nct6694_gpio_data *data = d;387387+ struct nct6694 *nct6694 = data->nct6694;388388+389389+ ida_free(&nct6694->gpio_ida, data->group);390390+}391391+392392+static int nct6694_gpio_probe(struct platform_device *pdev)393393+{394394+ struct device *dev = &pdev->dev;395395+ struct nct6694 *nct6694 = dev_get_drvdata(dev->parent);396396+ struct nct6694_gpio_data *data;397397+ struct gpio_irq_chip *girq;398398+ int ret, i;399399+ char **names;400400+401401+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);402402+ if (!data)403403+ return -ENOMEM;404404+405405+ data->nct6694 = nct6694;406406+407407+ ret = ida_alloc(&nct6694->gpio_ida, GFP_KERNEL);408408+ if (ret < 0)409409+ return ret;410410+ data->group = ret;411411+412412+ ret = devm_add_action_or_reset(dev, nct6694_gpio_ida_free, data);413413+ if (ret)414414+ return ret;415415+416416+ names = devm_kcalloc(dev, NCT6694_NR_GPIO, sizeof(char *),417417+ GFP_KERNEL);418418+ if (!names)419419+ return -ENOMEM;420420+421421+ for (i = 0; i < NCT6694_NR_GPIO; i++) {422422+ names[i] = devm_kasprintf(dev, GFP_KERNEL, "GPIO%X%d",423423+ data->group, i);424424+ if (!names[i])425425+ return -ENOMEM;426426+ }427427+428428+ data->irq = irq_create_mapping(nct6694->domain,429429+ NCT6694_IRQ_GPIO0 + data->group);430430+ if (!data->irq)431431+ return -EINVAL;432432+433433+ ret = devm_add_action_or_reset(dev, nct6694_irq_dispose_mapping, data);434434+ if (ret)435435+ return ret;436436+437437+ data->gpio.names = (const char * const*)names;438438+ data->gpio.label = pdev->name;439439+ data->gpio.direction_input = nct6694_direction_input;440440+ data->gpio.get = nct6694_get_value;441441+ data->gpio.direction_output = nct6694_direction_output;442442+ data->gpio.set = nct6694_set_value;443443+ data->gpio.get_direction = nct6694_get_direction;444444+ data->gpio.set_config = nct6694_set_config;445445+ data->gpio.init_valid_mask = nct6694_init_valid_mask;446446+ data->gpio.base = -1;447447+ data->gpio.can_sleep = false;448448+ data->gpio.owner = THIS_MODULE;449449+ data->gpio.ngpio = NCT6694_NR_GPIO;450450+451451+ platform_set_drvdata(pdev, data);452452+453453+ ret = devm_mutex_init(dev, &data->lock);454454+ if (ret)455455+ return ret;456456+457457+ ret = devm_mutex_init(dev, &data->irq_lock);458458+ if (ret)459459+ return ret;460460+461461+ ret = nct6694_get_irq_trig(data);462462+ if (ret) {463463+ dev_err_probe(dev, ret, "Failed to get irq trigger type\n");464464+ return ret;465465+ }466466+467467+ girq = &data->gpio.irq;468468+ gpio_irq_chip_set_chip(girq, &nct6694_irq_chip);469469+ girq->parent_handler = NULL;470470+ girq->num_parents = 0;471471+ girq->parents = NULL;472472+ girq->default_type = IRQ_TYPE_NONE;473473+ girq->handler = handle_level_irq;474474+ girq->threaded = true;475475+476476+ ret = devm_request_threaded_irq(dev, data->irq, NULL, nct6694_irq_handler,477477+ IRQF_ONESHOT | IRQF_SHARED,478478+ "gpio-nct6694", data);479479+ if (ret) {480480+ dev_err_probe(dev, ret, "Failed to request irq\n");481481+ return ret;482482+ }483483+484484+ return devm_gpiochip_add_data(dev, &data->gpio, data);485485+}486486+487487+static struct platform_driver nct6694_gpio_driver = {488488+ .driver = {489489+ .name = "nct6694-gpio",490490+ },491491+ .probe = nct6694_gpio_probe,492492+};493493+494494+module_platform_driver(nct6694_gpio_driver);495495+496496+MODULE_DESCRIPTION("USB-GPIO controller driver for NCT6694");497497+MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>");498498+MODULE_LICENSE("GPL");499499+MODULE_ALIAS("platform:nct6694-gpio");
+28-2
drivers/gpio/gpio-regmap.c
···3232 unsigned int reg_dir_in_base;3333 unsigned int reg_dir_out_base;34343535+#ifdef CONFIG_REGMAP_IRQ3636+ int regmap_irq_line;3737+ struct regmap_irq_chip_data *irq_chip_data;3838+#endif3939+3540 int (*reg_mask_xlate)(struct gpio_regmap *gpio, unsigned int base,3641 unsigned int offset, unsigned int *reg,3742 unsigned int *mask);···220215 */221216struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config)222217{218218+ struct irq_domain *irq_domain;223219 struct gpio_regmap *gpio;224220 struct gpio_chip *chip;225221 int ret;···261255 chip->names = config->names;262256 chip->label = config->label ?: dev_name(config->parent);263257 chip->can_sleep = regmap_might_sleep(config->regmap);258258+ chip->init_valid_mask = config->init_valid_mask;264259265260 chip->request = gpiochip_generic_request;266261 chip->free = gpiochip_generic_free;···302295 if (ret < 0)303296 goto err_free_gpio;304297305305- if (config->irq_domain) {306306- ret = gpiochip_irqchip_add_domain(chip, config->irq_domain);298298+#ifdef CONFIG_REGMAP_IRQ299299+ if (config->regmap_irq_chip) {300300+ gpio->regmap_irq_line = config->regmap_irq_line;301301+ ret = regmap_add_irq_chip_fwnode(dev_fwnode(config->parent), config->regmap,302302+ config->regmap_irq_line, config->regmap_irq_flags,303303+ 0, config->regmap_irq_chip, &gpio->irq_chip_data);304304+ if (ret)305305+ goto err_free_gpio;306306+307307+ irq_domain = regmap_irq_get_domain(gpio->irq_chip_data);308308+ } else309309+#endif310310+ irq_domain = config->irq_domain;311311+312312+ if (irq_domain) {313313+ ret = gpiochip_irqchip_add_domain(chip, irq_domain);307314 if (ret)308315 goto err_remove_gpiochip;309316 }···338317 */339318void gpio_regmap_unregister(struct gpio_regmap *gpio)340319{320320+#ifdef CONFIG_REGMAP_IRQ321321+ if (gpio->irq_chip_data)322322+ regmap_del_irq_chip(gpio->regmap_irq_line, gpio->irq_chip_data);323323+#endif324324+341325 gpiochip_remove(&gpio->gpio_chip);342326 kfree(gpio);343327}
+10
drivers/hwmon/Kconfig
···16981698 This driver can also be built as a module. If so, the module16991699 will be called nct6683.1700170017011701+config SENSORS_NCT669417021702+ tristate "Nuvoton NCT6694 Hardware Monitor support"17031703+ depends on MFD_NCT669417041704+ help17051705+ Say Y here to support Nuvoton NCT6694 hardware monitoring17061706+ functionality.17071707+17081708+ This driver can also be built as a module. If so, the module17091709+ will be called nct6694-hwmon.17101710+17011711config SENSORS_NCT6775_CORE17021712 tristate17031713 select REGMAP
···11+// SPDX-License-Identifier: GPL-2.022+/*33+ * Nuvoton NCT6694 HWMON driver based on USB interface.44+ *55+ * Copyright (C) 2025 Nuvoton Technology Corp.66+ */77+88+#include <linux/bits.h>99+#include <linux/bitfield.h>1010+#include <linux/hwmon.h>1111+#include <linux/kernel.h>1212+#include <linux/mfd/core.h>1313+#include <linux/mfd/nct6694.h>1414+#include <linux/module.h>1515+#include <linux/platform_device.h>1616+#include <linux/slab.h>1717+1818+/*1919+ * USB command module type for NCT6694 report channel2020+ * This defines the module type used for communication with the NCT66942121+ * report channel over the USB interface.2222+ */2323+#define NCT6694_RPT_MOD 0xFF2424+2525+/* Report channel */2626+/*2727+ * The report channel is used to report the status of the hardware monitor2828+ * devices, such as voltage, temperature, fan speed, and PWM.2929+ */3030+#define NCT6694_VIN_IDX(x) (0x00 + (x))3131+#define NCT6694_TIN_IDX(x) \3232+ ({ typeof(x) (_x) = (x); \3333+ ((_x) < 10) ? (0x10 + ((_x) * 2)) : \3434+ (0x30 + (((_x) - 10) * 2)); })3535+#define NCT6694_FIN_IDX(x) (0x50 + ((x) * 2))3636+#define NCT6694_PWM_IDX(x) (0x70 + (x))3737+#define NCT6694_VIN_STS(x) (0x68 + (x))3838+#define NCT6694_TIN_STS(x) (0x6A + (x))3939+#define NCT6694_FIN_STS(x) (0x6E + (x))4040+4141+/*4242+ * USB command module type for NCT6694 HWMON controller.4343+ * This defines the module type used for communication with the NCT66944444+ * HWMON controller over the USB interface.4545+ */4646+#define NCT6694_HWMON_MOD 0x004747+4848+/* Command 00h - Hardware Monitor Control */4949+#define NCT6694_HWMON_CONTROL 0x005050+#define NCT6694_HWMON_CONTROL_SEL 0x005151+5252+/* Command 02h - Alarm Control */5353+#define NCT6694_HWMON_ALARM 0x025454+#define NCT6694_HWMON_ALARM_SEL 0x005555+5656+/*5757+ * USB command module type for NCT6694 PWM controller.5858+ * This defines the module type used for communication with the NCT66945959+ * PWM controller over the USB interface.6060+ */6161+#define NCT6694_PWM_MOD 0x016262+6363+/* PWM Command - Manual Control */6464+#define NCT6694_PWM_CONTROL 0x016565+#define NCT6694_PWM_CONTROL_SEL 0x006666+6767+#define NCT6694_FREQ_FROM_REG(reg) ((reg) * 25000 / 255)6868+#define NCT6694_FREQ_TO_REG(val) \6969+ (DIV_ROUND_CLOSEST(clamp_val((val), 100, 25000) * 255, 25000))7070+7171+#define NCT6694_LSB_REG_MASK GENMASK(7, 5)7272+#define NCT6694_TIN_HYST_MASK GENMASK(7, 5)7373+7474+enum nct6694_hwmon_temp_mode {7575+ NCT6694_HWMON_TWOTIME_IRQ = 0,7676+ NCT6694_HWMON_ONETIME_IRQ,7777+ NCT6694_HWMON_REALTIME_IRQ,7878+ NCT6694_HWMON_COMPARE_IRQ,7979+};8080+8181+struct __packed nct6694_hwmon_control {8282+ u8 vin_en[2];8383+ u8 tin_en[2];8484+ u8 fin_en[2];8585+ u8 pwm_en[2];8686+ u8 reserved1[40];8787+ u8 pwm_freq[10];8888+ u8 reserved2[6];8989+};9090+9191+struct __packed nct6694_hwmon_alarm {9292+ u8 smi_ctrl;9393+ u8 reserved1[15];9494+ struct {9595+ u8 hl;9696+ u8 ll;9797+ } vin_limit[16];9898+ struct {9999+ u8 hyst;100100+ s8 hl;101101+ } tin_cfg[32];102102+ __be16 fin_ll[10];103103+ u8 reserved2[4];104104+};105105+106106+struct __packed nct6694_pwm_control {107107+ u8 mal_en[2];108108+ u8 mal_val[10];109109+ u8 reserved[12];110110+};111111+112112+union __packed nct6694_hwmon_rpt {113113+ u8 vin;114114+ struct {115115+ u8 msb;116116+ u8 lsb;117117+ } tin;118118+ __be16 fin;119119+ u8 pwm;120120+ u8 status;121121+};122122+123123+union __packed nct6694_hwmon_msg {124124+ struct nct6694_hwmon_alarm hwmon_alarm;125125+ struct nct6694_pwm_control pwm_ctrl;126126+};127127+128128+struct nct6694_hwmon_data {129129+ struct nct6694 *nct6694;130130+ struct mutex lock;131131+ struct nct6694_hwmon_control hwmon_en;132132+ union nct6694_hwmon_rpt *rpt;133133+ union nct6694_hwmon_msg *msg;134134+};135135+136136+static inline long in_from_reg(u8 reg)137137+{138138+ return reg * 16;139139+}140140+141141+static inline u8 in_to_reg(long val)142142+{143143+ return DIV_ROUND_CLOSEST(val, 16);144144+}145145+146146+static inline long temp_from_reg(s8 reg)147147+{148148+ return reg * 1000;149149+}150150+151151+static inline s8 temp_to_reg(long val)152152+{153153+ return DIV_ROUND_CLOSEST(val, 1000);154154+}155155+156156+#define NCT6694_HWMON_IN_CONFIG (HWMON_I_INPUT | HWMON_I_ENABLE | \157157+ HWMON_I_MAX | HWMON_I_MIN | \158158+ HWMON_I_ALARM)159159+#define NCT6694_HWMON_TEMP_CONFIG (HWMON_T_INPUT | HWMON_T_ENABLE | \160160+ HWMON_T_MAX | HWMON_T_MAX_HYST | \161161+ HWMON_T_MAX_ALARM)162162+#define NCT6694_HWMON_FAN_CONFIG (HWMON_F_INPUT | HWMON_F_ENABLE | \163163+ HWMON_F_MIN | HWMON_F_MIN_ALARM)164164+#define NCT6694_HWMON_PWM_CONFIG (HWMON_PWM_INPUT | HWMON_PWM_ENABLE | \165165+ HWMON_PWM_FREQ)166166+static const struct hwmon_channel_info *nct6694_info[] = {167167+ HWMON_CHANNEL_INFO(in,168168+ NCT6694_HWMON_IN_CONFIG, /* VIN0 */169169+ NCT6694_HWMON_IN_CONFIG, /* VIN1 */170170+ NCT6694_HWMON_IN_CONFIG, /* VIN2 */171171+ NCT6694_HWMON_IN_CONFIG, /* VIN3 */172172+ NCT6694_HWMON_IN_CONFIG, /* VIN5 */173173+ NCT6694_HWMON_IN_CONFIG, /* VIN6 */174174+ NCT6694_HWMON_IN_CONFIG, /* VIN7 */175175+ NCT6694_HWMON_IN_CONFIG, /* VIN14 */176176+ NCT6694_HWMON_IN_CONFIG, /* VIN15 */177177+ NCT6694_HWMON_IN_CONFIG, /* VIN16 */178178+ NCT6694_HWMON_IN_CONFIG, /* VBAT */179179+ NCT6694_HWMON_IN_CONFIG, /* VSB */180180+ NCT6694_HWMON_IN_CONFIG, /* AVSB */181181+ NCT6694_HWMON_IN_CONFIG, /* VCC */182182+ NCT6694_HWMON_IN_CONFIG, /* VHIF */183183+ NCT6694_HWMON_IN_CONFIG), /* VTT */184184+185185+ HWMON_CHANNEL_INFO(temp,186186+ NCT6694_HWMON_TEMP_CONFIG, /* THR1 */187187+ NCT6694_HWMON_TEMP_CONFIG, /* THR2 */188188+ NCT6694_HWMON_TEMP_CONFIG, /* THR14 */189189+ NCT6694_HWMON_TEMP_CONFIG, /* THR15 */190190+ NCT6694_HWMON_TEMP_CONFIG, /* THR16 */191191+ NCT6694_HWMON_TEMP_CONFIG, /* TDP0 */192192+ NCT6694_HWMON_TEMP_CONFIG, /* TDP1 */193193+ NCT6694_HWMON_TEMP_CONFIG, /* TDP2 */194194+ NCT6694_HWMON_TEMP_CONFIG, /* TDP3 */195195+ NCT6694_HWMON_TEMP_CONFIG, /* TDP4 */196196+ NCT6694_HWMON_TEMP_CONFIG, /* DTIN0 */197197+ NCT6694_HWMON_TEMP_CONFIG, /* DTIN1 */198198+ NCT6694_HWMON_TEMP_CONFIG, /* DTIN2 */199199+ NCT6694_HWMON_TEMP_CONFIG, /* DTIN3 */200200+ NCT6694_HWMON_TEMP_CONFIG, /* DTIN4 */201201+ NCT6694_HWMON_TEMP_CONFIG, /* DTIN5 */202202+ NCT6694_HWMON_TEMP_CONFIG, /* DTIN6 */203203+ NCT6694_HWMON_TEMP_CONFIG, /* DTIN7 */204204+ NCT6694_HWMON_TEMP_CONFIG, /* DTIN8 */205205+ NCT6694_HWMON_TEMP_CONFIG, /* DTIN9 */206206+ NCT6694_HWMON_TEMP_CONFIG, /* DTIN10 */207207+ NCT6694_HWMON_TEMP_CONFIG, /* DTIN11 */208208+ NCT6694_HWMON_TEMP_CONFIG, /* DTIN12 */209209+ NCT6694_HWMON_TEMP_CONFIG, /* DTIN13 */210210+ NCT6694_HWMON_TEMP_CONFIG, /* DTIN14 */211211+ NCT6694_HWMON_TEMP_CONFIG), /* DTIN15 */212212+213213+ HWMON_CHANNEL_INFO(fan,214214+ NCT6694_HWMON_FAN_CONFIG, /* FIN0 */215215+ NCT6694_HWMON_FAN_CONFIG, /* FIN1 */216216+ NCT6694_HWMON_FAN_CONFIG, /* FIN2 */217217+ NCT6694_HWMON_FAN_CONFIG, /* FIN3 */218218+ NCT6694_HWMON_FAN_CONFIG, /* FIN4 */219219+ NCT6694_HWMON_FAN_CONFIG, /* FIN5 */220220+ NCT6694_HWMON_FAN_CONFIG, /* FIN6 */221221+ NCT6694_HWMON_FAN_CONFIG, /* FIN7 */222222+ NCT6694_HWMON_FAN_CONFIG, /* FIN8 */223223+ NCT6694_HWMON_FAN_CONFIG), /* FIN9 */224224+225225+ HWMON_CHANNEL_INFO(pwm,226226+ NCT6694_HWMON_PWM_CONFIG, /* PWM0 */227227+ NCT6694_HWMON_PWM_CONFIG, /* PWM1 */228228+ NCT6694_HWMON_PWM_CONFIG, /* PWM2 */229229+ NCT6694_HWMON_PWM_CONFIG, /* PWM3 */230230+ NCT6694_HWMON_PWM_CONFIG, /* PWM4 */231231+ NCT6694_HWMON_PWM_CONFIG, /* PWM5 */232232+ NCT6694_HWMON_PWM_CONFIG, /* PWM6 */233233+ NCT6694_HWMON_PWM_CONFIG, /* PWM7 */234234+ NCT6694_HWMON_PWM_CONFIG, /* PWM8 */235235+ NCT6694_HWMON_PWM_CONFIG), /* PWM9 */236236+ NULL237237+};238238+239239+static int nct6694_in_read(struct device *dev, u32 attr, int channel,240240+ long *val)241241+{242242+ struct nct6694_hwmon_data *data = dev_get_drvdata(dev);243243+ struct nct6694_cmd_header cmd_hd;244244+ unsigned char vin_en;245245+ int ret;246246+247247+ guard(mutex)(&data->lock);248248+249249+ switch (attr) {250250+ case hwmon_in_enable:251251+ vin_en = data->hwmon_en.vin_en[(channel / 8)];252252+ *val = !!(vin_en & BIT(channel % 8));253253+254254+ return 0;255255+ case hwmon_in_input:256256+ cmd_hd = (struct nct6694_cmd_header) {257257+ .mod = NCT6694_RPT_MOD,258258+ .offset = cpu_to_le16(NCT6694_VIN_IDX(channel)),259259+ .len = cpu_to_le16(sizeof(data->rpt->vin))260260+ };261261+ ret = nct6694_read_msg(data->nct6694, &cmd_hd,262262+ &data->rpt->vin);263263+ if (ret)264264+ return ret;265265+266266+ *val = in_from_reg(data->rpt->vin);267267+268268+ return 0;269269+ case hwmon_in_max:270270+ cmd_hd = (struct nct6694_cmd_header) {271271+ .mod = NCT6694_HWMON_MOD,272272+ .cmd = NCT6694_HWMON_ALARM,273273+ .sel = NCT6694_HWMON_ALARM_SEL,274274+ .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))275275+ };276276+ ret = nct6694_read_msg(data->nct6694, &cmd_hd,277277+ &data->msg->hwmon_alarm);278278+ if (ret)279279+ return ret;280280+281281+ *val = in_from_reg(data->msg->hwmon_alarm.vin_limit[channel].hl);282282+283283+ return 0;284284+ case hwmon_in_min:285285+ cmd_hd = (struct nct6694_cmd_header) {286286+ .mod = NCT6694_HWMON_MOD,287287+ .cmd = NCT6694_HWMON_ALARM,288288+ .sel = NCT6694_HWMON_ALARM_SEL,289289+ .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))290290+ };291291+ ret = nct6694_read_msg(data->nct6694, &cmd_hd,292292+ &data->msg->hwmon_alarm);293293+ if (ret)294294+ return ret;295295+296296+ *val = in_from_reg(data->msg->hwmon_alarm.vin_limit[channel].ll);297297+298298+ return 0;299299+ case hwmon_in_alarm:300300+ cmd_hd = (struct nct6694_cmd_header) {301301+ .mod = NCT6694_RPT_MOD,302302+ .offset = cpu_to_le16(NCT6694_VIN_STS(channel / 8)),303303+ .len = cpu_to_le16(sizeof(data->rpt->status))304304+ };305305+ ret = nct6694_read_msg(data->nct6694, &cmd_hd,306306+ &data->rpt->status);307307+ if (ret)308308+ return ret;309309+310310+ *val = !!(data->rpt->status & BIT(channel % 8));311311+312312+ return 0;313313+ default:314314+ return -EOPNOTSUPP;315315+ }316316+}317317+318318+static int nct6694_temp_read(struct device *dev, u32 attr, int channel,319319+ long *val)320320+{321321+ struct nct6694_hwmon_data *data = dev_get_drvdata(dev);322322+ struct nct6694_cmd_header cmd_hd;323323+ unsigned char temp_en, temp_hyst;324324+ signed char temp_max;325325+ int ret, temp_raw;326326+327327+ guard(mutex)(&data->lock);328328+329329+ switch (attr) {330330+ case hwmon_temp_enable:331331+ temp_en = data->hwmon_en.tin_en[channel / 8];332332+ *val = !!(temp_en & BIT(channel % 8));333333+334334+ return 0;335335+ case hwmon_temp_input:336336+ cmd_hd = (struct nct6694_cmd_header) {337337+ .mod = NCT6694_RPT_MOD,338338+ .offset = cpu_to_le16(NCT6694_TIN_IDX(channel)),339339+ .len = cpu_to_le16(sizeof(data->rpt->tin))340340+ };341341+ ret = nct6694_read_msg(data->nct6694, &cmd_hd,342342+ &data->rpt->tin);343343+ if (ret)344344+ return ret;345345+346346+ temp_raw = data->rpt->tin.msb << 3;347347+ temp_raw |= FIELD_GET(NCT6694_LSB_REG_MASK, data->rpt->tin.lsb);348348+349349+ /* Real temperature(milli degrees Celsius) = temp_raw * 1000 * 0.125 */350350+ *val = sign_extend32(temp_raw, 10) * 125;351351+352352+ return 0;353353+ case hwmon_temp_max:354354+ cmd_hd = (struct nct6694_cmd_header) {355355+ .mod = NCT6694_HWMON_MOD,356356+ .cmd = NCT6694_HWMON_ALARM,357357+ .sel = NCT6694_HWMON_ALARM_SEL,358358+ .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))359359+ };360360+ ret = nct6694_read_msg(data->nct6694, &cmd_hd,361361+ &data->msg->hwmon_alarm);362362+ if (ret)363363+ return ret;364364+365365+ *val = temp_from_reg(data->msg->hwmon_alarm.tin_cfg[channel].hl);366366+367367+ return 0;368368+ case hwmon_temp_max_hyst:369369+ cmd_hd = (struct nct6694_cmd_header) {370370+ .mod = NCT6694_HWMON_MOD,371371+ .cmd = NCT6694_HWMON_ALARM,372372+ .sel = NCT6694_HWMON_ALARM_SEL,373373+ .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))374374+ };375375+ ret = nct6694_read_msg(data->nct6694, &cmd_hd,376376+ &data->msg->hwmon_alarm);377377+ if (ret)378378+ return ret;379379+380380+ temp_max = data->msg->hwmon_alarm.tin_cfg[channel].hl;381381+ temp_hyst = FIELD_GET(NCT6694_TIN_HYST_MASK,382382+ data->msg->hwmon_alarm.tin_cfg[channel].hyst);383383+ *val = temp_from_reg(temp_max - temp_hyst);384384+385385+ return 0;386386+ case hwmon_temp_max_alarm:387387+ cmd_hd = (struct nct6694_cmd_header) {388388+ .mod = NCT6694_RPT_MOD,389389+ .offset = cpu_to_le16(NCT6694_TIN_STS(channel / 8)),390390+ .len = cpu_to_le16(sizeof(data->rpt->status))391391+ };392392+ ret = nct6694_read_msg(data->nct6694, &cmd_hd,393393+ &data->rpt->status);394394+ if (ret)395395+ return ret;396396+397397+ *val = !!(data->rpt->status & BIT(channel % 8));398398+399399+ return 0;400400+ default:401401+ return -EOPNOTSUPP;402402+ }403403+}404404+405405+static int nct6694_fan_read(struct device *dev, u32 attr, int channel,406406+ long *val)407407+{408408+ struct nct6694_hwmon_data *data = dev_get_drvdata(dev);409409+ struct nct6694_cmd_header cmd_hd;410410+ unsigned char fanin_en;411411+ int ret;412412+413413+ guard(mutex)(&data->lock);414414+415415+ switch (attr) {416416+ case hwmon_fan_enable:417417+ fanin_en = data->hwmon_en.fin_en[channel / 8];418418+ *val = !!(fanin_en & BIT(channel % 8));419419+420420+ return 0;421421+ case hwmon_fan_input:422422+ cmd_hd = (struct nct6694_cmd_header) {423423+ .mod = NCT6694_RPT_MOD,424424+ .offset = cpu_to_le16(NCT6694_FIN_IDX(channel)),425425+ .len = cpu_to_le16(sizeof(data->rpt->fin))426426+ };427427+ ret = nct6694_read_msg(data->nct6694, &cmd_hd,428428+ &data->rpt->fin);429429+ if (ret)430430+ return ret;431431+432432+ *val = be16_to_cpu(data->rpt->fin);433433+434434+ return 0;435435+ case hwmon_fan_min:436436+ cmd_hd = (struct nct6694_cmd_header) {437437+ .mod = NCT6694_HWMON_MOD,438438+ .cmd = NCT6694_HWMON_ALARM,439439+ .sel = NCT6694_HWMON_ALARM_SEL,440440+ .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))441441+ };442442+ ret = nct6694_read_msg(data->nct6694, &cmd_hd,443443+ &data->msg->hwmon_alarm);444444+ if (ret)445445+ return ret;446446+447447+ *val = be16_to_cpu(data->msg->hwmon_alarm.fin_ll[channel]);448448+449449+ return 0;450450+ case hwmon_fan_min_alarm:451451+ cmd_hd = (struct nct6694_cmd_header) {452452+ .mod = NCT6694_RPT_MOD,453453+ .offset = cpu_to_le16(NCT6694_FIN_STS(channel / 8)),454454+ .len = cpu_to_le16(sizeof(data->rpt->status))455455+ };456456+ ret = nct6694_read_msg(data->nct6694, &cmd_hd,457457+ &data->rpt->status);458458+ if (ret)459459+ return ret;460460+461461+ *val = !!(data->rpt->status & BIT(channel % 8));462462+463463+ return 0;464464+ default:465465+ return -EOPNOTSUPP;466466+ }467467+}468468+469469+static int nct6694_pwm_read(struct device *dev, u32 attr, int channel,470470+ long *val)471471+{472472+ struct nct6694_hwmon_data *data = dev_get_drvdata(dev);473473+ struct nct6694_cmd_header cmd_hd;474474+ unsigned char pwm_en;475475+ int ret;476476+477477+ guard(mutex)(&data->lock);478478+479479+ switch (attr) {480480+ case hwmon_pwm_enable:481481+ pwm_en = data->hwmon_en.pwm_en[channel / 8];482482+ *val = !!(pwm_en & BIT(channel % 8));483483+484484+ return 0;485485+ case hwmon_pwm_input:486486+ cmd_hd = (struct nct6694_cmd_header) {487487+ .mod = NCT6694_RPT_MOD,488488+ .offset = cpu_to_le16(NCT6694_PWM_IDX(channel)),489489+ .len = cpu_to_le16(sizeof(data->rpt->pwm))490490+ };491491+ ret = nct6694_read_msg(data->nct6694, &cmd_hd,492492+ &data->rpt->pwm);493493+ if (ret)494494+ return ret;495495+496496+ *val = data->rpt->pwm;497497+498498+ return 0;499499+ case hwmon_pwm_freq:500500+ *val = NCT6694_FREQ_FROM_REG(data->hwmon_en.pwm_freq[channel]);501501+502502+ return 0;503503+ default:504504+ return -EOPNOTSUPP;505505+ }506506+}507507+508508+static int nct6694_in_write(struct device *dev, u32 attr, int channel,509509+ long val)510510+{511511+ struct nct6694_hwmon_data *data = dev_get_drvdata(dev);512512+ struct nct6694_cmd_header cmd_hd;513513+ int ret;514514+515515+ guard(mutex)(&data->lock);516516+517517+ switch (attr) {518518+ case hwmon_in_enable:519519+ if (val == 0)520520+ data->hwmon_en.vin_en[channel / 8] &= ~BIT(channel % 8);521521+ else if (val == 1)522522+ data->hwmon_en.vin_en[channel / 8] |= BIT(channel % 8);523523+ else524524+ return -EINVAL;525525+526526+ cmd_hd = (struct nct6694_cmd_header) {527527+ .mod = NCT6694_HWMON_MOD,528528+ .cmd = NCT6694_HWMON_CONTROL,529529+ .sel = NCT6694_HWMON_CONTROL_SEL,530530+ .len = cpu_to_le16(sizeof(data->hwmon_en))531531+ };532532+533533+ return nct6694_write_msg(data->nct6694, &cmd_hd,534534+ &data->hwmon_en);535535+ case hwmon_in_max:536536+ cmd_hd = (struct nct6694_cmd_header) {537537+ .mod = NCT6694_HWMON_MOD,538538+ .cmd = NCT6694_HWMON_ALARM,539539+ .sel = NCT6694_HWMON_ALARM_SEL,540540+ .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))541541+ };542542+ ret = nct6694_read_msg(data->nct6694, &cmd_hd,543543+ &data->msg->hwmon_alarm);544544+ if (ret)545545+ return ret;546546+547547+ val = clamp_val(val, 0, 2032);548548+ data->msg->hwmon_alarm.vin_limit[channel].hl = in_to_reg(val);549549+550550+ return nct6694_write_msg(data->nct6694, &cmd_hd,551551+ &data->msg->hwmon_alarm);552552+ case hwmon_in_min:553553+ cmd_hd = (struct nct6694_cmd_header) {554554+ .mod = NCT6694_HWMON_MOD,555555+ .cmd = NCT6694_HWMON_ALARM,556556+ .sel = NCT6694_HWMON_ALARM_SEL,557557+ .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))558558+ };559559+ ret = nct6694_read_msg(data->nct6694, &cmd_hd,560560+ &data->msg->hwmon_alarm);561561+ if (ret)562562+ return ret;563563+564564+ val = clamp_val(val, 0, 2032);565565+ data->msg->hwmon_alarm.vin_limit[channel].ll = in_to_reg(val);566566+567567+ return nct6694_write_msg(data->nct6694, &cmd_hd,568568+ &data->msg->hwmon_alarm);569569+ default:570570+ return -EOPNOTSUPP;571571+ }572572+}573573+574574+static int nct6694_temp_write(struct device *dev, u32 attr, int channel,575575+ long val)576576+{577577+ struct nct6694_hwmon_data *data = dev_get_drvdata(dev);578578+ struct nct6694_cmd_header cmd_hd;579579+ unsigned char temp_hyst;580580+ signed char temp_max;581581+ int ret;582582+583583+ guard(mutex)(&data->lock);584584+585585+ switch (attr) {586586+ case hwmon_temp_enable:587587+ if (val == 0)588588+ data->hwmon_en.tin_en[channel / 8] &= ~BIT(channel % 8);589589+ else if (val == 1)590590+ data->hwmon_en.tin_en[channel / 8] |= BIT(channel % 8);591591+ else592592+ return -EINVAL;593593+594594+ cmd_hd = (struct nct6694_cmd_header) {595595+ .mod = NCT6694_HWMON_MOD,596596+ .cmd = NCT6694_HWMON_CONTROL,597597+ .sel = NCT6694_HWMON_CONTROL_SEL,598598+ .len = cpu_to_le16(sizeof(data->hwmon_en))599599+ };600600+601601+ return nct6694_write_msg(data->nct6694, &cmd_hd,602602+ &data->hwmon_en);603603+ case hwmon_temp_max:604604+ cmd_hd = (struct nct6694_cmd_header) {605605+ .mod = NCT6694_HWMON_MOD,606606+ .cmd = NCT6694_HWMON_ALARM,607607+ .sel = NCT6694_HWMON_ALARM_SEL,608608+ .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))609609+ };610610+ ret = nct6694_read_msg(data->nct6694, &cmd_hd,611611+ &data->msg->hwmon_alarm);612612+ if (ret)613613+ return ret;614614+615615+ val = clamp_val(val, -127000, 127000);616616+ data->msg->hwmon_alarm.tin_cfg[channel].hl = temp_to_reg(val);617617+618618+ return nct6694_write_msg(data->nct6694, &cmd_hd,619619+ &data->msg->hwmon_alarm);620620+ case hwmon_temp_max_hyst:621621+ cmd_hd = (struct nct6694_cmd_header) {622622+ .mod = NCT6694_HWMON_MOD,623623+ .cmd = NCT6694_HWMON_ALARM,624624+ .sel = NCT6694_HWMON_ALARM_SEL,625625+ .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))626626+ };627627+ ret = nct6694_read_msg(data->nct6694, &cmd_hd,628628+ &data->msg->hwmon_alarm);629629+630630+ val = clamp_val(val, -127000, 127000);631631+ temp_max = data->msg->hwmon_alarm.tin_cfg[channel].hl;632632+ temp_hyst = temp_max - temp_to_reg(val);633633+ temp_hyst = clamp_val(temp_hyst, 0, 7);634634+ data->msg->hwmon_alarm.tin_cfg[channel].hyst =635635+ (data->msg->hwmon_alarm.tin_cfg[channel].hyst & ~NCT6694_TIN_HYST_MASK) |636636+ FIELD_PREP(NCT6694_TIN_HYST_MASK, temp_hyst);637637+638638+ return nct6694_write_msg(data->nct6694, &cmd_hd,639639+ &data->msg->hwmon_alarm);640640+ default:641641+ return -EOPNOTSUPP;642642+ }643643+}644644+645645+static int nct6694_fan_write(struct device *dev, u32 attr, int channel,646646+ long val)647647+{648648+ struct nct6694_hwmon_data *data = dev_get_drvdata(dev);649649+ struct nct6694_cmd_header cmd_hd;650650+ int ret;651651+652652+ guard(mutex)(&data->lock);653653+654654+ switch (attr) {655655+ case hwmon_fan_enable:656656+ if (val == 0)657657+ data->hwmon_en.fin_en[channel / 8] &= ~BIT(channel % 8);658658+ else if (val == 1)659659+ data->hwmon_en.fin_en[channel / 8] |= BIT(channel % 8);660660+ else661661+ return -EINVAL;662662+663663+ cmd_hd = (struct nct6694_cmd_header) {664664+ .mod = NCT6694_HWMON_MOD,665665+ .cmd = NCT6694_HWMON_CONTROL,666666+ .sel = NCT6694_HWMON_CONTROL_SEL,667667+ .len = cpu_to_le16(sizeof(data->hwmon_en))668668+ };669669+670670+ return nct6694_write_msg(data->nct6694, &cmd_hd,671671+ &data->hwmon_en);672672+ case hwmon_fan_min:673673+ cmd_hd = (struct nct6694_cmd_header) {674674+ .mod = NCT6694_HWMON_MOD,675675+ .cmd = NCT6694_HWMON_ALARM,676676+ .sel = NCT6694_HWMON_ALARM_SEL,677677+ .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))678678+ };679679+ ret = nct6694_read_msg(data->nct6694, &cmd_hd,680680+ &data->msg->hwmon_alarm);681681+ if (ret)682682+ return ret;683683+684684+ val = clamp_val(val, 1, 65535);685685+ data->msg->hwmon_alarm.fin_ll[channel] = cpu_to_be16(val);686686+687687+ return nct6694_write_msg(data->nct6694, &cmd_hd,688688+ &data->msg->hwmon_alarm);689689+ default:690690+ return -EOPNOTSUPP;691691+ }692692+}693693+694694+static int nct6694_pwm_write(struct device *dev, u32 attr, int channel,695695+ long val)696696+{697697+ struct nct6694_hwmon_data *data = dev_get_drvdata(dev);698698+ struct nct6694_cmd_header cmd_hd;699699+ int ret;700700+701701+ guard(mutex)(&data->lock);702702+703703+ switch (attr) {704704+ case hwmon_pwm_enable:705705+ if (val == 0)706706+ data->hwmon_en.pwm_en[channel / 8] &= ~BIT(channel % 8);707707+ else if (val == 1)708708+ data->hwmon_en.pwm_en[channel / 8] |= BIT(channel % 8);709709+ else710710+ return -EINVAL;711711+712712+ cmd_hd = (struct nct6694_cmd_header) {713713+ .mod = NCT6694_HWMON_MOD,714714+ .cmd = NCT6694_HWMON_CONTROL,715715+ .sel = NCT6694_HWMON_CONTROL_SEL,716716+ .len = cpu_to_le16(sizeof(data->hwmon_en))717717+ };718718+719719+ return nct6694_write_msg(data->nct6694, &cmd_hd,720720+ &data->hwmon_en);721721+ case hwmon_pwm_input:722722+ if (val < 0 || val > 255)723723+ return -EINVAL;724724+725725+ cmd_hd = (struct nct6694_cmd_header) {726726+ .mod = NCT6694_PWM_MOD,727727+ .cmd = NCT6694_PWM_CONTROL,728728+ .sel = NCT6694_PWM_CONTROL_SEL,729729+ .len = cpu_to_le16(sizeof(data->msg->pwm_ctrl))730730+ };731731+732732+ ret = nct6694_read_msg(data->nct6694, &cmd_hd,733733+ &data->msg->pwm_ctrl);734734+ if (ret)735735+ return ret;736736+737737+ data->msg->pwm_ctrl.mal_val[channel] = val;738738+739739+ return nct6694_write_msg(data->nct6694, &cmd_hd,740740+ &data->msg->pwm_ctrl);741741+ case hwmon_pwm_freq:742742+ cmd_hd = (struct nct6694_cmd_header) {743743+ .mod = NCT6694_HWMON_MOD,744744+ .cmd = NCT6694_HWMON_CONTROL,745745+ .sel = NCT6694_HWMON_CONTROL_SEL,746746+ .len = cpu_to_le16(sizeof(data->hwmon_en))747747+ };748748+749749+ data->hwmon_en.pwm_freq[channel] = NCT6694_FREQ_TO_REG(val);750750+751751+ return nct6694_write_msg(data->nct6694, &cmd_hd,752752+ &data->hwmon_en);753753+ default:754754+ return -EOPNOTSUPP;755755+ }756756+}757757+758758+static int nct6694_read(struct device *dev, enum hwmon_sensor_types type,759759+ u32 attr, int channel, long *val)760760+{761761+ switch (type) {762762+ case hwmon_in:763763+ /* in mV */764764+ return nct6694_in_read(dev, attr, channel, val);765765+ case hwmon_temp:766766+ /* in mC */767767+ return nct6694_temp_read(dev, attr, channel, val);768768+ case hwmon_fan:769769+ /* in RPM */770770+ return nct6694_fan_read(dev, attr, channel, val);771771+ case hwmon_pwm:772772+ /* in value 0~255 */773773+ return nct6694_pwm_read(dev, attr, channel, val);774774+ default:775775+ return -EOPNOTSUPP;776776+ }777777+}778778+779779+static int nct6694_write(struct device *dev, enum hwmon_sensor_types type,780780+ u32 attr, int channel, long val)781781+{782782+ switch (type) {783783+ case hwmon_in:784784+ return nct6694_in_write(dev, attr, channel, val);785785+ case hwmon_temp:786786+ return nct6694_temp_write(dev, attr, channel, val);787787+ case hwmon_fan:788788+ return nct6694_fan_write(dev, attr, channel, val);789789+ case hwmon_pwm:790790+ return nct6694_pwm_write(dev, attr, channel, val);791791+ default:792792+ return -EOPNOTSUPP;793793+ }794794+}795795+796796+static umode_t nct6694_is_visible(const void *data,797797+ enum hwmon_sensor_types type,798798+ u32 attr, int channel)799799+{800800+ switch (type) {801801+ case hwmon_in:802802+ switch (attr) {803803+ case hwmon_in_enable:804804+ case hwmon_in_max:805805+ case hwmon_in_min:806806+ return 0644;807807+ case hwmon_in_alarm:808808+ case hwmon_in_input:809809+ return 0444;810810+ default:811811+ return 0;812812+ }813813+ case hwmon_temp:814814+ switch (attr) {815815+ case hwmon_temp_enable:816816+ case hwmon_temp_max:817817+ case hwmon_temp_max_hyst:818818+ return 0644;819819+ case hwmon_temp_input:820820+ case hwmon_temp_max_alarm:821821+ return 0444;822822+ default:823823+ return 0;824824+ }825825+ case hwmon_fan:826826+ switch (attr) {827827+ case hwmon_fan_enable:828828+ case hwmon_fan_min:829829+ return 0644;830830+ case hwmon_fan_input:831831+ case hwmon_fan_min_alarm:832832+ return 0444;833833+ default:834834+ return 0;835835+ }836836+ case hwmon_pwm:837837+ switch (attr) {838838+ case hwmon_pwm_enable:839839+ case hwmon_pwm_freq:840840+ case hwmon_pwm_input:841841+ return 0644;842842+ default:843843+ return 0;844844+ }845845+ default:846846+ return 0;847847+ }848848+}849849+850850+static const struct hwmon_ops nct6694_hwmon_ops = {851851+ .is_visible = nct6694_is_visible,852852+ .read = nct6694_read,853853+ .write = nct6694_write,854854+};855855+856856+static const struct hwmon_chip_info nct6694_chip_info = {857857+ .ops = &nct6694_hwmon_ops,858858+ .info = nct6694_info,859859+};860860+861861+static int nct6694_hwmon_init(struct nct6694_hwmon_data *data)862862+{863863+ struct nct6694_cmd_header cmd_hd = {864864+ .mod = NCT6694_HWMON_MOD,865865+ .cmd = NCT6694_HWMON_CONTROL,866866+ .sel = NCT6694_HWMON_CONTROL_SEL,867867+ .len = cpu_to_le16(sizeof(data->hwmon_en))868868+ };869869+ int ret;870870+871871+ /*872872+ * Record each Hardware Monitor Channel enable status873873+ * and PWM frequency register874874+ */875875+ ret = nct6694_read_msg(data->nct6694, &cmd_hd,876876+ &data->hwmon_en);877877+ if (ret)878878+ return ret;879879+880880+ cmd_hd = (struct nct6694_cmd_header) {881881+ .mod = NCT6694_HWMON_MOD,882882+ .cmd = NCT6694_HWMON_ALARM,883883+ .sel = NCT6694_HWMON_ALARM_SEL,884884+ .len = cpu_to_le16(sizeof(data->msg->hwmon_alarm))885885+ };886886+887887+ /* Select hwmon device alarm mode */888888+ ret = nct6694_read_msg(data->nct6694, &cmd_hd,889889+ &data->msg->hwmon_alarm);890890+ if (ret)891891+ return ret;892892+893893+ data->msg->hwmon_alarm.smi_ctrl = NCT6694_HWMON_REALTIME_IRQ;894894+895895+ return nct6694_write_msg(data->nct6694, &cmd_hd,896896+ &data->msg->hwmon_alarm);897897+}898898+899899+static int nct6694_hwmon_probe(struct platform_device *pdev)900900+{901901+ struct nct6694_hwmon_data *data;902902+ struct nct6694 *nct6694 = dev_get_drvdata(pdev->dev.parent);903903+ struct device *hwmon_dev;904904+ int ret;905905+906906+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);907907+ if (!data)908908+ return -ENOMEM;909909+910910+ data->rpt = devm_kzalloc(&pdev->dev, sizeof(union nct6694_hwmon_rpt),911911+ GFP_KERNEL);912912+ if (!data->rpt)913913+ return -ENOMEM;914914+915915+ data->msg = devm_kzalloc(&pdev->dev, sizeof(union nct6694_hwmon_msg),916916+ GFP_KERNEL);917917+ if (!data->msg)918918+ return -ENOMEM;919919+920920+ data->nct6694 = nct6694;921921+ ret = devm_mutex_init(&pdev->dev, &data->lock);922922+ if (ret)923923+ return ret;924924+925925+ ret = nct6694_hwmon_init(data);926926+ if (ret)927927+ return ret;928928+929929+ /* Register hwmon device to HWMON framework */930930+ hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev,931931+ "nct6694", data,932932+ &nct6694_chip_info,933933+ NULL);934934+ return PTR_ERR_OR_ZERO(hwmon_dev);935935+}936936+937937+static struct platform_driver nct6694_hwmon_driver = {938938+ .driver = {939939+ .name = "nct6694-hwmon",940940+ },941941+ .probe = nct6694_hwmon_probe,942942+};943943+944944+module_platform_driver(nct6694_hwmon_driver);945945+946946+MODULE_DESCRIPTION("USB-HWMON driver for NCT6694");947947+MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>");948948+MODULE_LICENSE("GPL");949949+MODULE_ALIAS("platform:nct6694-hwmon");
+10
drivers/i2c/busses/Kconfig
···13571357 This driver can also be built as a module. If so, the module13581358 will be called i2c-ljca.1359135913601360+config I2C_NCT669413611361+ tristate "Nuvoton NCT6694 I2C adapter support"13621362+ depends on MFD_NCT669413631363+ help13641364+ If you say yes to this option, support will be included for Nuvoton13651365+ NCT6694, a USB to I2C interface.13661366+13671367+ This driver can also be built as a module. If so, the module will13681368+ be called i2c-nct6694.13691369+13601370config I2C_CP261513611371 tristate "Silicon Labs CP2615 USB sound card and I2C adapter"13621372 depends on USB
···11+// SPDX-License-Identifier: GPL-2.022+/*33+ * Nuvoton NCT6694 I2C adapter driver based on USB interface.44+ *55+ * Copyright (C) 2025 Nuvoton Technology Corp.66+ */77+88+#include <linux/i2c.h>99+#include <linux/idr.h>1010+#include <linux/kernel.h>1111+#include <linux/mfd/nct6694.h>1212+#include <linux/module.h>1313+#include <linux/platform_device.h>1414+1515+/*1616+ * USB command module type for NCT6694 I2C controller.1717+ * This defines the module type used for communication with the NCT66941818+ * I2C controller over the USB interface.1919+ */2020+#define NCT6694_I2C_MOD 0x032121+2222+/* Command 00h - I2C Deliver */2323+#define NCT6694_I2C_DELIVER 0x002424+#define NCT6694_I2C_DELIVER_SEL 0x002525+2626+#define NCT6694_I2C_MAX_XFER_SIZE 642727+#define NCT6694_I2C_MAX_DEVS 62828+2929+static unsigned char br_reg[NCT6694_I2C_MAX_DEVS] = {[0 ... (NCT6694_I2C_MAX_DEVS - 1)] = 0xFF};3030+3131+module_param_array(br_reg, byte, NULL, 0644);3232+MODULE_PARM_DESC(br_reg,3333+ "I2C Baudrate register per adapter: (0=25K, 1=50K, 2=100K, 3=200K, 4=400K, 5=800K, 6=1M), default=2");3434+3535+enum nct6694_i2c_baudrate {3636+ NCT6694_I2C_BR_25K = 0,3737+ NCT6694_I2C_BR_50K,3838+ NCT6694_I2C_BR_100K,3939+ NCT6694_I2C_BR_200K,4040+ NCT6694_I2C_BR_400K,4141+ NCT6694_I2C_BR_800K,4242+ NCT6694_I2C_BR_1M4343+};4444+4545+struct __packed nct6694_i2c_deliver {4646+ u8 port;4747+ u8 br;4848+ u8 addr;4949+ u8 w_cnt;5050+ u8 r_cnt;5151+ u8 rsv[11];5252+ u8 write_data[NCT6694_I2C_MAX_XFER_SIZE];5353+ u8 read_data[NCT6694_I2C_MAX_XFER_SIZE];5454+};5555+5656+struct nct6694_i2c_data {5757+ struct device *dev;5858+ struct nct6694 *nct6694;5959+ struct i2c_adapter adapter;6060+ struct nct6694_i2c_deliver deliver;6161+ unsigned char port;6262+ unsigned char br;6363+};6464+6565+static int nct6694_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)6666+{6767+ struct nct6694_i2c_data *data = adap->algo_data;6868+ struct nct6694_i2c_deliver *deliver = &data->deliver;6969+ static const struct nct6694_cmd_header cmd_hd = {7070+ .mod = NCT6694_I2C_MOD,7171+ .cmd = NCT6694_I2C_DELIVER,7272+ .sel = NCT6694_I2C_DELIVER_SEL,7373+ .len = cpu_to_le16(sizeof(*deliver))7474+ };7575+ int ret, i;7676+7777+ for (i = 0; i < num; i++) {7878+ struct i2c_msg *msg_temp = &msgs[i];7979+8080+ memset(deliver, 0, sizeof(*deliver));8181+8282+ deliver->port = data->port;8383+ deliver->br = data->br;8484+ deliver->addr = i2c_8bit_addr_from_msg(msg_temp);8585+ if (msg_temp->flags & I2C_M_RD) {8686+ deliver->r_cnt = msg_temp->len;8787+ ret = nct6694_write_msg(data->nct6694, &cmd_hd, deliver);8888+ if (ret < 0)8989+ return ret;9090+9191+ memcpy(msg_temp->buf, deliver->read_data, msg_temp->len);9292+ } else {9393+ deliver->w_cnt = msg_temp->len;9494+ memcpy(deliver->write_data, msg_temp->buf, msg_temp->len);9595+ ret = nct6694_write_msg(data->nct6694, &cmd_hd, deliver);9696+ if (ret < 0)9797+ return ret;9898+ }9999+ }100100+101101+ return num;102102+}103103+104104+static u32 nct6694_i2c_func(struct i2c_adapter *adapter)105105+{106106+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;107107+}108108+109109+static const struct i2c_adapter_quirks nct6694_i2c_quirks = {110110+ .max_read_len = NCT6694_I2C_MAX_XFER_SIZE,111111+ .max_write_len = NCT6694_I2C_MAX_XFER_SIZE,112112+};113113+114114+static const struct i2c_algorithm nct6694_i2c_algo = {115115+ .xfer = nct6694_i2c_xfer,116116+ .functionality = nct6694_i2c_func,117117+};118118+119119+static int nct6694_i2c_set_baudrate(struct nct6694_i2c_data *data)120120+{121121+ if (data->port >= NCT6694_I2C_MAX_DEVS) {122122+ dev_err(data->dev, "Invalid I2C port index %d\n", data->port);123123+ return -EINVAL;124124+ }125125+126126+ if (br_reg[data->port] > NCT6694_I2C_BR_1M) {127127+ dev_warn(data->dev, "Invalid baudrate %d for I2C%d, using 100K\n",128128+ br_reg[data->port], data->port);129129+ br_reg[data->port] = NCT6694_I2C_BR_100K;130130+ }131131+132132+ data->br = br_reg[data->port];133133+134134+ return 0;135135+}136136+137137+static void nct6694_i2c_ida_free(void *d)138138+{139139+ struct nct6694_i2c_data *data = d;140140+ struct nct6694 *nct6694 = data->nct6694;141141+142142+ ida_free(&nct6694->i2c_ida, data->port);143143+}144144+145145+static int nct6694_i2c_probe(struct platform_device *pdev)146146+{147147+ struct device *dev = &pdev->dev;148148+ struct nct6694 *nct6694 = dev_get_drvdata(dev->parent);149149+ struct nct6694_i2c_data *data;150150+ int ret;151151+152152+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);153153+ if (!data)154154+ return -ENOMEM;155155+156156+ data->dev = dev;157157+ data->nct6694 = nct6694;158158+159159+ ret = ida_alloc(&nct6694->i2c_ida, GFP_KERNEL);160160+ if (ret < 0)161161+ return ret;162162+ data->port = ret;163163+164164+ ret = devm_add_action_or_reset(dev, nct6694_i2c_ida_free, data);165165+ if (ret)166166+ return ret;167167+168168+ ret = nct6694_i2c_set_baudrate(data);169169+ if (ret)170170+ return ret;171171+172172+ sprintf(data->adapter.name, "NCT6694 I2C Adapter %d", data->port);173173+ data->adapter.owner = THIS_MODULE;174174+ data->adapter.algo = &nct6694_i2c_algo;175175+ data->adapter.quirks = &nct6694_i2c_quirks;176176+ data->adapter.dev.parent = dev;177177+ data->adapter.algo_data = data;178178+179179+ platform_set_drvdata(pdev, data);180180+181181+ return devm_i2c_add_adapter(dev, &data->adapter);182182+}183183+184184+static struct platform_driver nct6694_i2c_driver = {185185+ .driver = {186186+ .name = "nct6694-i2c",187187+ },188188+ .probe = nct6694_i2c_probe,189189+};190190+191191+module_platform_driver(nct6694_i2c_driver);192192+193193+MODULE_DESCRIPTION("USB-I2C adapter driver for NCT6694");194194+MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>");195195+MODULE_LICENSE("GPL");196196+MODULE_ALIAS("platform:nct6694-i2c");
+12
drivers/input/keyboard/Kconfig
···422422 To compile this driver as a module, choose M here: the423423 module will be called max7359_keypad.424424425425+config KEYBOARD_MAX7360426426+ tristate "Maxim MAX7360 Key Switch Controller"427427+ select INPUT_MATRIXKMAP428428+ depends on I2C429429+ depends on MFD_MAX7360430430+ help431431+ If you say yes here you get support for the keypad controller on the432432+ Maxim MAX7360 I/O Expander.433433+434434+ To compile this driver as a module, choose M here: the module will be435435+ called max7360_keypad.436436+425437config KEYBOARD_MPR121426438 tristate "Freescale MPR121 Touchkey"427439 depends on I2C
···11+// SPDX-License-Identifier: GPL-2.0-only22+/*33+ * Copyright 2025 Bootlin44+ *55+ * Author: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>66+ */77+88+#include <linux/bitfield.h>99+#include <linux/bitops.h>1010+#include <linux/dev_printk.h>1111+#include <linux/device/devres.h>1212+#include <linux/err.h>1313+#include <linux/init.h>1414+#include <linux/input.h>1515+#include <linux/input/matrix_keypad.h>1616+#include <linux/interrupt.h>1717+#include <linux/mfd/max7360.h>1818+#include <linux/mod_devicetable.h>1919+#include <linux/minmax.h>2020+#include <linux/module.h>2121+#include <linux/property.h>2222+#include <linux/platform_device.h>2323+#include <linux/pm_wakeirq.h>2424+#include <linux/regmap.h>2525+2626+struct max7360_keypad {2727+ struct input_dev *input;2828+ unsigned int rows;2929+ unsigned int cols;3030+ unsigned int debounce_ms;3131+ int irq;3232+ struct regmap *regmap;3333+ unsigned short keycodes[MAX7360_MAX_KEY_ROWS * MAX7360_MAX_KEY_COLS];3434+};3535+3636+static irqreturn_t max7360_keypad_irq(int irq, void *data)3737+{3838+ struct max7360_keypad *max7360_keypad = data;3939+ struct device *dev = max7360_keypad->input->dev.parent;4040+ unsigned int val;4141+ unsigned int row, col;4242+ unsigned int release;4343+ unsigned int code;4444+ int error;4545+4646+ error = regmap_read(max7360_keypad->regmap, MAX7360_REG_KEYFIFO, &val);4747+ if (error) {4848+ dev_err(dev, "Failed to read MAX7360 FIFO");4949+ return IRQ_NONE;5050+ }5151+5252+ /* FIFO overflow: ignore it and get next event. */5353+ if (val == MAX7360_FIFO_OVERFLOW) {5454+ dev_warn(dev, "max7360 FIFO overflow");5555+ error = regmap_read_poll_timeout(max7360_keypad->regmap, MAX7360_REG_KEYFIFO,5656+ val, val != MAX7360_FIFO_OVERFLOW, 0, 1000);5757+ if (error) {5858+ dev_err(dev, "Failed to empty MAX7360 FIFO");5959+ return IRQ_NONE;6060+ }6161+ }6262+6363+ if (val == MAX7360_FIFO_EMPTY) {6464+ dev_dbg(dev, "Got a spurious interrupt");6565+6666+ return IRQ_NONE;6767+ }6868+6969+ row = FIELD_GET(MAX7360_FIFO_ROW, val);7070+ col = FIELD_GET(MAX7360_FIFO_COL, val);7171+ release = val & MAX7360_FIFO_RELEASE;7272+7373+ code = MATRIX_SCAN_CODE(row, col, get_count_order(max7360_keypad->cols));7474+7575+ dev_dbg(dev, "key[%d:%d] %s\n", row, col, release ? "release" : "press");7676+7777+ input_event(max7360_keypad->input, EV_MSC, MSC_SCAN, code);7878+ input_report_key(max7360_keypad->input, max7360_keypad->keycodes[code], !release);7979+ input_sync(max7360_keypad->input);8080+8181+ return IRQ_HANDLED;8282+}8383+8484+static int max7360_keypad_open(struct input_dev *pdev)8585+{8686+ struct max7360_keypad *max7360_keypad = input_get_drvdata(pdev);8787+ struct device *dev = max7360_keypad->input->dev.parent;8888+ int error;8989+9090+ /* Somebody is using the device: get out of sleep. */9191+ error = regmap_write_bits(max7360_keypad->regmap, MAX7360_REG_CONFIG,9292+ MAX7360_CFG_SLEEP, MAX7360_CFG_SLEEP);9393+ if (error)9494+ dev_err(dev, "Failed to write max7360 configuration: %d\n", error);9595+9696+ return error;9797+}9898+9999+static void max7360_keypad_close(struct input_dev *pdev)100100+{101101+ struct max7360_keypad *max7360_keypad = input_get_drvdata(pdev);102102+ struct device *dev = max7360_keypad->input->dev.parent;103103+ int error;104104+105105+ /* Nobody is using the device anymore: go to sleep. */106106+ error = regmap_write_bits(max7360_keypad->regmap, MAX7360_REG_CONFIG, MAX7360_CFG_SLEEP, 0);107107+ if (error)108108+ dev_err(dev, "Failed to write max7360 configuration: %d\n", error);109109+}110110+111111+static int max7360_keypad_hw_init(struct max7360_keypad *max7360_keypad)112112+{113113+ struct device *dev = max7360_keypad->input->dev.parent;114114+ unsigned int val;115115+ int error;116116+117117+ val = max7360_keypad->debounce_ms - MAX7360_DEBOUNCE_MIN;118118+ error = regmap_write_bits(max7360_keypad->regmap, MAX7360_REG_DEBOUNCE,119119+ MAX7360_DEBOUNCE,120120+ FIELD_PREP(MAX7360_DEBOUNCE, val));121121+ if (error)122122+ return dev_err_probe(dev, error,123123+ "Failed to write max7360 debounce configuration\n");124124+125125+ error = regmap_write_bits(max7360_keypad->regmap, MAX7360_REG_INTERRUPT,126126+ MAX7360_INTERRUPT_TIME_MASK,127127+ FIELD_PREP(MAX7360_INTERRUPT_TIME_MASK, 1));128128+ if (error)129129+ return dev_err_probe(dev, error,130130+ "Failed to write max7360 keypad interrupt configuration\n");131131+132132+ return 0;133133+}134134+135135+static int max7360_keypad_build_keymap(struct max7360_keypad *max7360_keypad)136136+{137137+ struct input_dev *input_dev = max7360_keypad->input;138138+ struct device *dev = input_dev->dev.parent->parent;139139+ struct matrix_keymap_data keymap_data;140140+ const char *propname = "linux,keymap";141141+ unsigned int max_keys;142142+ int error;143143+ int size;144144+145145+ size = device_property_count_u32(dev, propname);146146+ if (size <= 0) {147147+ dev_err(dev, "missing or malformed property %s: %d\n", propname, size);148148+ return size < 0 ? size : -EINVAL;149149+ }150150+151151+ max_keys = max7360_keypad->cols * max7360_keypad->rows;152152+ if (size > max_keys) {153153+ dev_err(dev, "%s size overflow (%d vs max %u)\n", propname, size, max_keys);154154+ return -EINVAL;155155+ }156156+157157+ u32 *keys __free(kfree) = kmalloc_array(size, sizeof(*keys), GFP_KERNEL);158158+ if (!keys)159159+ return -ENOMEM;160160+161161+ error = device_property_read_u32_array(dev, propname, keys, size);162162+ if (error) {163163+ dev_err(dev, "failed to read %s property: %d\n", propname, error);164164+ return error;165165+ }166166+167167+ keymap_data.keymap = keys;168168+ keymap_data.keymap_size = size;169169+ error = matrix_keypad_build_keymap(&keymap_data, NULL,170170+ max7360_keypad->rows, max7360_keypad->cols,171171+ max7360_keypad->keycodes, max7360_keypad->input);172172+ if (error)173173+ return error;174174+175175+ return 0;176176+}177177+178178+static int max7360_keypad_parse_fw(struct device *dev,179179+ struct max7360_keypad *max7360_keypad,180180+ bool *autorepeat)181181+{182182+ int error;183183+184184+ error = matrix_keypad_parse_properties(dev->parent, &max7360_keypad->rows,185185+ &max7360_keypad->cols);186186+ if (error)187187+ return error;188188+189189+ if (!max7360_keypad->rows || !max7360_keypad->cols ||190190+ max7360_keypad->rows > MAX7360_MAX_KEY_ROWS ||191191+ max7360_keypad->cols > MAX7360_MAX_KEY_COLS) {192192+ dev_err(dev, "Invalid number of columns or rows (%ux%u)\n",193193+ max7360_keypad->cols, max7360_keypad->rows);194194+ return -EINVAL;195195+ }196196+197197+ *autorepeat = device_property_read_bool(dev->parent, "autorepeat");198198+199199+ max7360_keypad->debounce_ms = MAX7360_DEBOUNCE_MIN;200200+ error = device_property_read_u32(dev->parent, "keypad-debounce-delay-ms",201201+ &max7360_keypad->debounce_ms);202202+ if (error == -EINVAL) {203203+ dev_info(dev, "Using default keypad-debounce-delay-ms: %u\n",204204+ max7360_keypad->debounce_ms);205205+ } else if (error < 0) {206206+ dev_err(dev, "Failed to read keypad-debounce-delay-ms property\n");207207+ return error;208208+ }209209+210210+ if (!in_range(max7360_keypad->debounce_ms, MAX7360_DEBOUNCE_MIN,211211+ MAX7360_DEBOUNCE_MAX - MAX7360_DEBOUNCE_MIN + 1)) {212212+ dev_err(dev, "Invalid keypad-debounce-delay-ms: %u, should be between %u and %u.\n",213213+ max7360_keypad->debounce_ms, MAX7360_DEBOUNCE_MIN, MAX7360_DEBOUNCE_MAX);214214+ return -EINVAL;215215+ }216216+217217+ return 0;218218+}219219+220220+static int max7360_keypad_probe(struct platform_device *pdev)221221+{222222+ struct max7360_keypad *max7360_keypad;223223+ struct device *dev = &pdev->dev;224224+ struct input_dev *input;225225+ struct regmap *regmap;226226+ bool autorepeat;227227+ int error;228228+ int irq;229229+230230+ regmap = dev_get_regmap(dev->parent, NULL);231231+ if (!regmap)232232+ return dev_err_probe(dev, -ENODEV, "Could not get parent regmap\n");233233+234234+ irq = fwnode_irq_get_byname(dev_fwnode(dev->parent), "intk");235235+ if (irq < 0)236236+ return dev_err_probe(dev, irq, "Failed to get IRQ\n");237237+238238+ max7360_keypad = devm_kzalloc(dev, sizeof(*max7360_keypad), GFP_KERNEL);239239+ if (!max7360_keypad)240240+ return -ENOMEM;241241+242242+ max7360_keypad->regmap = regmap;243243+244244+ error = max7360_keypad_parse_fw(dev, max7360_keypad, &autorepeat);245245+ if (error)246246+ return error;247247+248248+ input = devm_input_allocate_device(dev);249249+ if (!input)250250+ return -ENOMEM;251251+252252+ max7360_keypad->input = input;253253+254254+ input->id.bustype = BUS_I2C;255255+ input->name = pdev->name;256256+ input->open = max7360_keypad_open;257257+ input->close = max7360_keypad_close;258258+259259+ error = max7360_keypad_build_keymap(max7360_keypad);260260+ if (error)261261+ return dev_err_probe(dev, error, "Failed to build keymap\n");262262+263263+ input_set_capability(input, EV_MSC, MSC_SCAN);264264+ if (autorepeat)265265+ __set_bit(EV_REP, input->evbit);266266+267267+ input_set_drvdata(input, max7360_keypad);268268+269269+ error = devm_request_threaded_irq(dev, irq, NULL, max7360_keypad_irq,270270+ IRQF_ONESHOT,271271+ "max7360-keypad", max7360_keypad);272272+ if (error)273273+ return dev_err_probe(dev, error, "Failed to register interrupt\n");274274+275275+ error = input_register_device(input);276276+ if (error)277277+ return dev_err_probe(dev, error, "Could not register input device\n");278278+279279+ error = max7360_keypad_hw_init(max7360_keypad);280280+ if (error)281281+ return dev_err_probe(dev, error, "Failed to initialize max7360 keypad\n");282282+283283+ device_init_wakeup(dev, true);284284+ error = dev_pm_set_wake_irq(dev, irq);285285+ if (error)286286+ dev_warn(dev, "Failed to set up wakeup irq: %d\n", error);287287+288288+ return 0;289289+}290290+291291+static void max7360_keypad_remove(struct platform_device *pdev)292292+{293293+ dev_pm_clear_wake_irq(&pdev->dev);294294+ device_init_wakeup(&pdev->dev, false);295295+}296296+297297+static struct platform_driver max7360_keypad_driver = {298298+ .driver = {299299+ .name = "max7360-keypad",300300+ },301301+ .probe = max7360_keypad_probe,302302+ .remove = max7360_keypad_remove,303303+};304304+module_platform_driver(max7360_keypad_driver);305305+306306+MODULE_DESCRIPTION("MAX7360 Keypad driver");307307+MODULE_AUTHOR("Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>");308308+MODULE_LICENSE("GPL");
+20
drivers/input/misc/Kconfig
···230230 tristate "M68k Beeper support"231231 depends on M68K232232233233+config INPUT_MAX7360_ROTARY234234+ tristate "Maxim MAX7360 Rotary Encoder"235235+ depends on MFD_MAX7360236236+ help237237+ If you say yes here you get support for the rotary encoder on the238238+ Maxim MAX7360 I/O Expander.239239+240240+ To compile this driver as a module, choose M here: the module will be241241+ called max7360_rotary.242242+233243config INPUT_MAX77650_ONKEY234244 tristate "Maxim MAX77650 ONKEY support"235245 depends on MFD_MAX77650···515505516506 To compile this driver as a module, choose M here. The module will517507 be called tps65219-pwrbutton.508508+509509+config INPUT_TPS6594_PWRBUTTON510510+ tristate "TPS6594 Power button driver"511511+ depends on MFD_TPS6594512512+ help513513+ Say Y here if you want to enable power button reporting for514514+ TPS6594 Power Management IC devices.515515+516516+ To compile this driver as a module, choose M here. The module will517517+ be called tps6594-pwrbutton.518518519519config INPUT_AXP20X_PEK520520 tristate "X-Powers AXP20X power button driver"
···4242{4343 struct mc13783_ts_priv *priv = data;44444545- mc13xxx_irq_ack(priv->mc13xxx, irq);4646-4745 /*4846 * Kick off reading coordinates. Note that if work happens already4947 * be queued for future execution (it rearms itself) it will not···134136 int ret;135137136138 mc13xxx_lock(priv->mc13xxx);137137-138138- mc13xxx_irq_ack(priv->mc13xxx, MC13XXX_IRQ_TS);139139140140 ret = mc13xxx_irq_request(priv->mc13xxx, MC13XXX_IRQ_TS,141141 mc13783_ts_handler, MC13783_TS_NAME, priv);
+40
drivers/mfd/Kconfig
···11341134 This driver can also be built as a module. If so the module11351135 will be called menf21bmc.1136113611371137+config MFD_NCT669411381138+ tristate "Nuvoton NCT6694 support"11391139+ select MFD_CORE11401140+ depends on USB11411141+ help11421142+ This enables support for the Nuvoton USB device NCT6694, which shares11431143+ peripherals.11441144+ The Nuvoton NCT6694 is a peripheral expander with 16 GPIO chips,11451145+ 6 I2C controllers, 2 CANfd controllers, 2 Watchdog timers, ADC,11461146+ PWM, and RTC.11471147+ This driver provides core APIs to access the NCT6694 hardware11481148+ monitoring and control features.11491149+ Additional drivers must be enabled to utilize the specific11501150+ functionalities of the device.11511151+11371152config MFD_OCELOT11381153 tristate "Microsemi Ocelot External Control Support"11391154 depends on SPI_MASTER···16551640 TI LMU MFD supports LM3532, LM3631, LM3632, LM3633, LM3695 and16561641 LM36274. It consists of backlight, LED and regulator driver.16571642 It provides consistent device controls for lighting functions.16431643+16441644+config MFD_BQ257XX16451645+ tristate "TI BQ257XX Buck/Boost Charge Controller"16461646+ depends on I2C16471647+ select MFD_CORE16481648+ select REGMAP_I2C16491649+ help16501650+ Support Texas Instruments BQ25703 Buck/Boost converter with16511651+ charge controller. It consists of regulators that provide16521652+ system voltage and OTG voltage, and a charger manager for16531653+ batteries containing one or more cells.1658165416591655config MFD_OMAP_USB_HOST16601656 bool "TI OMAP USBHS core and TLL driver"···2517249125182492 To compile this driver as a module, choose M here: the module will be25192493 called upboard-fpga.24942494+24952495+config MFD_MAX736024962496+ tristate "Maxim MAX7360 I2C IO Expander"24972497+ depends on I2C24982498+ select MFD_CORE24992499+ select REGMAP_I2C25002500+ select REGMAP_IRQ25012501+ help25022502+ Say yes here to add support for Maxim MAX7360 device, embedding25032503+ keypad, rotary encoder, PWM and GPIO features.25042504+25052505+ This driver provides common support for accessing the device;25062506+ additional drivers must be enabled in order to use the functionality25072507+ of the device.2520250825212509endmenu25222510endif
···11+// SPDX-License-Identifier: GPL-2.0-only22+/*33+ * Maxim MAX7360 Core Driver44+ *55+ * Copyright 2025 Bootlin66+ *77+ * Authors:88+ * Kamel Bouhara <kamel.bouhara@bootlin.com>99+ * Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>1010+ */1111+1212+#include <linux/array_size.h>1313+#include <linux/bits.h>1414+#include <linux/delay.h>1515+#include <linux/device/devres.h>1616+#include <linux/dev_printk.h>1717+#include <linux/err.h>1818+#include <linux/i2c.h>1919+#include <linux/interrupt.h>2020+#include <linux/mfd/core.h>2121+#include <linux/mfd/max7360.h>2222+#include <linux/mod_devicetable.h>2323+#include <linux/module.h>2424+#include <linux/regmap.h>2525+#include <linux/types.h>2626+2727+static const struct mfd_cell max7360_cells[] = {2828+ { .name = "max7360-pinctrl" },2929+ { .name = "max7360-pwm" },3030+ { .name = "max7360-keypad" },3131+ { .name = "max7360-rotary" },3232+ {3333+ .name = "max7360-gpo",3434+ .of_compatible = "maxim,max7360-gpo",3535+ },3636+ {3737+ .name = "max7360-gpio",3838+ .of_compatible = "maxim,max7360-gpio",3939+ },4040+};4141+4242+static const struct regmap_range max7360_volatile_ranges[] = {4343+ regmap_reg_range(MAX7360_REG_KEYFIFO, MAX7360_REG_KEYFIFO),4444+ regmap_reg_range(MAX7360_REG_I2C_TIMEOUT, MAX7360_REG_RTR_CNT),4545+};4646+4747+static const struct regmap_access_table max7360_volatile_table = {4848+ .yes_ranges = max7360_volatile_ranges,4949+ .n_yes_ranges = ARRAY_SIZE(max7360_volatile_ranges),5050+};5151+5252+static const struct regmap_config max7360_regmap_config = {5353+ .reg_bits = 8,5454+ .val_bits = 8,5555+ .max_register = MAX7360_REG_PWMCFG(MAX7360_PORT_PWM_COUNT - 1),5656+ .volatile_table = &max7360_volatile_table,5757+ .cache_type = REGCACHE_MAPLE,5858+};5959+6060+static int max7360_mask_irqs(struct regmap *regmap)6161+{6262+ struct device *dev = regmap_get_device(regmap);6363+ unsigned int val;6464+ int ret;6565+6666+ /*6767+ * GPIO/PWM interrupts are not masked on reset: as the MAX7360 "INTI"6868+ * interrupt line is shared between GPIOs and rotary encoder, this could6969+ * result in repeated spurious interrupts on the rotary encoder driver7070+ * if the GPIO driver is not loaded. Mask them now to avoid this7171+ * situation.7272+ */7373+ for (unsigned int i = 0; i < MAX7360_PORT_PWM_COUNT; i++) {7474+ ret = regmap_write_bits(regmap, MAX7360_REG_PWMCFG(i),7575+ MAX7360_PORT_CFG_INTERRUPT_MASK,7676+ MAX7360_PORT_CFG_INTERRUPT_MASK);7777+ if (ret)7878+ return dev_err_probe(dev, ret,7979+ "Failed to write MAX7360 port configuration\n");8080+ }8181+8282+ /* Read GPIO in register, to ACK any pending IRQ. */8383+ ret = regmap_read(regmap, MAX7360_REG_GPIOIN, &val);8484+ if (ret)8585+ return dev_err_probe(dev, ret, "Failed to read GPIO values\n");8686+8787+ return 0;8888+}8989+9090+static int max7360_reset(struct regmap *regmap)9191+{9292+ struct device *dev = regmap_get_device(regmap);9393+ int ret;9494+9595+ ret = regmap_write(regmap, MAX7360_REG_GPIOCFG, MAX7360_GPIO_CFG_GPIO_RST);9696+ if (ret) {9797+ dev_err(dev, "Failed to reset GPIO configuration: %x\n", ret);9898+ return ret;9999+ }100100+101101+ ret = regcache_drop_region(regmap, MAX7360_REG_GPIOCFG, MAX7360_REG_GPIO_LAST);102102+ if (ret) {103103+ dev_err(dev, "Failed to drop regmap cache: %x\n", ret);104104+ return ret;105105+ }106106+107107+ ret = regmap_write(regmap, MAX7360_REG_SLEEP, 0);108108+ if (ret) {109109+ dev_err(dev, "Failed to reset autosleep configuration: %x\n", ret);110110+ return ret;111111+ }112112+113113+ ret = regmap_write(regmap, MAX7360_REG_DEBOUNCE, 0);114114+ if (ret)115115+ dev_err(dev, "Failed to reset GPO port count: %x\n", ret);116116+117117+ return ret;118118+}119119+120120+static int max7360_probe(struct i2c_client *client)121121+{122122+ struct device *dev = &client->dev;123123+ struct regmap *regmap;124124+ int ret;125125+126126+ regmap = devm_regmap_init_i2c(client, &max7360_regmap_config);127127+ if (IS_ERR(regmap))128128+ return dev_err_probe(dev, PTR_ERR(regmap), "Failed to initialise regmap\n");129129+130130+ ret = max7360_reset(regmap);131131+ if (ret)132132+ return dev_err_probe(dev, ret, "Failed to reset device\n");133133+134134+ /* Get the device out of shutdown mode. */135135+ ret = regmap_write_bits(regmap, MAX7360_REG_GPIOCFG,136136+ MAX7360_GPIO_CFG_GPIO_EN,137137+ MAX7360_GPIO_CFG_GPIO_EN);138138+ if (ret)139139+ return dev_err_probe(dev, ret, "Failed to enable GPIO and PWM module\n");140140+141141+ ret = max7360_mask_irqs(regmap);142142+ if (ret)143143+ return dev_err_probe(dev, ret, "Could not mask interrupts\n");144144+145145+ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,146146+ max7360_cells, ARRAY_SIZE(max7360_cells),147147+ NULL, 0, NULL);148148+ if (ret)149149+ return dev_err_probe(dev, ret, "Failed to register child devices\n");150150+151151+ return 0;152152+}153153+154154+static const struct of_device_id max7360_dt_match[] = {155155+ { .compatible = "maxim,max7360" },156156+ {}157157+};158158+MODULE_DEVICE_TABLE(of, max7360_dt_match);159159+160160+static struct i2c_driver max7360_driver = {161161+ .driver = {162162+ .name = "max7360",163163+ .of_match_table = max7360_dt_match,164164+ },165165+ .probe = max7360_probe,166166+};167167+module_i2c_driver(max7360_driver);168168+169169+MODULE_DESCRIPTION("Maxim MAX7360 I2C IO Expander core driver");170170+MODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>");171171+MODULE_LICENSE("GPL");
+388
drivers/mfd/nct6694.c
···11+// SPDX-License-Identifier: GPL-2.022+/*33+ * Copyright (C) 2025 Nuvoton Technology Corp.44+ *55+ * Nuvoton NCT6694 core driver using USB interface to provide66+ * access to the NCT6694 hardware monitoring and control features.77+ *88+ * The NCT6694 is an integrated controller that provides GPIO, I2C,99+ * CAN, WDT, HWMON and RTC management.1010+ */1111+1212+#include <linux/bits.h>1313+#include <linux/interrupt.h>1414+#include <linux/idr.h>1515+#include <linux/irq.h>1616+#include <linux/irqdomain.h>1717+#include <linux/kernel.h>1818+#include <linux/mfd/core.h>1919+#include <linux/mfd/nct6694.h>2020+#include <linux/module.h>2121+#include <linux/slab.h>2222+#include <linux/spinlock.h>2323+#include <linux/usb.h>2424+2525+static const struct mfd_cell nct6694_devs[] = {2626+ MFD_CELL_NAME("nct6694-gpio"),2727+ MFD_CELL_NAME("nct6694-gpio"),2828+ MFD_CELL_NAME("nct6694-gpio"),2929+ MFD_CELL_NAME("nct6694-gpio"),3030+ MFD_CELL_NAME("nct6694-gpio"),3131+ MFD_CELL_NAME("nct6694-gpio"),3232+ MFD_CELL_NAME("nct6694-gpio"),3333+ MFD_CELL_NAME("nct6694-gpio"),3434+ MFD_CELL_NAME("nct6694-gpio"),3535+ MFD_CELL_NAME("nct6694-gpio"),3636+ MFD_CELL_NAME("nct6694-gpio"),3737+ MFD_CELL_NAME("nct6694-gpio"),3838+ MFD_CELL_NAME("nct6694-gpio"),3939+ MFD_CELL_NAME("nct6694-gpio"),4040+ MFD_CELL_NAME("nct6694-gpio"),4141+ MFD_CELL_NAME("nct6694-gpio"),4242+4343+ MFD_CELL_NAME("nct6694-i2c"),4444+ MFD_CELL_NAME("nct6694-i2c"),4545+ MFD_CELL_NAME("nct6694-i2c"),4646+ MFD_CELL_NAME("nct6694-i2c"),4747+ MFD_CELL_NAME("nct6694-i2c"),4848+ MFD_CELL_NAME("nct6694-i2c"),4949+5050+ MFD_CELL_NAME("nct6694-canfd"),5151+ MFD_CELL_NAME("nct6694-canfd"),5252+5353+ MFD_CELL_NAME("nct6694-wdt"),5454+ MFD_CELL_NAME("nct6694-wdt"),5555+5656+ MFD_CELL_NAME("nct6694-hwmon"),5757+5858+ MFD_CELL_NAME("nct6694-rtc"),5959+};6060+6161+static int nct6694_response_err_handling(struct nct6694 *nct6694, unsigned char err_status)6262+{6363+ switch (err_status) {6464+ case NCT6694_NO_ERROR:6565+ return 0;6666+ case NCT6694_NOT_SUPPORT_ERROR:6767+ dev_err(nct6694->dev, "Command is not supported!\n");6868+ break;6969+ case NCT6694_NO_RESPONSE_ERROR:7070+ dev_warn(nct6694->dev, "Command received no response!\n");7171+ break;7272+ case NCT6694_TIMEOUT_ERROR:7373+ dev_warn(nct6694->dev, "Command timed out!\n");7474+ break;7575+ case NCT6694_PENDING:7676+ dev_err(nct6694->dev, "Command is pending!\n");7777+ break;7878+ default:7979+ return -EINVAL;8080+ }8181+8282+ return -EIO;8383+}8484+8585+/**8686+ * nct6694_read_msg() - Read message from NCT6694 device8787+ * @nct6694: NCT6694 device pointer8888+ * @cmd_hd: command header structure8989+ * @buf: buffer to store the response data9090+ *9191+ * Sends a command to the NCT6694 device and reads the response.9292+ * The command header is specified in @cmd_hd, and the response9393+ * data is stored in @buf.9494+ *9595+ * Return: Negative value on error or 0 on success.9696+ */9797+int nct6694_read_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *cmd_hd, void *buf)9898+{9999+ union nct6694_usb_msg *msg = nct6694->usb_msg;100100+ struct usb_device *udev = nct6694->udev;101101+ int tx_len, rx_len, ret;102102+103103+ guard(mutex)(&nct6694->access_lock);104104+105105+ memcpy(&msg->cmd_header, cmd_hd, sizeof(*cmd_hd));106106+ msg->cmd_header.hctrl = NCT6694_HCTRL_GET;107107+108108+ /* Send command packet to USB device */109109+ ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, NCT6694_BULK_OUT_EP), &msg->cmd_header,110110+ sizeof(*msg), &tx_len, NCT6694_URB_TIMEOUT);111111+ if (ret)112112+ return ret;113113+114114+ /* Receive response packet from USB device */115115+ ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, NCT6694_BULK_IN_EP), &msg->response_header,116116+ sizeof(*msg), &rx_len, NCT6694_URB_TIMEOUT);117117+ if (ret)118118+ return ret;119119+120120+ /* Receive data packet from USB device */121121+ ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, NCT6694_BULK_IN_EP), buf,122122+ le16_to_cpu(cmd_hd->len), &rx_len, NCT6694_URB_TIMEOUT);123123+ if (ret)124124+ return ret;125125+126126+ if (rx_len != le16_to_cpu(cmd_hd->len)) {127127+ dev_err(nct6694->dev, "Expected received length %d, but got %d\n",128128+ le16_to_cpu(cmd_hd->len), rx_len);129129+ return -EIO;130130+ }131131+132132+ return nct6694_response_err_handling(nct6694, msg->response_header.sts);133133+}134134+EXPORT_SYMBOL_GPL(nct6694_read_msg);135135+136136+/**137137+ * nct6694_write_msg() - Write message to NCT6694 device138138+ * @nct6694: NCT6694 device pointer139139+ * @cmd_hd: command header structure140140+ * @buf: buffer containing the data to be sent141141+ *142142+ * Sends a command to the NCT6694 device and writes the data143143+ * from @buf. The command header is specified in @cmd_hd.144144+ *145145+ * Return: Negative value on error or 0 on success.146146+ */147147+int nct6694_write_msg(struct nct6694 *nct6694, const struct nct6694_cmd_header *cmd_hd, void *buf)148148+{149149+ union nct6694_usb_msg *msg = nct6694->usb_msg;150150+ struct usb_device *udev = nct6694->udev;151151+ int tx_len, rx_len, ret;152152+153153+ guard(mutex)(&nct6694->access_lock);154154+155155+ memcpy(&msg->cmd_header, cmd_hd, sizeof(*cmd_hd));156156+ msg->cmd_header.hctrl = NCT6694_HCTRL_SET;157157+158158+ /* Send command packet to USB device */159159+ ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, NCT6694_BULK_OUT_EP), &msg->cmd_header,160160+ sizeof(*msg), &tx_len, NCT6694_URB_TIMEOUT);161161+ if (ret)162162+ return ret;163163+164164+ /* Send data packet to USB device */165165+ ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, NCT6694_BULK_OUT_EP), buf,166166+ le16_to_cpu(cmd_hd->len), &tx_len, NCT6694_URB_TIMEOUT);167167+ if (ret)168168+ return ret;169169+170170+ /* Receive response packet from USB device */171171+ ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, NCT6694_BULK_IN_EP), &msg->response_header,172172+ sizeof(*msg), &rx_len, NCT6694_URB_TIMEOUT);173173+ if (ret)174174+ return ret;175175+176176+ /* Receive data packet from USB device */177177+ ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, NCT6694_BULK_IN_EP), buf,178178+ le16_to_cpu(cmd_hd->len), &rx_len, NCT6694_URB_TIMEOUT);179179+ if (ret)180180+ return ret;181181+182182+ if (rx_len != le16_to_cpu(cmd_hd->len)) {183183+ dev_err(nct6694->dev, "Expected transmitted length %d, but got %d\n",184184+ le16_to_cpu(cmd_hd->len), rx_len);185185+ return -EIO;186186+ }187187+188188+ return nct6694_response_err_handling(nct6694, msg->response_header.sts);189189+}190190+EXPORT_SYMBOL_GPL(nct6694_write_msg);191191+192192+static void usb_int_callback(struct urb *urb)193193+{194194+ struct nct6694 *nct6694 = urb->context;195195+ __le32 *status_le = urb->transfer_buffer;196196+ u32 int_status;197197+ int ret;198198+199199+ switch (urb->status) {200200+ case 0:201201+ break;202202+ case -ECONNRESET:203203+ case -ENOENT:204204+ case -ESHUTDOWN:205205+ return;206206+ default:207207+ goto resubmit;208208+ }209209+210210+ int_status = le32_to_cpu(*status_le);211211+212212+ while (int_status) {213213+ int irq = __ffs(int_status);214214+215215+ generic_handle_irq_safe(irq_find_mapping(nct6694->domain, irq));216216+ int_status &= ~BIT(irq);217217+ }218218+219219+resubmit:220220+ ret = usb_submit_urb(urb, GFP_ATOMIC);221221+ if (ret)222222+ dev_warn(nct6694->dev, "Failed to resubmit urb, status %pe", ERR_PTR(ret));223223+}224224+225225+static void nct6694_irq_enable(struct irq_data *data)226226+{227227+ struct nct6694 *nct6694 = irq_data_get_irq_chip_data(data);228228+ irq_hw_number_t hwirq = irqd_to_hwirq(data);229229+230230+ guard(spinlock_irqsave)(&nct6694->irq_lock);231231+232232+ nct6694->irq_enable |= BIT(hwirq);233233+}234234+235235+static void nct6694_irq_disable(struct irq_data *data)236236+{237237+ struct nct6694 *nct6694 = irq_data_get_irq_chip_data(data);238238+ irq_hw_number_t hwirq = irqd_to_hwirq(data);239239+240240+ guard(spinlock_irqsave)(&nct6694->irq_lock);241241+242242+ nct6694->irq_enable &= ~BIT(hwirq);243243+}244244+245245+static const struct irq_chip nct6694_irq_chip = {246246+ .name = "nct6694-irq",247247+ .flags = IRQCHIP_SKIP_SET_WAKE,248248+ .irq_enable = nct6694_irq_enable,249249+ .irq_disable = nct6694_irq_disable,250250+};251251+252252+static int nct6694_irq_domain_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)253253+{254254+ struct nct6694 *nct6694 = d->host_data;255255+256256+ irq_set_chip_data(irq, nct6694);257257+ irq_set_chip_and_handler(irq, &nct6694_irq_chip, handle_simple_irq);258258+259259+ return 0;260260+}261261+262262+static void nct6694_irq_domain_unmap(struct irq_domain *d, unsigned int irq)263263+{264264+ irq_set_chip_and_handler(irq, NULL, NULL);265265+ irq_set_chip_data(irq, NULL);266266+}267267+268268+static const struct irq_domain_ops nct6694_irq_domain_ops = {269269+ .map = nct6694_irq_domain_map,270270+ .unmap = nct6694_irq_domain_unmap,271271+};272272+273273+static int nct6694_usb_probe(struct usb_interface *iface,274274+ const struct usb_device_id *id)275275+{276276+ struct usb_device *udev = interface_to_usbdev(iface);277277+ struct usb_endpoint_descriptor *int_endpoint;278278+ struct usb_host_interface *interface;279279+ struct device *dev = &iface->dev;280280+ struct nct6694 *nct6694;281281+ int ret;282282+283283+ nct6694 = devm_kzalloc(dev, sizeof(*nct6694), GFP_KERNEL);284284+ if (!nct6694)285285+ return -ENOMEM;286286+287287+ nct6694->usb_msg = devm_kzalloc(dev, sizeof(union nct6694_usb_msg), GFP_KERNEL);288288+ if (!nct6694->usb_msg)289289+ return -ENOMEM;290290+291291+ nct6694->int_buffer = devm_kzalloc(dev, sizeof(*nct6694->int_buffer), GFP_KERNEL);292292+ if (!nct6694->int_buffer)293293+ return -ENOMEM;294294+295295+ nct6694->int_in_urb = usb_alloc_urb(0, GFP_KERNEL);296296+ if (!nct6694->int_in_urb)297297+ return -ENOMEM;298298+299299+ nct6694->domain = irq_domain_create_simple(NULL, NCT6694_NR_IRQS, 0,300300+ &nct6694_irq_domain_ops,301301+ nct6694);302302+ if (!nct6694->domain) {303303+ ret = -ENODEV;304304+ goto err_urb;305305+ }306306+307307+ nct6694->dev = dev;308308+ nct6694->udev = udev;309309+310310+ ida_init(&nct6694->gpio_ida);311311+ ida_init(&nct6694->i2c_ida);312312+ ida_init(&nct6694->canfd_ida);313313+ ida_init(&nct6694->wdt_ida);314314+315315+ spin_lock_init(&nct6694->irq_lock);316316+317317+ ret = devm_mutex_init(dev, &nct6694->access_lock);318318+ if (ret)319319+ goto err_ida;320320+321321+ interface = iface->cur_altsetting;322322+323323+ int_endpoint = &interface->endpoint[0].desc;324324+ if (!usb_endpoint_is_int_in(int_endpoint)) {325325+ ret = -ENODEV;326326+ goto err_ida;327327+ }328328+329329+ usb_fill_int_urb(nct6694->int_in_urb, udev, usb_rcvintpipe(udev, NCT6694_INT_IN_EP),330330+ nct6694->int_buffer, sizeof(*nct6694->int_buffer), usb_int_callback,331331+ nct6694, int_endpoint->bInterval);332332+333333+ ret = usb_submit_urb(nct6694->int_in_urb, GFP_KERNEL);334334+ if (ret)335335+ goto err_ida;336336+337337+ usb_set_intfdata(iface, nct6694);338338+339339+ ret = mfd_add_hotplug_devices(dev, nct6694_devs, ARRAY_SIZE(nct6694_devs));340340+ if (ret)341341+ goto err_mfd;342342+343343+ return 0;344344+345345+err_mfd:346346+ usb_kill_urb(nct6694->int_in_urb);347347+err_ida:348348+ ida_destroy(&nct6694->wdt_ida);349349+ ida_destroy(&nct6694->canfd_ida);350350+ ida_destroy(&nct6694->i2c_ida);351351+ ida_destroy(&nct6694->gpio_ida);352352+ irq_domain_remove(nct6694->domain);353353+err_urb:354354+ usb_free_urb(nct6694->int_in_urb);355355+ return ret;356356+}357357+358358+static void nct6694_usb_disconnect(struct usb_interface *iface)359359+{360360+ struct nct6694 *nct6694 = usb_get_intfdata(iface);361361+362362+ mfd_remove_devices(nct6694->dev);363363+ usb_kill_urb(nct6694->int_in_urb);364364+ ida_destroy(&nct6694->wdt_ida);365365+ ida_destroy(&nct6694->canfd_ida);366366+ ida_destroy(&nct6694->i2c_ida);367367+ ida_destroy(&nct6694->gpio_ida);368368+ irq_domain_remove(nct6694->domain);369369+ usb_free_urb(nct6694->int_in_urb);370370+}371371+372372+static const struct usb_device_id nct6694_ids[] = {373373+ { USB_DEVICE_AND_INTERFACE_INFO(NCT6694_VENDOR_ID, NCT6694_PRODUCT_ID, 0xFF, 0x00, 0x00) },374374+ { }375375+};376376+MODULE_DEVICE_TABLE(usb, nct6694_ids);377377+378378+static struct usb_driver nct6694_usb_driver = {379379+ .name = "nct6694",380380+ .id_table = nct6694_ids,381381+ .probe = nct6694_usb_probe,382382+ .disconnect = nct6694_usb_disconnect,383383+};384384+module_usb_driver(nct6694_usb_driver);385385+386386+MODULE_DESCRIPTION("Nuvoton NCT6694 core driver");387387+MODULE_AUTHOR("Ming Yu <tmyu0@nuvoton.com>");388388+MODULE_LICENSE("GPL");
+57-2
drivers/mfd/tps6594-core.c
···1010 * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/1111 */12121313+#include <linux/bitfield.h>1314#include <linux/completion.h>1415#include <linux/delay.h>1516#include <linux/interrupt.h>1617#include <linux/module.h>1718#include <linux/of.h>1919+#include <linux/reboot.h>18201921#include <linux/mfd/core.h>2022#include <linux/mfd/tps6594.h>21232224#define TPS6594_CRC_SYNC_TIMEOUT_MS 1502525+#define TPS65224_EN_SEL_PB 12626+#define TPS65224_GPIO3_SEL_PB 323272428/* Completion to synchronize CRC feature enabling on all PMICs */2529static DECLARE_COMPLETION(tps6594_crc_comp);···130126 DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_TIMER, TPS6594_IRQ_NAME_TIMER),131127 DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_ALARM, TPS6594_IRQ_NAME_ALARM),132128 DEFINE_RES_IRQ_NAMED(TPS6594_IRQ_POWER_UP, TPS6594_IRQ_NAME_POWERUP),129129+};130130+131131+static const struct resource tps6594_pwrbutton_resources[] = {132132+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_PB_FALL, TPS65224_IRQ_NAME_PB_FALL),133133+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_PB_RISE, TPS65224_IRQ_NAME_PB_RISE),134134+ DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_PB_SHORT, TPS65224_IRQ_NAME_PB_SHORT),133135};134136135137static const struct mfd_cell tps6594_common_cells[] = {···328318 DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_REG_UNLOCK, TPS65224_IRQ_NAME_REG_UNLOCK),329319 DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_TWARN, TPS65224_IRQ_NAME_TWARN),330320 DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_PB_LONG, TPS65224_IRQ_NAME_PB_LONG),331331- DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_PB_FALL, TPS65224_IRQ_NAME_PB_FALL),332332- DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_PB_RISE, TPS65224_IRQ_NAME_PB_RISE),333321 DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_TSD_ORD, TPS65224_IRQ_NAME_TSD_ORD),334322 DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_BIST_FAIL, TPS65224_IRQ_NAME_BIST_FAIL),335323 DEFINE_RES_IRQ_NAMED(TPS65224_IRQ_REG_CRC_ERR, TPS65224_IRQ_NAME_REG_CRC_ERR),···353345 MFD_CELL_RES("tps6594-pfsm", tps65224_pfsm_resources),354346 MFD_CELL_RES("tps6594-pinctrl", tps65224_pinctrl_resources),355347 MFD_CELL_RES("tps6594-regulator", tps65224_regulator_resources),348348+};349349+350350+static const struct mfd_cell tps6594_pwrbutton_cell = {351351+ .name = "tps6594-pwrbutton",352352+ .resources = tps6594_pwrbutton_resources,353353+ .num_resources = ARRAY_SIZE(tps6594_pwrbutton_resources),356354};357355358356static const struct regmap_irq tps65224_irqs[] = {···690676 return ret;691677}692678679679+static int tps6594_power_off_handler(struct sys_off_data *data)680680+{681681+ struct tps6594 *tps = data->cb_data;682682+ int ret;683683+684684+ ret = regmap_update_bits(tps->regmap, TPS6594_REG_FSM_I2C_TRIGGERS,685685+ TPS6594_BIT_TRIGGER_I2C(0), TPS6594_BIT_TRIGGER_I2C(0));686686+ if (ret)687687+ return notifier_from_errno(ret);688688+689689+ return NOTIFY_DONE;690690+}691691+693692int tps6594_device_init(struct tps6594 *tps, bool enable_crc)694693{695694 struct device *dev = tps->dev;696695 int ret;697696 struct regmap_irq_chip *irq_chip;697697+ unsigned int pwr_on, gpio3_cfg;698698 const struct mfd_cell *cells;699699 int n_cells;700700···755727 if (ret)756728 return dev_err_probe(dev, ret, "Failed to add common child devices\n");757729730730+ /* If either the PB/EN/VSENSE or GPIO3 is configured as PB, register a driver for it */731731+ if (tps->chip_id == TPS65224 || tps->chip_id == TPS652G1) {732732+ ret = regmap_read(tps->regmap, TPS6594_REG_NPWRON_CONF, &pwr_on);733733+ if (ret)734734+ return dev_err_probe(dev, ret, "Failed to read PB/EN/VSENSE config\n");735735+736736+ ret = regmap_read(tps->regmap, TPS6594_REG_GPIOX_CONF(2), &gpio3_cfg);737737+ if (ret)738738+ return dev_err_probe(dev, ret, "Failed to read GPIO3 config\n");739739+740740+ if (FIELD_GET(TPS65224_MASK_EN_PB_VSENSE_CONFIG, pwr_on) == TPS65224_EN_SEL_PB ||741741+ FIELD_GET(TPS65224_MASK_GPIO_SEL, gpio3_cfg) == TPS65224_GPIO3_SEL_PB) {742742+ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO,743743+ &tps6594_pwrbutton_cell, 1, NULL, 0,744744+ regmap_irq_get_domain(tps->irq_data));745745+ if (ret)746746+ return dev_err_probe(dev, ret,747747+ "Failed to add power button device.\n");748748+ }749749+ }750750+758751 /* No RTC for LP8764, TPS65224 and TPS652G1 */759752 if (tps->chip_id != LP8764 && tps->chip_id != TPS65224 && tps->chip_id != TPS652G1) {760753 ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, tps6594_rtc_cells,···783734 regmap_irq_get_domain(tps->irq_data));784735 if (ret)785736 return dev_err_probe(dev, ret, "Failed to add RTC child device\n");737737+ }738738+739739+ if (of_device_is_system_power_controller(dev->of_node)) {740740+ ret = devm_register_power_off_handler(tps->dev, tps6594_power_off_handler, tps);741741+ if (ret)742742+ return dev_err_probe(dev, ret, "Failed to register power-off handler\n");786743 }787744788745 return 0;
+20-5
drivers/mfd/vexpress-sysreg.c
···55 */6677#include <linux/gpio/driver.h>88+#include <linux/gpio/generic.h>89#include <linux/err.h>910#include <linux/io.h>1011#include <linux/mfd/core.h>···97969897static int vexpress_sysreg_probe(struct platform_device *pdev)9998{9999+ struct gpio_generic_chip *mmc_gpio_chip;100100+ struct gpio_generic_chip_config config;100101 struct resource *mem;101102 void __iomem *base;102102- struct gpio_chip *mmc_gpio_chip;103103+ int ret;103104104105 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);105106 if (!mem)···119116 GFP_KERNEL);120117 if (!mmc_gpio_chip)121118 return -ENOMEM;122122- bgpio_init(mmc_gpio_chip, &pdev->dev, 0x4, base + SYS_MCI,123123- NULL, NULL, NULL, NULL, 0);124124- mmc_gpio_chip->ngpio = 2;125125- devm_gpiochip_add_data(&pdev->dev, mmc_gpio_chip, NULL);119119+120120+ config = (typeof(config)){121121+ .dev = &pdev->dev,122122+ .sz = 4,123123+ .dat = base + SYS_MCI,124124+ };125125+126126+ ret = gpio_generic_chip_init(mmc_gpio_chip, &config);127127+ if (ret)128128+ return ret;129129+130130+ mmc_gpio_chip->gc.ngpio = 2;131131+132132+ ret = devm_gpiochip_add_data(&pdev->dev, &mmc_gpio_chip->gc, NULL);133133+ if (ret)134134+ return ret;126135127136 return devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO,128137 vexpress_sysreg_cells,
+11
drivers/net/can/usb/Kconfig
···134134 This driver supports the CAN BUS Analyzer interface135135 from Microchip (http://www.microchip.com/development-tools/).136136137137+config CAN_NCT6694138138+ tristate "Nuvoton NCT6694 Socket CANfd support"139139+ depends on MFD_NCT6694140140+ select CAN_RX_OFFLOAD141141+ help142142+ If you say yes to this option, support will be included for Nuvoton143143+ NCT6694, a USB device to socket CANfd controller.144144+145145+ This driver can also be built as a module. If so, the module will146146+ be called nct6694_canfd.147147+137148config CAN_PEAK_USB138149 tristate "PEAK PCAN-USB/USB Pro interfaces for CAN 2.0b/CAN-FD"139150 help
···358358 help359359 Pinctrl driver for NXP LPC18xx/43xx System Control Unit (SCU).360360361361+config PINCTRL_MAX7360362362+ tristate "MAX7360 Pincontrol support"363363+ depends on MFD_MAX7360364364+ select PINMUX365365+ select GENERIC_PINCONF366366+ help367367+ Say Y here to enable pin control support for Maxim MAX7360 keypad368368+ controller.369369+ This keypad controller has 8 GPIO pins that may work as GPIO, or PWM,370370+ or rotary encoder alternate modes.371371+361372config PINCTRL_MAX77620362373 tristate "MAX77620/MAX20024 Pincontrol support"363374 depends on MFD_MAX77620 && OF
···11+// SPDX-License-Identifier: GPL-2.0-only22+/*33+ * Copyright 2025 Bootlin44+ *55+ * Author: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>66+ */77+88+#include <linux/array_size.h>99+#include <linux/dev_printk.h>1010+#include <linux/device.h>1111+#include <linux/device/devres.h>1212+#include <linux/err.h>1313+#include <linux/init.h>1414+#include <linux/mfd/max7360.h>1515+#include <linux/module.h>1616+#include <linux/platform_device.h>1717+#include <linux/regmap.h>1818+#include <linux/stddef.h>1919+2020+#include <linux/pinctrl/pinctrl.h>2121+#include <linux/pinctrl/pinconf-generic.h>2222+#include <linux/pinctrl/pinmux.h>2323+2424+#include "core.h"2525+#include "pinmux.h"2626+2727+struct max7360_pinctrl {2828+ struct pinctrl_dev *pctldev;2929+ struct pinctrl_desc pinctrl_desc;3030+};3131+3232+static const struct pinctrl_pin_desc max7360_pins[] = {3333+ PINCTRL_PIN(0, "PORT0"),3434+ PINCTRL_PIN(1, "PORT1"),3535+ PINCTRL_PIN(2, "PORT2"),3636+ PINCTRL_PIN(3, "PORT3"),3737+ PINCTRL_PIN(4, "PORT4"),3838+ PINCTRL_PIN(5, "PORT5"),3939+ PINCTRL_PIN(6, "PORT6"),4040+ PINCTRL_PIN(7, "PORT7"),4141+};4242+4343+static const unsigned int port0_pins[] = {0};4444+static const unsigned int port1_pins[] = {1};4545+static const unsigned int port2_pins[] = {2};4646+static const unsigned int port3_pins[] = {3};4747+static const unsigned int port4_pins[] = {4};4848+static const unsigned int port5_pins[] = {5};4949+static const unsigned int port6_pins[] = {6};5050+static const unsigned int port7_pins[] = {7};5151+static const unsigned int rotary_pins[] = {6, 7};5252+5353+static const struct pingroup max7360_groups[] = {5454+ PINCTRL_PINGROUP("PORT0", port0_pins, ARRAY_SIZE(port0_pins)),5555+ PINCTRL_PINGROUP("PORT1", port1_pins, ARRAY_SIZE(port1_pins)),5656+ PINCTRL_PINGROUP("PORT2", port2_pins, ARRAY_SIZE(port2_pins)),5757+ PINCTRL_PINGROUP("PORT3", port3_pins, ARRAY_SIZE(port3_pins)),5858+ PINCTRL_PINGROUP("PORT4", port4_pins, ARRAY_SIZE(port4_pins)),5959+ PINCTRL_PINGROUP("PORT5", port5_pins, ARRAY_SIZE(port5_pins)),6060+ PINCTRL_PINGROUP("PORT6", port6_pins, ARRAY_SIZE(port6_pins)),6161+ PINCTRL_PINGROUP("PORT7", port7_pins, ARRAY_SIZE(port7_pins)),6262+ PINCTRL_PINGROUP("ROTARY", rotary_pins, ARRAY_SIZE(rotary_pins)),6363+};6464+6565+static int max7360_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)6666+{6767+ return ARRAY_SIZE(max7360_groups);6868+}6969+7070+static const char *max7360_pinctrl_get_group_name(struct pinctrl_dev *pctldev,7171+ unsigned int group)7272+{7373+ return max7360_groups[group].name;7474+}7575+7676+static int max7360_pinctrl_get_group_pins(struct pinctrl_dev *pctldev,7777+ unsigned int group,7878+ const unsigned int **pins,7979+ unsigned int *num_pins)8080+{8181+ *pins = max7360_groups[group].pins;8282+ *num_pins = max7360_groups[group].npins;8383+ return 0;8484+}8585+8686+static const struct pinctrl_ops max7360_pinctrl_ops = {8787+ .get_groups_count = max7360_pinctrl_get_groups_count,8888+ .get_group_name = max7360_pinctrl_get_group_name,8989+ .get_group_pins = max7360_pinctrl_get_group_pins,9090+#ifdef CONFIG_OF9191+ .dt_node_to_map = pinconf_generic_dt_node_to_map_pin,9292+ .dt_free_map = pinconf_generic_dt_free_map,9393+#endif9494+};9595+9696+static const char * const simple_groups[] = {9797+ "PORT0", "PORT1", "PORT2", "PORT3",9898+ "PORT4", "PORT5", "PORT6", "PORT7",9999+};100100+101101+static const char * const rotary_groups[] = { "ROTARY" };102102+103103+#define MAX7360_PINCTRL_FN_GPIO 0104104+#define MAX7360_PINCTRL_FN_PWM 1105105+#define MAX7360_PINCTRL_FN_ROTARY 2106106+static const struct pinfunction max7360_functions[] = {107107+ [MAX7360_PINCTRL_FN_GPIO] = PINCTRL_PINFUNCTION("gpio", simple_groups,108108+ ARRAY_SIZE(simple_groups)),109109+ [MAX7360_PINCTRL_FN_PWM] = PINCTRL_PINFUNCTION("pwm", simple_groups,110110+ ARRAY_SIZE(simple_groups)),111111+ [MAX7360_PINCTRL_FN_ROTARY] = PINCTRL_PINFUNCTION("rotary", rotary_groups,112112+ ARRAY_SIZE(rotary_groups)),113113+};114114+115115+static int max7360_get_functions_count(struct pinctrl_dev *pctldev)116116+{117117+ return ARRAY_SIZE(max7360_functions);118118+}119119+120120+static const char *max7360_get_function_name(struct pinctrl_dev *pctldev, unsigned int selector)121121+{122122+ return max7360_functions[selector].name;123123+}124124+125125+static int max7360_get_function_groups(struct pinctrl_dev *pctldev, unsigned int selector,126126+ const char * const **groups,127127+ unsigned int * const num_groups)128128+{129129+ *groups = max7360_functions[selector].groups;130130+ *num_groups = max7360_functions[selector].ngroups;131131+132132+ return 0;133133+}134134+135135+static int max7360_set_mux(struct pinctrl_dev *pctldev, unsigned int selector,136136+ unsigned int group)137137+{138138+ struct regmap *regmap = dev_get_regmap(pctldev->dev->parent, NULL);139139+ int val;140140+141141+ /*142142+ * GPIO and PWM functions are the same: we only need to handle the143143+ * rotary encoder function, on pins 6 and 7.144144+ */145145+ if (max7360_groups[group].pins[0] >= 6) {146146+ if (selector == MAX7360_PINCTRL_FN_ROTARY)147147+ val = MAX7360_GPIO_CFG_RTR_EN;148148+ else149149+ val = 0;150150+151151+ return regmap_write_bits(regmap, MAX7360_REG_GPIOCFG, MAX7360_GPIO_CFG_RTR_EN, val);152152+ }153153+154154+ return 0;155155+}156156+157157+static const struct pinmux_ops max7360_pmxops = {158158+ .get_functions_count = max7360_get_functions_count,159159+ .get_function_name = max7360_get_function_name,160160+ .get_function_groups = max7360_get_function_groups,161161+ .set_mux = max7360_set_mux,162162+ .strict = true,163163+};164164+165165+static int max7360_pinctrl_probe(struct platform_device *pdev)166166+{167167+ struct regmap *regmap;168168+ struct pinctrl_desc *pd;169169+ struct max7360_pinctrl *chip;170170+ struct device *dev = &pdev->dev;171171+172172+ regmap = dev_get_regmap(dev->parent, NULL);173173+ if (!regmap)174174+ return dev_err_probe(dev, -ENODEV, "Could not get parent regmap\n");175175+176176+ chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);177177+ if (!chip)178178+ return -ENOMEM;179179+180180+ pd = &chip->pinctrl_desc;181181+182182+ pd->pctlops = &max7360_pinctrl_ops;183183+ pd->pmxops = &max7360_pmxops;184184+ pd->name = dev_name(dev);185185+ pd->pins = max7360_pins;186186+ pd->npins = MAX7360_MAX_GPIO;187187+ pd->owner = THIS_MODULE;188188+189189+ /*190190+ * This MFD sub-device does not have any associated device tree node:191191+ * properties are stored in the device node of the parent (MFD) device192192+ * and this same node is used in phandles of client devices.193193+ * Reuse this device tree node here, as otherwise the pinctrl subsystem194194+ * would be confused by this topology.195195+ */196196+ device_set_of_node_from_dev(dev, dev->parent);197197+198198+ chip->pctldev = devm_pinctrl_register(dev, pd, chip);199199+ if (IS_ERR(chip->pctldev))200200+ return dev_err_probe(dev, PTR_ERR(chip->pctldev), "can't register controller\n");201201+202202+ return 0;203203+}204204+205205+static struct platform_driver max7360_pinctrl_driver = {206206+ .driver = {207207+ .name = "max7360-pinctrl",208208+ },209209+ .probe = max7360_pinctrl_probe,210210+};211211+module_platform_driver(max7360_pinctrl_driver);212212+213213+MODULE_DESCRIPTION("MAX7360 pinctrl driver");214214+MODULE_AUTHOR("Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>");215215+MODULE_LICENSE("GPL");
+7
drivers/power/supply/Kconfig
···767767 rail, ADC for battery and system monitoring, and push-button768768 controller.769769770770+config CHARGER_BQ257XX771771+ tristate "TI BQ257XX battery charger family"772772+ depends on MFD_BQ257XX773773+ help774774+ Say Y to enable support for the TI BQ257XX family of battery775775+ charging integrated circuits.776776+770777config CHARGER_BQ25890771778 tristate "TI BQ25890 battery charger driver"772779 depends on I2C
···11+// SPDX-License-Identifier: GPL-2.022+/*33+ * BQ257XX Battery Charger Driver44+ * Copyright (C) 2025 Chris Morgan <macromorgan@hotmail.com>55+ */66+77+#include <linux/bitfield.h>88+#include <linux/i2c.h>99+#include <linux/interrupt.h>1010+#include <linux/mfd/bq257xx.h>1111+#include <linux/platform_device.h>1212+#include <linux/power_supply.h>1313+#include <linux/property.h>1414+#include <linux/regmap.h>1515+1616+/* Forward declaration of driver data. */1717+struct bq257xx_chg;1818+1919+/**2020+ * struct bq257xx_chip_info - chip specific routines2121+ * @bq257xx_hw_init: init function for hw2222+ * @bq257xx_hw_shutdown: shutdown function for hw2323+ * @bq257xx_get_state: get and update state of hardware2424+ * @bq257xx_set_ichg: set maximum charge current (in uA)2525+ * @bq257xx_set_vbatreg: set maximum charge voltage (in uV)2626+ * @bq257xx_set_iindpm: set maximum input current (in uA)2727+ */2828+struct bq257xx_chip_info {2929+ int (*bq257xx_hw_init)(struct bq257xx_chg *pdata);3030+ void (*bq257xx_hw_shutdown)(struct bq257xx_chg *pdata);3131+ int (*bq257xx_get_state)(struct bq257xx_chg *pdata);3232+ int (*bq257xx_set_ichg)(struct bq257xx_chg *pdata, int ichg);3333+ int (*bq257xx_set_vbatreg)(struct bq257xx_chg *pdata, int vbatreg);3434+ int (*bq257xx_set_iindpm)(struct bq257xx_chg *pdata, int iindpm);3535+};3636+3737+/**3838+ * struct bq257xx_chg - driver data for charger3939+ * @chip: hw specific functions4040+ * @bq: parent MFD device4141+ * @charger: power supply device4242+ * @online: charger input is present4343+ * @fast_charge: charger is in fast charge mode4444+ * @pre_charge: charger is in pre-charge mode4545+ * @ov_fault: charger reports over voltage fault4646+ * @batoc_fault: charger reports battery over current fault4747+ * @oc_fault: charger reports over current fault4848+ * @usb_type: USB type reported from parent power supply4949+ * @supplied: Status of parent power supply5050+ * @iindpm_max: maximum input current limit (uA)5151+ * @vbat_max: maximum charge voltage (uV)5252+ * @ichg_max: maximum charge current (uA)5353+ * @vsys_min: minimum system voltage (uV)5454+ */5555+struct bq257xx_chg {5656+ const struct bq257xx_chip_info *chip;5757+ struct bq257xx_device *bq;5858+ struct power_supply *charger;5959+ bool online;6060+ bool fast_charge;6161+ bool pre_charge;6262+ bool ov_fault;6363+ bool batoc_fault;6464+ bool oc_fault;6565+ int usb_type;6666+ int supplied;6767+ u32 iindpm_max;6868+ u32 vbat_max;6969+ u32 ichg_max;7070+ u32 vsys_min;7171+};7272+7373+/**7474+ * bq25703_get_state() - Get the current state of the device7575+ * @pdata: driver platform data7676+ *7777+ * Get the current state of the charger. Check if the charger is7878+ * powered, what kind of charge state (if any) the device is in,7979+ * and if there are any active faults.8080+ *8181+ * Return: Returns 0 on success, or error on failure to read device.8282+ */8383+static int bq25703_get_state(struct bq257xx_chg *pdata)8484+{8585+ unsigned int reg;8686+ int ret;8787+8888+ ret = regmap_read(pdata->bq->regmap, BQ25703_CHARGER_STATUS, ®);8989+ if (ret)9090+ return ret;9191+9292+ pdata->online = reg & BQ25703_STS_AC_STAT;9393+ pdata->fast_charge = reg & BQ25703_STS_IN_FCHRG;9494+ pdata->pre_charge = reg & BQ25703_STS_IN_PCHRG;9595+ pdata->ov_fault = reg & BQ25703_STS_FAULT_ACOV;9696+ pdata->batoc_fault = reg & BQ25703_STS_FAULT_BATOC;9797+ pdata->oc_fault = reg & BQ25703_STS_FAULT_ACOC;9898+9999+ return 0;100100+}101101+102102+/**103103+ * bq25703_get_min_vsys() - Get the minimum system voltage104104+ * @pdata: driver platform data105105+ * @intval: value for minimum voltage106106+ *107107+ * Return: Returns 0 on success or error on failure to read.108108+ */109109+static int bq25703_get_min_vsys(struct bq257xx_chg *pdata, int *intval)110110+{111111+ unsigned int reg;112112+ int ret;113113+114114+ ret = regmap_read(pdata->bq->regmap, BQ25703_MIN_VSYS,115115+ ®);116116+ if (ret)117117+ return ret;118118+119119+ reg = FIELD_GET(BQ25703_MINVSYS_MASK, reg);120120+ *intval = (reg * BQ25703_MINVSYS_STEP_UV) + BQ25703_MINVSYS_MIN_UV;121121+122122+ return ret;123123+}124124+125125+/**126126+ * bq25703_set_min_vsys() - Set the minimum system voltage127127+ * @pdata: driver platform data128128+ * @vsys: voltage value to set in uV.129129+ *130130+ * This function takes a requested minimum system voltage value, clamps131131+ * it between the minimum supported value by the charger and a user132132+ * defined minimum system value, and then writes the value to the133133+ * appropriate register.134134+ *135135+ * Return: Returns 0 on success or error if an error occurs.136136+ */137137+static int bq25703_set_min_vsys(struct bq257xx_chg *pdata, int vsys)138138+{139139+ unsigned int reg;140140+ int vsys_min = pdata->vsys_min;141141+142142+ vsys = clamp(vsys, BQ25703_MINVSYS_MIN_UV, vsys_min);143143+ reg = ((vsys - BQ25703_MINVSYS_MIN_UV) / BQ25703_MINVSYS_STEP_UV);144144+ reg = FIELD_PREP(BQ25703_MINVSYS_MASK, reg);145145+146146+ return regmap_write(pdata->bq->regmap, BQ25703_MIN_VSYS,147147+ reg);148148+}149149+150150+/**151151+ * bq25703_get_cur() - Get the reported current from the battery152152+ * @pdata: driver platform data153153+ * @intval: value of reported battery current154154+ *155155+ * Read the reported current from the battery. Since value is always156156+ * positive set sign to negative if discharging.157157+ *158158+ * Return: Returns 0 on success or error if unable to read value.159159+ */160160+static int bq25703_get_cur(struct bq257xx_chg *pdata, int *intval)161161+{162162+ unsigned int reg;163163+ int ret;164164+165165+ ret = regmap_read(pdata->bq->regmap, BQ25703_ADCIBAT_CHG, ®);166166+ if (ret < 0)167167+ return ret;168168+169169+ if (pdata->online)170170+ *intval = FIELD_GET(BQ25703_ADCIBAT_CHG_MASK, reg) *171171+ BQ25703_ADCIBAT_CHG_STEP_UA;172172+ else173173+ *intval = -(FIELD_GET(BQ25703_ADCIBAT_DISCHG_MASK, reg) *174174+ BQ25703_ADCIBAT_DIS_STEP_UA);175175+176176+ return ret;177177+}178178+179179+/**180180+ * bq25703_get_ichg_cur() - Get the maximum reported charge current181181+ * @pdata: driver platform data182182+ * @intval: value of maximum reported charge current183183+ *184184+ * Get the maximum reported charge current from the battery.185185+ *186186+ * Return: Returns 0 on success or error if unable to read value.187187+ */188188+static int bq25703_get_ichg_cur(struct bq257xx_chg *pdata, int *intval)189189+{190190+ unsigned int reg;191191+ int ret;192192+193193+ ret = regmap_read(pdata->bq->regmap, BQ25703_CHARGE_CURRENT, ®);194194+ if (ret)195195+ return ret;196196+197197+ *intval = FIELD_GET(BQ25703_ICHG_MASK, reg) * BQ25703_ICHG_STEP_UA;198198+199199+ return ret;200200+}201201+202202+/**203203+ * bq25703_set_ichg_cur() - Set the maximum charge current204204+ * @pdata: driver platform data205205+ * @ichg: current value to set in uA.206206+ *207207+ * This function takes a requested maximum charge current value, clamps208208+ * it between the minimum supported value by the charger and a user209209+ * defined maximum charging value, and then writes the value to the210210+ * appropriate register.211211+ *212212+ * Return: Returns 0 on success or error if an error occurs.213213+ */214214+static int bq25703_set_ichg_cur(struct bq257xx_chg *pdata, int ichg)215215+{216216+ unsigned int reg;217217+ int ichg_max = pdata->ichg_max;218218+219219+ ichg = clamp(ichg, BQ25703_ICHG_MIN_UA, ichg_max);220220+ reg = FIELD_PREP(BQ25703_ICHG_MASK, (ichg / BQ25703_ICHG_STEP_UA));221221+222222+ return regmap_write(pdata->bq->regmap, BQ25703_CHARGE_CURRENT,223223+ reg);224224+}225225+226226+/**227227+ * bq25703_get_chrg_volt() - Get the maximum set charge voltage228228+ * @pdata: driver platform data229229+ * @intval: maximum charge voltage value230230+ *231231+ * Return: Returns 0 on success or error if unable to read value.232232+ */233233+static int bq25703_get_chrg_volt(struct bq257xx_chg *pdata, int *intval)234234+{235235+ unsigned int reg;236236+ int ret;237237+238238+ ret = regmap_read(pdata->bq->regmap, BQ25703_MAX_CHARGE_VOLT,239239+ ®);240240+ if (ret)241241+ return ret;242242+243243+ *intval = FIELD_GET(BQ25703_MAX_CHARGE_VOLT_MASK, reg) *244244+ BQ25703_VBATREG_STEP_UV;245245+246246+ return ret;247247+}248248+249249+/**250250+ * bq25703_set_chrg_volt() - Set the maximum charge voltage251251+ * @pdata: driver platform data252252+ * @vbat: voltage value to set in uV.253253+ *254254+ * This function takes a requested maximum charge voltage value, clamps255255+ * it between the minimum supported value by the charger and a user256256+ * defined maximum charging value, and then writes the value to the257257+ * appropriate register.258258+ *259259+ * Return: Returns 0 on success or error if an error occurs.260260+ */261261+static int bq25703_set_chrg_volt(struct bq257xx_chg *pdata, int vbat)262262+{263263+ unsigned int reg;264264+ int vbat_max = pdata->vbat_max;265265+266266+ vbat = clamp(vbat, BQ25703_VBATREG_MIN_UV, vbat_max);267267+268268+ reg = FIELD_PREP(BQ25703_MAX_CHARGE_VOLT_MASK,269269+ (vbat / BQ25703_VBATREG_STEP_UV));270270+271271+ return regmap_write(pdata->bq->regmap, BQ25703_MAX_CHARGE_VOLT,272272+ reg);273273+}274274+275275+/**276276+ * bq25703_get_iindpm() - Get the maximum set input current277277+ * @pdata: driver platform data278278+ * @intval: maximum input current value279279+ *280280+ * Read the actual input current limit from the device into intval.281281+ * This can differ from the value programmed due to some autonomous282282+ * functions that may be enabled (but are not currently). This is why283283+ * there is a different register used.284284+ *285285+ * Return: Returns 0 on success or error if unable to read register286286+ * value.287287+ */288288+static int bq25703_get_iindpm(struct bq257xx_chg *pdata, int *intval)289289+{290290+ unsigned int reg;291291+ int ret;292292+293293+ ret = regmap_read(pdata->bq->regmap, BQ25703_IIN_DPM, ®);294294+ if (ret)295295+ return ret;296296+297297+ reg = FIELD_GET(BQ25703_IINDPM_MASK, reg);298298+ *intval = (reg * BQ25703_IINDPM_STEP_UA) + BQ25703_IINDPM_OFFSET_UA;299299+300300+ return ret;301301+}302302+303303+/**304304+ * bq25703_set_iindpm() - Set the maximum input current305305+ * @pdata: driver platform data306306+ * @iindpm: current value in uA.307307+ *308308+ * This function takes a requested maximum input current value, clamps309309+ * it between the minimum supported value by the charger and a user310310+ * defined maximum input value, and then writes the value to the311311+ * appropriate register.312312+ *313313+ * Return: Returns 0 on success or error if an error occurs.314314+ */315315+static int bq25703_set_iindpm(struct bq257xx_chg *pdata, int iindpm)316316+{317317+ unsigned int reg;318318+ int iindpm_max = pdata->iindpm_max;319319+320320+ iindpm = clamp(iindpm, BQ25703_IINDPM_MIN_UA, iindpm_max);321321+322322+ reg = ((iindpm - BQ25703_IINDPM_OFFSET_UA) / BQ25703_IINDPM_STEP_UA);323323+324324+ return regmap_write(pdata->bq->regmap, BQ25703_IIN_HOST,325325+ FIELD_PREP(BQ25703_IINDPM_MASK, reg));326326+}327327+328328+/**329329+ * bq25703_get_vbat() - Get the reported voltage from the battery330330+ * @pdata: driver platform data331331+ * @intval: value of reported battery voltage332332+ *333333+ * Read value of battery voltage into intval.334334+ *335335+ * Return: Returns 0 on success or error if unable to read value.336336+ */337337+static int bq25703_get_vbat(struct bq257xx_chg *pdata, int *intval)338338+{339339+ unsigned int reg;340340+ int ret;341341+342342+ ret = regmap_read(pdata->bq->regmap, BQ25703_ADCVSYSVBAT, ®);343343+ if (ret)344344+ return ret;345345+346346+ reg = FIELD_GET(BQ25703_ADCVBAT_MASK, reg);347347+ *intval = (reg * BQ25703_ADCVSYSVBAT_STEP) + BQ25703_ADCVSYSVBAT_OFFSET_UV;348348+349349+ return ret;350350+}351351+352352+/**353353+ * bq25703_hw_init() - Set all the required registers to init the charger354354+ * @pdata: driver platform data355355+ *356356+ * Initialize the BQ25703 by first disabling the watchdog timer (which357357+ * shuts off the charger in the absence of periodic writes). Then, set358358+ * the charge current, charge voltage, minimum system voltage, and359359+ * input current limit. Disable low power mode to allow ADCs and360360+ * interrupts. Enable the ADC, start the ADC, set the ADC scale to361361+ * full, and enable each individual ADC channel.362362+ *363363+ * Return: Returns 0 on success or error code on error.364364+ */365365+static int bq25703_hw_init(struct bq257xx_chg *pdata)366366+{367367+ struct regmap *regmap = pdata->bq->regmap;368368+ int ret = 0;369369+370370+ regmap_update_bits(regmap, BQ25703_CHARGE_OPTION_0,371371+ BQ25703_WDTMR_ADJ_MASK,372372+ FIELD_PREP(BQ25703_WDTMR_ADJ_MASK,373373+ BQ25703_WDTMR_DISABLE));374374+375375+ ret = pdata->chip->bq257xx_set_ichg(pdata, pdata->ichg_max);376376+ if (ret)377377+ return ret;378378+379379+ ret = pdata->chip->bq257xx_set_vbatreg(pdata, pdata->vbat_max);380380+ if (ret)381381+ return ret;382382+383383+ ret = bq25703_set_min_vsys(pdata, pdata->vsys_min);384384+ if (ret)385385+ return ret;386386+387387+ ret = pdata->chip->bq257xx_set_iindpm(pdata, pdata->iindpm_max);388388+ if (ret)389389+ return ret;390390+391391+ /* Disable low power mode by writing 0 to the register. */392392+ regmap_update_bits(regmap, BQ25703_CHARGE_OPTION_0,393393+ BQ25703_EN_LWPWR, 0);394394+395395+ /* Enable the ADC. */396396+ regmap_update_bits(regmap, BQ25703_ADC_OPTION,397397+ BQ25703_ADC_CONV_EN, BQ25703_ADC_CONV_EN);398398+399399+ /* Start the ADC. */400400+ regmap_update_bits(regmap, BQ25703_ADC_OPTION,401401+ BQ25703_ADC_START, BQ25703_ADC_START);402402+403403+ /* Set the scale of the ADC. */404404+ regmap_update_bits(regmap, BQ25703_ADC_OPTION,405405+ BQ25703_ADC_FULL_SCALE, BQ25703_ADC_FULL_SCALE);406406+407407+ /* Enable each of the ADC channels available. */408408+ regmap_update_bits(regmap, BQ25703_ADC_OPTION,409409+ BQ25703_ADC_CH_MASK,410410+ (BQ25703_ADC_CMPIN_EN | BQ25703_ADC_VBUS_EN |411411+ BQ25703_ADC_PSYS_EN | BQ25703_ADC_IIN_EN |412412+ BQ25703_ADC_IDCHG_EN | BQ25703_ADC_ICHG_EN |413413+ BQ25703_ADC_VSYS_EN | BQ25703_ADC_VBAT_EN));414414+415415+ return ret;416416+}417417+418418+/**419419+ * bq25703_hw_shutdown() - Set registers for shutdown420420+ * @pdata: driver platform data421421+ *422422+ * Enable low power mode for the device while in shutdown.423423+ */424424+static void bq25703_hw_shutdown(struct bq257xx_chg *pdata)425425+{426426+ regmap_update_bits(pdata->bq->regmap, BQ25703_CHARGE_OPTION_0,427427+ BQ25703_EN_LWPWR, BQ25703_EN_LWPWR);428428+}429429+430430+static int bq257xx_set_charger_property(struct power_supply *psy,431431+ enum power_supply_property prop,432432+ const union power_supply_propval *val)433433+{434434+ struct bq257xx_chg *pdata = power_supply_get_drvdata(psy);435435+436436+ switch (prop) {437437+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:438438+ return pdata->chip->bq257xx_set_iindpm(pdata, val->intval);439439+440440+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:441441+ return pdata->chip->bq257xx_set_vbatreg(pdata, val->intval);442442+443443+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:444444+ return pdata->chip->bq257xx_set_ichg(pdata, val->intval);445445+446446+ default:447447+ break;448448+ }449449+450450+ return -EINVAL;451451+}452452+453453+static int bq257xx_get_charger_property(struct power_supply *psy,454454+ enum power_supply_property psp,455455+ union power_supply_propval *val)456456+{457457+ struct bq257xx_chg *pdata = power_supply_get_drvdata(psy);458458+ int ret = 0;459459+460460+ ret = pdata->chip->bq257xx_get_state(pdata);461461+ if (ret)462462+ return ret;463463+464464+ switch (psp) {465465+ case POWER_SUPPLY_PROP_STATUS:466466+ if (!pdata->online)467467+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;468468+ else if (pdata->fast_charge || pdata->pre_charge)469469+ val->intval = POWER_SUPPLY_STATUS_CHARGING;470470+ else471471+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;472472+ break;473473+474474+ case POWER_SUPPLY_PROP_HEALTH:475475+ if (pdata->ov_fault || pdata->batoc_fault)476476+ val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;477477+ else if (pdata->oc_fault)478478+ val->intval = POWER_SUPPLY_HEALTH_OVERCURRENT;479479+ else480480+ val->intval = POWER_SUPPLY_HEALTH_GOOD;481481+ break;482482+483483+ case POWER_SUPPLY_PROP_MANUFACTURER:484484+ val->strval = "Texas Instruments";485485+ break;486486+487487+ case POWER_SUPPLY_PROP_ONLINE:488488+ val->intval = pdata->online;489489+ break;490490+491491+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:492492+ return bq25703_get_iindpm(pdata, &val->intval);493493+494494+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:495495+ return bq25703_get_chrg_volt(pdata, &val->intval);496496+497497+ case POWER_SUPPLY_PROP_CURRENT_NOW:498498+ return bq25703_get_cur(pdata, &val->intval);499499+500500+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:501501+ return bq25703_get_vbat(pdata, &val->intval);502502+503503+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:504504+ return bq25703_get_ichg_cur(pdata, &val->intval);505505+506506+ case POWER_SUPPLY_PROP_VOLTAGE_MIN:507507+ return bq25703_get_min_vsys(pdata, &val->intval);508508+509509+ case POWER_SUPPLY_PROP_USB_TYPE:510510+ val->intval = pdata->usb_type;511511+ break;512512+513513+ default:514514+ return -EINVAL;515515+ }516516+517517+ return ret;518518+}519519+520520+static enum power_supply_property bq257xx_power_supply_props[] = {521521+ POWER_SUPPLY_PROP_MANUFACTURER,522522+ POWER_SUPPLY_PROP_STATUS,523523+ POWER_SUPPLY_PROP_ONLINE,524524+ POWER_SUPPLY_PROP_HEALTH,525525+ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,526526+ POWER_SUPPLY_PROP_CURRENT_NOW,527527+ POWER_SUPPLY_PROP_VOLTAGE_NOW,528528+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,529529+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,530530+ POWER_SUPPLY_PROP_VOLTAGE_MIN,531531+ POWER_SUPPLY_PROP_USB_TYPE,532532+};533533+534534+static int bq257xx_property_is_writeable(struct power_supply *psy,535535+ enum power_supply_property prop)536536+{537537+ switch (prop) {538538+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:539539+ case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:540540+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:541541+ return true;542542+ default:543543+ return false;544544+ }545545+}546546+547547+/**548548+ * bq257xx_external_power_changed() - Handler for external power change549549+ * @psy: Power supply data550550+ *551551+ * When the external power into the charger is changed, check the USB552552+ * type so that it can be reported. Additionally, update the max input553553+ * current and max charging current to the value reported if it is a554554+ * USB PD charger, otherwise use the default value. Note that each time555555+ * a charger is removed the max charge current register is erased, so556556+ * it must be set again each time the input changes or the device will557557+ * not charge.558558+ */559559+static void bq257xx_external_power_changed(struct power_supply *psy)560560+{561561+ struct bq257xx_chg *pdata = power_supply_get_drvdata(psy);562562+ union power_supply_propval val;563563+ int ret;564564+ int imax = pdata->iindpm_max;565565+566566+ pdata->chip->bq257xx_get_state(pdata);567567+568568+ pdata->supplied = power_supply_am_i_supplied(pdata->charger);569569+ if (pdata->supplied < 0)570570+ return;571571+572572+ if (pdata->supplied == 0)573573+ goto out;574574+575575+ ret = power_supply_get_property_from_supplier(psy,576576+ POWER_SUPPLY_PROP_USB_TYPE,577577+ &val);578578+ if (ret)579579+ return;580580+581581+ pdata->usb_type = val.intval;582582+583583+ if ((pdata->usb_type == POWER_SUPPLY_USB_TYPE_PD) ||584584+ (pdata->usb_type == POWER_SUPPLY_USB_TYPE_PD_DRP) ||585585+ (pdata->usb_type == POWER_SUPPLY_USB_TYPE_PD_PPS)) {586586+ ret = power_supply_get_property_from_supplier(psy,587587+ POWER_SUPPLY_PROP_CURRENT_MAX,588588+ &val);589589+ if (ret)590590+ return;591591+592592+ if (val.intval)593593+ imax = val.intval;594594+ }595595+596596+ if (pdata->supplied) {597597+ pdata->chip->bq257xx_set_ichg(pdata, pdata->ichg_max);598598+ pdata->chip->bq257xx_set_iindpm(pdata, imax);599599+ pdata->chip->bq257xx_set_vbatreg(pdata, pdata->vbat_max);600600+ }601601+602602+out:603603+ power_supply_changed(psy);604604+}605605+606606+static irqreturn_t bq257xx_irq_handler_thread(int irq, void *private)607607+{608608+ struct bq257xx_chg *pdata = private;609609+610610+ bq257xx_external_power_changed(pdata->charger);611611+ return IRQ_HANDLED;612612+}613613+614614+static const struct power_supply_desc bq257xx_power_supply_desc = {615615+ .name = "bq257xx-charger",616616+ .type = POWER_SUPPLY_TYPE_USB,617617+ .usb_types = BIT(POWER_SUPPLY_USB_TYPE_C) |618618+ BIT(POWER_SUPPLY_USB_TYPE_PD) |619619+ BIT(POWER_SUPPLY_USB_TYPE_PD_DRP) |620620+ BIT(POWER_SUPPLY_USB_TYPE_PD_PPS) |621621+ BIT(POWER_SUPPLY_USB_TYPE_UNKNOWN),622622+ .properties = bq257xx_power_supply_props,623623+ .num_properties = ARRAY_SIZE(bq257xx_power_supply_props),624624+ .get_property = bq257xx_get_charger_property,625625+ .set_property = bq257xx_set_charger_property,626626+ .property_is_writeable = bq257xx_property_is_writeable,627627+ .external_power_changed = bq257xx_external_power_changed,628628+};629629+630630+static const struct bq257xx_chip_info bq25703_chip_info = {631631+ .bq257xx_hw_init = &bq25703_hw_init,632632+ .bq257xx_hw_shutdown = &bq25703_hw_shutdown,633633+ .bq257xx_get_state = &bq25703_get_state,634634+ .bq257xx_set_ichg = &bq25703_set_ichg_cur,635635+ .bq257xx_set_vbatreg = &bq25703_set_chrg_volt,636636+ .bq257xx_set_iindpm = &bq25703_set_iindpm,637637+};638638+639639+/**640640+ * bq257xx_parse_dt() - Parse the device tree for required properties641641+ * @pdata: driver platform data642642+ * @psy_cfg: power supply config data643643+ * @dev: device struct644644+ *645645+ * Read the device tree to identify the minimum system voltage, the646646+ * maximum charge current, the maximum charge voltage, and the maximum647647+ * input current.648648+ *649649+ * Return: Returns 0 on success or error code on error.650650+ */651651+static int bq257xx_parse_dt(struct bq257xx_chg *pdata,652652+ struct power_supply_config *psy_cfg, struct device *dev)653653+{654654+ struct power_supply_battery_info *bat_info;655655+ int ret;656656+657657+ ret = power_supply_get_battery_info(pdata->charger,658658+ &bat_info);659659+ if (ret)660660+ return dev_err_probe(dev, ret,661661+ "Unable to get battery info\n");662662+663663+ if ((bat_info->voltage_min_design_uv <= 0) ||664664+ (bat_info->constant_charge_voltage_max_uv <= 0) ||665665+ (bat_info->constant_charge_current_max_ua <= 0))666666+ return dev_err_probe(dev, -EINVAL,667667+ "Required bat info missing or invalid\n");668668+669669+ pdata->vsys_min = bat_info->voltage_min_design_uv;670670+ pdata->vbat_max = bat_info->constant_charge_voltage_max_uv;671671+ pdata->ichg_max = bat_info->constant_charge_current_max_ua;672672+673673+ power_supply_put_battery_info(pdata->charger, bat_info);674674+675675+ ret = device_property_read_u32(dev,676676+ "input-current-limit-microamp",677677+ &pdata->iindpm_max);678678+ if (ret)679679+ pdata->iindpm_max = BQ25703_IINDPM_DEFAULT_UA;680680+681681+ return 0;682682+}683683+684684+static int bq257xx_charger_probe(struct platform_device *pdev)685685+{686686+ struct device *dev = &pdev->dev;687687+ struct bq257xx_device *bq = dev_get_drvdata(pdev->dev.parent);688688+ struct bq257xx_chg *pdata;689689+ struct power_supply_config psy_cfg = { };690690+ int ret;691691+692692+ device_set_of_node_from_dev(dev, pdev->dev.parent);693693+694694+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);695695+ if (!pdata)696696+ return -ENOMEM;697697+698698+ pdata->bq = bq;699699+ pdata->chip = &bq25703_chip_info;700700+701701+ platform_set_drvdata(pdev, pdata);702702+703703+ psy_cfg.drv_data = pdata;704704+ psy_cfg.fwnode = dev_fwnode(dev);705705+706706+ pdata->charger = devm_power_supply_register(dev,707707+ &bq257xx_power_supply_desc,708708+ &psy_cfg);709709+ if (IS_ERR(pdata->charger))710710+ return dev_err_probe(dev, PTR_ERR(pdata->charger),711711+ "Power supply register charger failed\n");712712+713713+ ret = bq257xx_parse_dt(pdata, &psy_cfg, dev);714714+ if (ret)715715+ return ret;716716+717717+ ret = pdata->chip->bq257xx_hw_init(pdata);718718+ if (ret)719719+ return dev_err_probe(dev, ret, "Cannot initialize the charger\n");720720+721721+ platform_set_drvdata(pdev, pdata);722722+723723+ if (bq->client->irq) {724724+ ret = devm_request_threaded_irq(dev, bq->client->irq, NULL,725725+ bq257xx_irq_handler_thread,726726+ IRQF_TRIGGER_RISING |727727+ IRQF_TRIGGER_FALLING |728728+ IRQF_ONESHOT,729729+ dev_name(&bq->client->dev), pdata);730730+ if (ret < 0)731731+ dev_err_probe(dev, ret, "Charger get irq failed\n");732732+ }733733+734734+ return ret;735735+}736736+737737+static void bq257xx_charger_shutdown(struct platform_device *pdev)738738+{739739+ struct bq257xx_chg *pdata = platform_get_drvdata(pdev);740740+741741+ pdata->chip->bq257xx_hw_shutdown(pdata);742742+}743743+744744+static struct platform_driver bq257xx_chg_driver = {745745+ .driver = {746746+ .name = "bq257xx-charger",747747+ },748748+ .probe = bq257xx_charger_probe,749749+ .shutdown = bq257xx_charger_shutdown,750750+};751751+module_platform_driver(bq257xx_chg_driver);752752+753753+MODULE_DESCRIPTION("bq257xx charger driver");754754+MODULE_AUTHOR("Chris Morgan <macromorgan@hotmail.com>");755755+MODULE_LICENSE("GPL");
+10
drivers/pwm/Kconfig
···432432 To compile this driver as a module, choose M here: the module433433 will be called pwm-lpss-platform.434434435435+config PWM_MAX7360436436+ tristate "MAX7360 PWMs"437437+ depends on MFD_MAX7360438438+ help439439+ PWM driver for Maxim Integrated MAX7360 multifunction device, with440440+ support for up to 8 PWM outputs.441441+442442+ To compile this driver as a module, choose M here: the module443443+ will be called pwm-max7360.444444+435445config PWM_MC33XS2410436446 tristate "MC33XS2410 PWM support"437447 depends on OF
···11+// SPDX-License-Identifier: GPL-2.0-only22+/*33+ * Copyright 2025 Bootlin44+ *55+ * Author: Kamel BOUHARA <kamel.bouhara@bootlin.com>66+ * Author: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>77+ *88+ * PWM functionality of the MAX7360 multi-function device.99+ * https://www.analog.com/media/en/technical-documentation/data-sheets/MAX7360.pdf1010+ *1111+ * Limitations:1212+ * - Only supports normal polarity.1313+ * - The period is fixed to 2 ms.1414+ * - Only the duty cycle can be changed, new values are applied at the beginning1515+ * of the next cycle.1616+ * - When disabled, the output is put in Hi-Z immediately.1717+ */1818+#include <linux/bits.h>1919+#include <linux/dev_printk.h>2020+#include <linux/err.h>2121+#include <linux/math64.h>2222+#include <linux/mfd/max7360.h>2323+#include <linux/minmax.h>2424+#include <linux/mod_devicetable.h>2525+#include <linux/module.h>2626+#include <linux/platform_device.h>2727+#include <linux/pwm.h>2828+#include <linux/regmap.h>2929+#include <linux/time.h>3030+#include <linux/types.h>3131+3232+#define MAX7360_NUM_PWMS 83333+#define MAX7360_PWM_MAX 2553434+#define MAX7360_PWM_STEPS 2563535+#define MAX7360_PWM_PERIOD_NS (2 * NSEC_PER_MSEC)3636+3737+struct max7360_pwm_waveform {3838+ u8 duty_steps;3939+ bool enabled;4040+};4141+4242+static int max7360_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)4343+{4444+ struct regmap *regmap = pwmchip_get_drvdata(chip);4545+4646+ /*4747+ * Make sure we use the individual PWM configuration register and not4848+ * the global one.4949+ * We never need to use the global one, so there is no need to revert5050+ * that in the .free() callback.5151+ */5252+ return regmap_write_bits(regmap, MAX7360_REG_PWMCFG(pwm->hwpwm),5353+ MAX7360_PORT_CFG_COMMON_PWM, 0);5454+}5555+5656+static int max7360_pwm_round_waveform_tohw(struct pwm_chip *chip,5757+ struct pwm_device *pwm,5858+ const struct pwm_waveform *wf,5959+ void *_wfhw)6060+{6161+ struct max7360_pwm_waveform *wfhw = _wfhw;6262+ u64 duty_steps;6363+6464+ /*6565+ * Ignore user provided values for period_length_ns and duty_offset_ns:6666+ * we only support fixed period of MAX7360_PWM_PERIOD_NS and offset of 0.6767+ * Values from 0 to 254 as duty_steps will provide duty cycles of 0/2566868+ * to 254/256, while value 255 will provide a duty cycle of 100%.6969+ */7070+ if (wf->duty_length_ns >= MAX7360_PWM_PERIOD_NS) {7171+ duty_steps = MAX7360_PWM_MAX;7272+ } else {7373+ duty_steps = (u32)wf->duty_length_ns * MAX7360_PWM_STEPS / MAX7360_PWM_PERIOD_NS;7474+ if (duty_steps == MAX7360_PWM_MAX)7575+ duty_steps = MAX7360_PWM_MAX - 1;7676+ }7777+7878+ wfhw->duty_steps = min(MAX7360_PWM_MAX, duty_steps);7979+ wfhw->enabled = !!wf->period_length_ns;8080+8181+ if (wf->period_length_ns && wf->period_length_ns < MAX7360_PWM_PERIOD_NS)8282+ return 1;8383+ else8484+ return 0;8585+}8686+8787+static int max7360_pwm_round_waveform_fromhw(struct pwm_chip *chip, struct pwm_device *pwm,8888+ const void *_wfhw, struct pwm_waveform *wf)8989+{9090+ const struct max7360_pwm_waveform *wfhw = _wfhw;9191+9292+ wf->period_length_ns = wfhw->enabled ? MAX7360_PWM_PERIOD_NS : 0;9393+ wf->duty_offset_ns = 0;9494+9595+ if (wfhw->enabled) {9696+ if (wfhw->duty_steps == MAX7360_PWM_MAX)9797+ wf->duty_length_ns = MAX7360_PWM_PERIOD_NS;9898+ else9999+ wf->duty_length_ns = DIV_ROUND_UP(wfhw->duty_steps * MAX7360_PWM_PERIOD_NS,100100+ MAX7360_PWM_STEPS);101101+ } else {102102+ wf->duty_length_ns = 0;103103+ }104104+105105+ return 0;106106+}107107+108108+static int max7360_pwm_write_waveform(struct pwm_chip *chip,109109+ struct pwm_device *pwm,110110+ const void *_wfhw)111111+{112112+ struct regmap *regmap = pwmchip_get_drvdata(chip);113113+ const struct max7360_pwm_waveform *wfhw = _wfhw;114114+ unsigned int val;115115+ int ret;116116+117117+ if (wfhw->enabled) {118118+ ret = regmap_write(regmap, MAX7360_REG_PWM(pwm->hwpwm), wfhw->duty_steps);119119+ if (ret)120120+ return ret;121121+ }122122+123123+ val = wfhw->enabled ? BIT(pwm->hwpwm) : 0;124124+ return regmap_write_bits(regmap, MAX7360_REG_GPIOCTRL, BIT(pwm->hwpwm), val);125125+}126126+127127+static int max7360_pwm_read_waveform(struct pwm_chip *chip,128128+ struct pwm_device *pwm,129129+ void *_wfhw)130130+{131131+ struct regmap *regmap = pwmchip_get_drvdata(chip);132132+ struct max7360_pwm_waveform *wfhw = _wfhw;133133+ unsigned int val;134134+ int ret;135135+136136+ ret = regmap_read(regmap, MAX7360_REG_GPIOCTRL, &val);137137+ if (ret)138138+ return ret;139139+140140+ if (val & BIT(pwm->hwpwm)) {141141+ wfhw->enabled = true;142142+ ret = regmap_read(regmap, MAX7360_REG_PWM(pwm->hwpwm), &val);143143+ if (ret)144144+ return ret;145145+146146+ wfhw->duty_steps = val;147147+ } else {148148+ wfhw->enabled = false;149149+ wfhw->duty_steps = 0;150150+ }151151+152152+ return 0;153153+}154154+155155+static const struct pwm_ops max7360_pwm_ops = {156156+ .request = max7360_pwm_request,157157+ .round_waveform_tohw = max7360_pwm_round_waveform_tohw,158158+ .round_waveform_fromhw = max7360_pwm_round_waveform_fromhw,159159+ .read_waveform = max7360_pwm_read_waveform,160160+ .write_waveform = max7360_pwm_write_waveform,161161+};162162+163163+static int max7360_pwm_probe(struct platform_device *pdev)164164+{165165+ struct device *dev = &pdev->dev;166166+ struct pwm_chip *chip;167167+ struct regmap *regmap;168168+ int ret;169169+170170+ regmap = dev_get_regmap(dev->parent, NULL);171171+ if (!regmap)172172+ return dev_err_probe(dev, -ENODEV, "Could not get parent regmap\n");173173+174174+ /*175175+ * This MFD sub-device does not have any associated device tree node:176176+ * properties are stored in the device node of the parent (MFD) device177177+ * and this same node is used in phandles of client devices.178178+ * Reuse this device tree node here, as otherwise the PWM subsystem179179+ * would be confused by this topology.180180+ */181181+ device_set_of_node_from_dev(dev, dev->parent);182182+183183+ chip = devm_pwmchip_alloc(dev, MAX7360_NUM_PWMS, 0);184184+ if (IS_ERR(chip))185185+ return PTR_ERR(chip);186186+ chip->ops = &max7360_pwm_ops;187187+188188+ pwmchip_set_drvdata(chip, regmap);189189+190190+ ret = devm_pwmchip_add(dev, chip);191191+ if (ret)192192+ return dev_err_probe(dev, ret, "Failed to add PWM chip\n");193193+194194+ return 0;195195+}196196+197197+static struct platform_driver max7360_pwm_driver = {198198+ .driver = {199199+ .name = "max7360-pwm",200200+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,201201+ },202202+ .probe = max7360_pwm_probe,203203+};204204+module_platform_driver(max7360_pwm_driver);205205+206206+MODULE_DESCRIPTION("MAX7360 PWM driver");207207+MODULE_AUTHOR("Kamel BOUHARA <kamel.bouhara@bootlin.com>");208208+MODULE_AUTHOR("Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>");209209+MODULE_LICENSE("GPL");
+8
drivers/regulator/Kconfig
···297297 This driver can also be built as a module. If so, the module298298 will be called bd96801-regulator.299299300300+config REGULATOR_BQ257XX301301+ tristate "TI BQ257XX regulator family"302302+ depends on MFD_BQ257XX303303+ depends on GPIOLIB || COMPILE_TEST304304+ help305305+ Say Y to enable support for the boost regulator function of306306+ the BQ257XX family of charger circuits.307307+300308config REGULATOR_CPCAP301309 tristate "Motorola CPCAP regulator"302310 depends on MFD_CPCAP
···416416 This driver can also be built as a module, if so, the module will be417417 called "rtc-nct3018y".418418419419+config RTC_DRV_NCT6694420420+ tristate "Nuvoton NCT6694 RTC support"421421+ depends on MFD_NCT6694422422+ help423423+ If you say yes to this option, support will be included for Nuvoton424424+ NCT6694, a USB device to RTC.425425+426426+ This driver can also be built as a module. If so, the module will427427+ be called rtc-nct6694.428428+419429config RTC_DRV_RK808420430 tristate "Rockchip RK805/RK808/RK809/RK817/RK818 RTC"421431 depends on MFD_RK8XX
···760760 MAX77620 chips. To compile this driver as a module,761761 choose M here: the module will be called max77620_wdt.762762763763+config NCT6694_WATCHDOG764764+ tristate "Nuvoton NCT6694 watchdog support"765765+ depends on MFD_NCT6694766766+ select WATCHDOG_CORE767767+ help768768+ Say Y here to support Nuvoton NCT6694 watchdog timer769769+ functionality.770770+771771+ This driver can also be built as a module. If so, the module772772+ will be called nct6694_wdt.773773+763774config IMX2_WDT764775 tristate "IMX2+ Watchdog"765776 depends on ARCH_MXC || ARCH_LAYERSCAPE || COMPILE_TEST
···66struct device;77struct fwnode_handle;88struct gpio_regmap;99+struct gpio_chip;910struct irq_domain;1011struct regmap;1112···4140 * @drvdata: (Optional) Pointer to driver specific data which is4241 * not used by gpio-remap but is provided "as is" to the4342 * driver callback(s).4343+ * @init_valid_mask: (Optional) Routine to initialize @valid_mask, to be used4444+ * if not all GPIOs are valid.4545+ * @regmap_irq_chip: (Optional) Pointer on an regmap_irq_chip structure. If4646+ * set, a regmap-irq device will be created and the IRQ4747+ * domain will be set accordingly.4848+ * @regmap_irq_line (Optional) The IRQ the device uses to signal interrupts.4949+ * @regmap_irq_flags (Optional) The IRQF_ flags to use for the interrupt.4450 *4551 * The ->reg_mask_xlate translates a given base address and GPIO offset to4652 * register and mask pair. The base address is one of the given register···8678 int ngpio_per_reg;8779 struct irq_domain *irq_domain;88808181+#ifdef CONFIG_REGMAP_IRQ8282+ struct regmap_irq_chip *regmap_irq_chip;8383+ int regmap_irq_line;8484+ unsigned long regmap_irq_flags;8585+#endif8686+8987 int (*reg_mask_xlate)(struct gpio_regmap *gpio, unsigned int base,9088 unsigned int offset, unsigned int *reg,9189 unsigned int *mask);9090+9191+ int (*init_valid_mask)(struct gpio_chip *gc,9292+ unsigned long *valid_mask,9393+ unsigned int ngpios);92949395 void *drvdata;9496};