···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
···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");
+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}
+12
drivers/input/keyboard/Kconfig
···404404 To compile this driver as a module, choose M here: the405405 module will be called max7359_keypad.406406407407+config KEYBOARD_MAX7360408408+ tristate "Maxim MAX7360 Key Switch Controller"409409+ select INPUT_MATRIXKMAP410410+ depends on I2C411411+ depends on MFD_MAX7360412412+ help413413+ If you say yes here you get support for the keypad controller on the414414+ Maxim MAX7360 I/O Expander.415415+416416+ To compile this driver as a module, choose M here: the module will be417417+ called max7360_keypad.418418+407419config KEYBOARD_MPR121408420 tristate "Freescale MPR121 Touchkey"409421 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");
+10
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
···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/device/devres.h>1010+#include <linux/dev_printk.h>1111+#include <linux/init.h>1212+#include <linux/input.h>1313+#include <linux/interrupt.h>1414+#include <linux/mfd/max7360.h>1515+#include <linux/property.h>1616+#include <linux/platform_device.h>1717+#include <linux/pm_wakeirq.h>1818+#include <linux/regmap.h>1919+#include <linux/types.h>2020+2121+#define MAX7360_ROTARY_DEFAULT_STEPS 242222+2323+struct max7360_rotary {2424+ struct input_dev *input;2525+ struct regmap *regmap;2626+ unsigned int debounce_ms;2727+2828+ unsigned int pos;2929+3030+ u32 steps;3131+ u32 axis;3232+ bool relative_axis;3333+ bool rollover;3434+};3535+3636+static void max7360_rotary_report_event(struct max7360_rotary *max7360_rotary, int steps)3737+{3838+ if (max7360_rotary->relative_axis) {3939+ input_report_rel(max7360_rotary->input, max7360_rotary->axis, steps);4040+ } else {4141+ int pos = max7360_rotary->pos;4242+ int maxval = max7360_rotary->steps;4343+4444+ /*4545+ * Add steps to the position.4646+ * Make sure added steps are always in ]-maxval; maxval[4747+ * interval, so (pos + maxval) is always >= 0.4848+ * Then set back pos to the [0; maxval[ interval.4949+ */5050+ pos += steps % maxval;5151+ if (max7360_rotary->rollover)5252+ pos = (pos + maxval) % maxval;5353+ else5454+ pos = clamp(pos, 0, maxval - 1);5555+5656+ max7360_rotary->pos = pos;5757+ input_report_abs(max7360_rotary->input, max7360_rotary->axis, max7360_rotary->pos);5858+ }5959+6060+ input_sync(max7360_rotary->input);6161+}6262+6363+static irqreturn_t max7360_rotary_irq(int irq, void *data)6464+{6565+ struct max7360_rotary *max7360_rotary = data;6666+ struct device *dev = max7360_rotary->input->dev.parent;6767+ unsigned int val;6868+ int error;6969+7070+ error = regmap_read(max7360_rotary->regmap, MAX7360_REG_RTR_CNT, &val);7171+ if (error < 0) {7272+ dev_err(dev, "Failed to read rotary counter\n");7373+ return IRQ_NONE;7474+ }7575+7676+ if (val == 0)7777+ return IRQ_NONE;7878+7979+ max7360_rotary_report_event(max7360_rotary, sign_extend32(val, 7));8080+8181+ return IRQ_HANDLED;8282+}8383+8484+static int max7360_rotary_hw_init(struct max7360_rotary *max7360_rotary)8585+{8686+ struct device *dev = max7360_rotary->input->dev.parent;8787+ int val;8888+ int error;8989+9090+ val = FIELD_PREP(MAX7360_ROT_DEBOUNCE, max7360_rotary->debounce_ms) |9191+ FIELD_PREP(MAX7360_ROT_INTCNT, 1) | MAX7360_ROT_INTCNT_DLY;9292+ error = regmap_write(max7360_rotary->regmap, MAX7360_REG_RTRCFG, val);9393+ if (error)9494+ dev_err(dev, "Failed to set max7360 rotary encoder configuration\n");9595+9696+ return error;9797+}9898+9999+static int max7360_rotary_probe(struct platform_device *pdev)100100+{101101+ struct max7360_rotary *max7360_rotary;102102+ struct device *dev = &pdev->dev;103103+ struct input_dev *input;104104+ struct regmap *regmap;105105+ int irq;106106+ int error;107107+108108+ regmap = dev_get_regmap(dev->parent, NULL);109109+ if (!regmap)110110+ return dev_err_probe(dev, -ENODEV, "Could not get parent regmap\n");111111+112112+ irq = fwnode_irq_get_byname(dev_fwnode(dev->parent), "inti");113113+ if (irq < 0)114114+ return dev_err_probe(dev, irq, "Failed to get IRQ\n");115115+116116+ max7360_rotary = devm_kzalloc(dev, sizeof(*max7360_rotary), GFP_KERNEL);117117+ if (!max7360_rotary)118118+ return -ENOMEM;119119+120120+ max7360_rotary->regmap = regmap;121121+122122+ device_property_read_u32(dev->parent, "linux,axis", &max7360_rotary->axis);123123+ max7360_rotary->rollover = device_property_read_bool(dev->parent,124124+ "rotary-encoder,rollover");125125+ max7360_rotary->relative_axis =126126+ device_property_read_bool(dev->parent, "rotary-encoder,relative-axis");127127+128128+ error = device_property_read_u32(dev->parent, "rotary-encoder,steps",129129+ &max7360_rotary->steps);130130+ if (error)131131+ max7360_rotary->steps = MAX7360_ROTARY_DEFAULT_STEPS;132132+133133+ device_property_read_u32(dev->parent, "rotary-debounce-delay-ms",134134+ &max7360_rotary->debounce_ms);135135+ if (max7360_rotary->debounce_ms > MAX7360_ROT_DEBOUNCE_MAX)136136+ return dev_err_probe(dev, -EINVAL, "Invalid debounce timing: %u\n",137137+ max7360_rotary->debounce_ms);138138+139139+ input = devm_input_allocate_device(dev);140140+ if (!input)141141+ return -ENOMEM;142142+143143+ max7360_rotary->input = input;144144+145145+ input->id.bustype = BUS_I2C;146146+ input->name = pdev->name;147147+148148+ if (max7360_rotary->relative_axis)149149+ input_set_capability(input, EV_REL, max7360_rotary->axis);150150+ else151151+ input_set_abs_params(input, max7360_rotary->axis, 0, max7360_rotary->steps, 0, 1);152152+153153+ error = devm_request_threaded_irq(dev, irq, NULL, max7360_rotary_irq,154154+ IRQF_ONESHOT | IRQF_SHARED,155155+ "max7360-rotary", max7360_rotary);156156+ if (error)157157+ return dev_err_probe(dev, error, "Failed to register interrupt\n");158158+159159+ error = input_register_device(input);160160+ if (error)161161+ return dev_err_probe(dev, error, "Could not register input device\n");162162+163163+ error = max7360_rotary_hw_init(max7360_rotary);164164+ if (error)165165+ return dev_err_probe(dev, error, "Failed to initialize max7360 rotary\n");166166+167167+ device_init_wakeup(dev, true);168168+ error = dev_pm_set_wake_irq(dev, irq);169169+ if (error)170170+ dev_warn(dev, "Failed to set up wakeup irq: %d\n", error);171171+172172+ return 0;173173+}174174+175175+static void max7360_rotary_remove(struct platform_device *pdev)176176+{177177+ dev_pm_clear_wake_irq(&pdev->dev);178178+ device_init_wakeup(&pdev->dev, false);179179+}180180+181181+static struct platform_driver max7360_rotary_driver = {182182+ .driver = {183183+ .name = "max7360-rotary",184184+ },185185+ .probe = max7360_rotary_probe,186186+ .remove = max7360_rotary_remove,187187+};188188+module_platform_driver(max7360_rotary_driver);189189+190190+MODULE_DESCRIPTION("MAX7360 Rotary driver");191191+MODULE_AUTHOR("Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>");192192+MODULE_LICENSE("GPL");
+14
drivers/mfd/Kconfig
···24812481 To compile this driver as a module, choose M here: the module will be24822482 called upboard-fpga.2483248324842484+config MFD_MAX736024852485+ tristate "Maxim MAX7360 I2C IO Expander"24862486+ depends on I2C24872487+ select MFD_CORE24882488+ select REGMAP_I2C24892489+ select REGMAP_IRQ24902490+ help24912491+ Say yes here to add support for Maxim MAX7360 device, embedding24922492+ keypad, rotary encoder, PWM and GPIO features.24932493+24942494+ This driver provides common support for accessing the device;24952495+ additional drivers must be enabled in order to use the functionality24962496+ of the device.24972497+24842498endmenu24852499endif
···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");
+11
drivers/pinctrl/Kconfig
···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");
+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");
+18
include/linux/gpio/regmap.h
···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};