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

Merge tag 'ib-mfd-gpio-input-pinctrl-pwm-v6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd into gpio/for-next

Pull changes from the immutable branch between MFD, GPIO, Input, Pinctrl
and PWM trees containing the GPIO driver for max7360.

+1869 -2
+83
Documentation/devicetree/bindings/gpio/maxim,max7360-gpio.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/gpio/maxim,max7360-gpio.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: Maxim MAX7360 GPIO controller 8 + 9 + maintainers: 10 + - Kamel Bouhara <kamel.bouhara@bootlin.com> 11 + - Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> 12 + 13 + description: | 14 + Maxim MAX7360 GPIO controller, in MAX7360 chipset 15 + https://www.analog.com/en/products/max7360.html 16 + 17 + The device provides two series of GPIOs, referred here as GPIOs and GPOs. 18 + 19 + PORT0 to PORT7 pins can be used as GPIOs, with support for interrupts and 20 + constant-current mode. These pins will also be used by the rotary encoder and 21 + PWM functionalities. 22 + 23 + COL2 to COL7 pins can be used as GPOs, there is no input capability. COL pins 24 + will be partitioned, with the first pins being affected to the keypad 25 + functionality and the last ones as GPOs. 26 + 27 + properties: 28 + compatible: 29 + enum: 30 + - maxim,max7360-gpio 31 + - maxim,max7360-gpo 32 + 33 + gpio-controller: true 34 + 35 + "#gpio-cells": 36 + const: 2 37 + 38 + interrupt-controller: true 39 + 40 + "#interrupt-cells": 41 + const: 2 42 + 43 + maxim,constant-current-disable: 44 + $ref: /schemas/types.yaml#/definitions/uint32 45 + description: 46 + Bit field, each bit disables constant-current output of the associated 47 + GPIO, starting from the least significant bit for the first GPIO. 48 + maximum: 0xff 49 + 50 + required: 51 + - compatible 52 + - gpio-controller 53 + 54 + allOf: 55 + - if: 56 + properties: 57 + compatible: 58 + contains: 59 + enum: 60 + - maxim,max7360-gpio 61 + ngpios: false 62 + then: 63 + required: 64 + - interrupt-controller 65 + else: 66 + properties: 67 + interrupt-controller: false 68 + maxim,constant-current-disable: false 69 + 70 + additionalProperties: false 71 + 72 + examples: 73 + - | 74 + gpio { 75 + compatible = "maxim,max7360-gpio"; 76 + 77 + gpio-controller; 78 + #gpio-cells = <2>; 79 + maxim,constant-current-disable = <0x06>; 80 + 81 + interrupt-controller; 82 + #interrupt-cells = <2>; 83 + };
+191
Documentation/devicetree/bindings/mfd/maxim,max7360.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/mfd/maxim,max7360.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: Maxim MAX7360 Keypad, Rotary encoder, PWM and GPIO controller 8 + 9 + maintainers: 10 + - Kamel Bouhara <kamel.bouhara@bootlin.com> 11 + - Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> 12 + 13 + description: | 14 + Maxim MAX7360 device, with following functions: 15 + - keypad controller 16 + - rotary controller 17 + - GPIO and GPO controller 18 + - PWM controller 19 + 20 + https://www.analog.com/en/products/max7360.html 21 + 22 + allOf: 23 + - $ref: /schemas/input/matrix-keymap.yaml# 24 + - $ref: /schemas/input/input.yaml# 25 + 26 + properties: 27 + compatible: 28 + enum: 29 + - maxim,max7360 30 + 31 + reg: 32 + maxItems: 1 33 + 34 + interrupts: 35 + maxItems: 2 36 + 37 + interrupt-names: 38 + items: 39 + - const: inti 40 + - const: intk 41 + 42 + keypad-debounce-delay-ms: 43 + description: Keypad debounce delay in ms 44 + minimum: 9 45 + maximum: 40 46 + default: 9 47 + 48 + rotary-debounce-delay-ms: 49 + description: Rotary encoder debounce delay in ms 50 + minimum: 0 51 + maximum: 15 52 + default: 0 53 + 54 + linux,axis: 55 + $ref: /schemas/input/rotary-encoder.yaml#/properties/linux,axis 56 + 57 + rotary-encoder,relative-axis: 58 + $ref: /schemas/types.yaml#/definitions/flag 59 + description: 60 + Register a relative axis rather than an absolute one. 61 + 62 + rotary-encoder,steps: 63 + $ref: /schemas/types.yaml#/definitions/uint32 64 + default: 24 65 + description: 66 + Number of steps in a full turnaround of the 67 + encoder. Only relevant for absolute axis. Defaults to 24 which is a 68 + typical value for such devices. 69 + 70 + rotary-encoder,rollover: 71 + $ref: /schemas/types.yaml#/definitions/flag 72 + description: 73 + Automatic rollover when the rotary value becomes 74 + greater than the specified steps or smaller than 0. For absolute axis only. 75 + 76 + "#pwm-cells": 77 + const: 3 78 + 79 + gpio: 80 + $ref: /schemas/gpio/maxim,max7360-gpio.yaml# 81 + description: 82 + PORT0 to PORT7 general purpose input/output pins configuration. 83 + 84 + gpo: 85 + $ref: /schemas/gpio/maxim,max7360-gpio.yaml# 86 + description: > 87 + COL2 to COL7 general purpose output pins configuration. Allows to use 88 + unused keypad columns as outputs. 89 + 90 + The MAX7360 has 8 column lines and 6 of them can be used as GPOs. GPIOs 91 + numbers used for this gpio-controller node do correspond to the column 92 + numbers: values 0 and 1 are never valid, values from 2 to 7 might be valid 93 + depending on the value of the keypad,num-column property. 94 + 95 + patternProperties: 96 + '-pins$': 97 + type: object 98 + description: 99 + Pinctrl node's client devices use subnodes for desired pin configuration. 100 + Client device subnodes use below standard properties. 101 + $ref: /schemas/pinctrl/pincfg-node.yaml 102 + 103 + properties: 104 + pins: 105 + description: 106 + List of gpio pins affected by the properties specified in this 107 + subnode. 108 + items: 109 + pattern: '^(PORT[0-7]|ROTARY)$' 110 + minItems: 1 111 + maxItems: 8 112 + 113 + function: 114 + description: 115 + Specify the alternative function to be configured for the specified 116 + pins. 117 + enum: [gpio, pwm, rotary] 118 + 119 + additionalProperties: false 120 + 121 + required: 122 + - compatible 123 + - reg 124 + - interrupts 125 + - interrupt-names 126 + - linux,keymap 127 + - linux,axis 128 + - "#pwm-cells" 129 + - gpio 130 + - gpo 131 + 132 + unevaluatedProperties: false 133 + 134 + examples: 135 + - | 136 + #include <dt-bindings/input/input.h> 137 + #include <dt-bindings/interrupt-controller/arm-gic.h> 138 + 139 + i2c { 140 + #address-cells = <1>; 141 + #size-cells = <0>; 142 + 143 + io-expander@38 { 144 + compatible = "maxim,max7360"; 145 + reg = <0x38>; 146 + 147 + interrupt-parent = <&gpio1>; 148 + interrupts = <23 IRQ_TYPE_LEVEL_LOW>, 149 + <24 IRQ_TYPE_LEVEL_LOW>; 150 + interrupt-names = "inti", "intk"; 151 + 152 + keypad,num-rows = <8>; 153 + keypad,num-columns = <4>; 154 + linux,keymap = < 155 + MATRIX_KEY(0x00, 0x00, KEY_F5) 156 + MATRIX_KEY(0x01, 0x00, KEY_F4) 157 + MATRIX_KEY(0x02, 0x01, KEY_F6) 158 + >; 159 + keypad-debounce-delay-ms = <10>; 160 + autorepeat; 161 + 162 + rotary-debounce-delay-ms = <2>; 163 + linux,axis = <0>; /* REL_X */ 164 + rotary-encoder,relative-axis; 165 + 166 + #pwm-cells = <3>; 167 + 168 + max7360_gpio: gpio { 169 + compatible = "maxim,max7360-gpio"; 170 + 171 + gpio-controller; 172 + #gpio-cells = <2>; 173 + maxim,constant-current-disable = <0x06>; 174 + 175 + interrupt-controller; 176 + #interrupt-cells = <0x2>; 177 + }; 178 + 179 + max7360_gpo: gpo { 180 + compatible = "maxim,max7360-gpo"; 181 + 182 + gpio-controller; 183 + #gpio-cells = <2>; 184 + }; 185 + 186 + backlight_pins: backlight-pins { 187 + pins = "PORT2"; 188 + function = "pwm"; 189 + }; 190 + }; 191 + };
+13
MAINTAINERS
··· 15023 15023 S: Maintained 15024 15024 F: drivers/iio/temperature/max30208.c 15025 15025 15026 + MAXIM MAX7360 KEYPAD LED MFD DRIVER 15027 + M: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> 15028 + S: Maintained 15029 + F: Documentation/devicetree/bindings/gpio/maxim,max7360-gpio.yaml 15030 + F: Documentation/devicetree/bindings/mfd/maxim,max7360.yaml 15031 + F: drivers/gpio/gpio-max7360.c 15032 + F: drivers/input/keyboard/max7360-keypad.c 15033 + F: drivers/input/misc/max7360-rotary.c 15034 + F: drivers/mfd/max7360.c 15035 + F: drivers/pinctrl/pinctrl-max7360.c 15036 + F: drivers/pwm/pwm-max7360.c 15037 + F: include/linux/mfd/max7360.h 15038 + 15026 15039 MAXIM MAX77650 PMIC MFD DRIVER 15027 15040 M: Bartosz Golaszewski <brgl@bgdev.pl> 15028 15041 L: linux-kernel@vger.kernel.org
+12
drivers/gpio/Kconfig
··· 1497 1497 help 1498 1498 Support for GPIOs on Cirrus Logic Madera class codecs. 1499 1499 1500 + config GPIO_MAX7360 1501 + tristate "MAX7360 GPIO support" 1502 + depends on MFD_MAX7360 1503 + select GPIO_REGMAP 1504 + select REGMAP_IRQ 1505 + help 1506 + Allows to use MAX7360 I/O Expander PWM lines as GPIO and keypad COL 1507 + lines as GPO. 1508 + 1509 + This driver can also be built as a module. If so, the module will be 1510 + called gpio-max7360. 1511 + 1500 1512 config GPIO_MAX77620 1501 1513 tristate "GPIO support for PMIC MAX77620 and MAX20024" 1502 1514 depends on MFD_MAX77620
+1
drivers/gpio/Makefile
··· 106 106 obj-$(CONFIG_GPIO_MAX7301) += gpio-max7301.o 107 107 obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o 108 108 obj-$(CONFIG_GPIO_MAX732X) += gpio-max732x.o 109 + obj-$(CONFIG_GPIO_MAX7360) += gpio-max7360.o 109 110 obj-$(CONFIG_GPIO_MAX77620) += gpio-max77620.o 110 111 obj-$(CONFIG_GPIO_MAX77650) += gpio-max77650.o 111 112 obj-$(CONFIG_GPIO_MAX77759) += gpio-max77759.o
+257
drivers/gpio/gpio-max7360.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright 2025 Bootlin 4 + * 5 + * Author: Kamel BOUHARA <kamel.bouhara@bootlin.com> 6 + * Author: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> 7 + */ 8 + 9 + #include <linux/bitfield.h> 10 + #include <linux/bitmap.h> 11 + #include <linux/err.h> 12 + #include <linux/gpio/driver.h> 13 + #include <linux/gpio/regmap.h> 14 + #include <linux/init.h> 15 + #include <linux/interrupt.h> 16 + #include <linux/mfd/max7360.h> 17 + #include <linux/minmax.h> 18 + #include <linux/mod_devicetable.h> 19 + #include <linux/module.h> 20 + #include <linux/platform_device.h> 21 + #include <linux/property.h> 22 + #include <linux/regmap.h> 23 + 24 + #define MAX7360_GPIO_PORT 1 25 + #define MAX7360_GPIO_COL 2 26 + 27 + struct max7360_gpio_plat_data { 28 + unsigned int function; 29 + }; 30 + 31 + static struct max7360_gpio_plat_data max7360_gpio_port_plat = { .function = MAX7360_GPIO_PORT }; 32 + static struct max7360_gpio_plat_data max7360_gpio_col_plat = { .function = MAX7360_GPIO_COL }; 33 + 34 + static int max7360_get_available_gpos(struct device *dev, unsigned int *available_gpios) 35 + { 36 + u32 columns; 37 + int ret; 38 + 39 + ret = device_property_read_u32(dev->parent, "keypad,num-columns", &columns); 40 + if (ret) { 41 + dev_err(dev, "Failed to read columns count\n"); 42 + return ret; 43 + } 44 + 45 + *available_gpios = min(MAX7360_MAX_GPO, MAX7360_MAX_KEY_COLS - columns); 46 + 47 + return 0; 48 + } 49 + 50 + static int max7360_gpo_init_valid_mask(struct gpio_chip *gc, 51 + unsigned long *valid_mask, 52 + unsigned int ngpios) 53 + { 54 + unsigned int available_gpios; 55 + int ret; 56 + 57 + ret = max7360_get_available_gpos(gc->parent, &available_gpios); 58 + if (ret) 59 + return ret; 60 + 61 + bitmap_clear(valid_mask, 0, MAX7360_MAX_KEY_COLS - available_gpios); 62 + 63 + return 0; 64 + } 65 + 66 + static int max7360_set_gpos_count(struct device *dev, struct regmap *regmap) 67 + { 68 + /* 69 + * MAX7360 COL0 to COL7 pins can be used either as keypad columns, 70 + * general purpose output or a mix of both. 71 + * By default, all pins are used as keypad, here we update this 72 + * configuration to allow to use some of them as GPIOs. 73 + */ 74 + unsigned int available_gpios; 75 + unsigned int val; 76 + int ret; 77 + 78 + ret = max7360_get_available_gpos(dev, &available_gpios); 79 + if (ret) 80 + return ret; 81 + 82 + /* 83 + * Configure which GPIOs will be used for keypad. 84 + * MAX7360_REG_DEBOUNCE contains configuration both for keypad debounce 85 + * timings and gpos/keypad columns repartition. Only the later is 86 + * modified here. 87 + */ 88 + val = FIELD_PREP(MAX7360_PORTS, available_gpios); 89 + ret = regmap_write_bits(regmap, MAX7360_REG_DEBOUNCE, MAX7360_PORTS, val); 90 + if (ret) 91 + dev_err(dev, "Failed to write max7360 columns/gpos configuration"); 92 + 93 + return ret; 94 + } 95 + 96 + static int max7360_gpio_reg_mask_xlate(struct gpio_regmap *gpio, 97 + unsigned int base, unsigned int offset, 98 + unsigned int *reg, unsigned int *mask) 99 + { 100 + if (base == MAX7360_REG_PWMBASE) { 101 + /* 102 + * GPIO output is using PWM duty cycle registers: one register 103 + * per line, with value being either 0 or 255. 104 + */ 105 + *reg = base + offset; 106 + *mask = GENMASK(7, 0); 107 + } else { 108 + *reg = base; 109 + *mask = BIT(offset); 110 + } 111 + 112 + return 0; 113 + } 114 + 115 + static const struct regmap_irq max7360_regmap_irqs[MAX7360_MAX_GPIO] = { 116 + REGMAP_IRQ_REG(0, 0, BIT(0)), 117 + REGMAP_IRQ_REG(1, 0, BIT(1)), 118 + REGMAP_IRQ_REG(2, 0, BIT(2)), 119 + REGMAP_IRQ_REG(3, 0, BIT(3)), 120 + REGMAP_IRQ_REG(4, 0, BIT(4)), 121 + REGMAP_IRQ_REG(5, 0, BIT(5)), 122 + REGMAP_IRQ_REG(6, 0, BIT(6)), 123 + REGMAP_IRQ_REG(7, 0, BIT(7)), 124 + }; 125 + 126 + static int max7360_handle_mask_sync(const int index, 127 + const unsigned int mask_buf_def, 128 + const unsigned int mask_buf, 129 + void *const irq_drv_data) 130 + { 131 + struct regmap *regmap = irq_drv_data; 132 + int ret; 133 + 134 + for (unsigned int i = 0; i < MAX7360_MAX_GPIO; i++) { 135 + ret = regmap_assign_bits(regmap, MAX7360_REG_PWMCFG(i), 136 + MAX7360_PORT_CFG_INTERRUPT_MASK, mask_buf & BIT(i)); 137 + if (ret) 138 + return ret; 139 + } 140 + 141 + return 0; 142 + } 143 + 144 + static int max7360_gpio_probe(struct platform_device *pdev) 145 + { 146 + const struct max7360_gpio_plat_data *plat_data; 147 + struct gpio_regmap_config gpio_config = { }; 148 + struct regmap_irq_chip *irq_chip; 149 + struct device *dev = &pdev->dev; 150 + struct regmap *regmap; 151 + unsigned int outconf; 152 + int ret; 153 + 154 + regmap = dev_get_regmap(dev->parent, NULL); 155 + if (!regmap) 156 + return dev_err_probe(dev, -ENODEV, "could not get parent regmap\n"); 157 + 158 + plat_data = device_get_match_data(dev); 159 + if (plat_data->function == MAX7360_GPIO_PORT) { 160 + if (device_property_read_bool(dev, "interrupt-controller")) { 161 + /* 162 + * Port GPIOs with interrupt-controller property: add IRQ 163 + * controller. 164 + */ 165 + gpio_config.regmap_irq_flags = IRQF_ONESHOT | IRQF_SHARED; 166 + gpio_config.regmap_irq_line = 167 + fwnode_irq_get_byname(dev_fwnode(dev->parent), "inti"); 168 + if (gpio_config.regmap_irq_line < 0) 169 + return dev_err_probe(dev, gpio_config.regmap_irq_line, 170 + "Failed to get IRQ\n"); 171 + 172 + /* Create custom IRQ configuration. */ 173 + irq_chip = devm_kzalloc(dev, sizeof(*irq_chip), GFP_KERNEL); 174 + gpio_config.regmap_irq_chip = irq_chip; 175 + if (!irq_chip) 176 + return -ENOMEM; 177 + 178 + irq_chip->name = dev_name(dev); 179 + irq_chip->status_base = MAX7360_REG_GPIOIN; 180 + irq_chip->status_is_level = true; 181 + irq_chip->num_regs = 1; 182 + irq_chip->num_irqs = MAX7360_MAX_GPIO; 183 + irq_chip->irqs = max7360_regmap_irqs; 184 + irq_chip->handle_mask_sync = max7360_handle_mask_sync; 185 + irq_chip->irq_drv_data = regmap; 186 + 187 + for (unsigned int i = 0; i < MAX7360_MAX_GPIO; i++) { 188 + ret = regmap_write_bits(regmap, MAX7360_REG_PWMCFG(i), 189 + MAX7360_PORT_CFG_INTERRUPT_EDGES, 190 + MAX7360_PORT_CFG_INTERRUPT_EDGES); 191 + if (ret) 192 + return dev_err_probe(dev, ret, 193 + "Failed to enable interrupts\n"); 194 + } 195 + } 196 + 197 + /* 198 + * Port GPIOs: set output mode configuration (constant-current or not). 199 + * This property is optional. 200 + */ 201 + ret = device_property_read_u32(dev, "maxim,constant-current-disable", &outconf); 202 + if (!ret) { 203 + ret = regmap_write(regmap, MAX7360_REG_GPIOOUTM, outconf); 204 + if (ret) 205 + return dev_err_probe(dev, ret, 206 + "Failed to set constant-current configuration\n"); 207 + } 208 + } 209 + 210 + /* Add gpio device. */ 211 + gpio_config.parent = dev; 212 + gpio_config.regmap = regmap; 213 + if (plat_data->function == MAX7360_GPIO_PORT) { 214 + gpio_config.ngpio = MAX7360_MAX_GPIO; 215 + gpio_config.reg_dat_base = GPIO_REGMAP_ADDR(MAX7360_REG_GPIOIN); 216 + gpio_config.reg_set_base = GPIO_REGMAP_ADDR(MAX7360_REG_PWMBASE); 217 + gpio_config.reg_dir_out_base = GPIO_REGMAP_ADDR(MAX7360_REG_GPIOCTRL); 218 + gpio_config.ngpio_per_reg = MAX7360_MAX_GPIO; 219 + gpio_config.reg_mask_xlate = max7360_gpio_reg_mask_xlate; 220 + } else { 221 + ret = max7360_set_gpos_count(dev, regmap); 222 + if (ret) 223 + return dev_err_probe(dev, ret, "Failed to set GPOS pin count\n"); 224 + 225 + gpio_config.reg_set_base = GPIO_REGMAP_ADDR(MAX7360_REG_PORTS); 226 + gpio_config.ngpio = MAX7360_MAX_KEY_COLS; 227 + gpio_config.init_valid_mask = max7360_gpo_init_valid_mask; 228 + } 229 + 230 + return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config)); 231 + } 232 + 233 + static const struct of_device_id max7360_gpio_of_match[] = { 234 + { 235 + .compatible = "maxim,max7360-gpo", 236 + .data = &max7360_gpio_col_plat 237 + }, { 238 + .compatible = "maxim,max7360-gpio", 239 + .data = &max7360_gpio_port_plat 240 + }, { 241 + } 242 + }; 243 + MODULE_DEVICE_TABLE(of, max7360_gpio_of_match); 244 + 245 + static struct platform_driver max7360_gpio_driver = { 246 + .driver = { 247 + .name = "max7360-gpio", 248 + .of_match_table = max7360_gpio_of_match, 249 + }, 250 + .probe = max7360_gpio_probe, 251 + }; 252 + module_platform_driver(max7360_gpio_driver); 253 + 254 + MODULE_DESCRIPTION("MAX7360 GPIO driver"); 255 + MODULE_AUTHOR("Kamel BOUHARA <kamel.bouhara@bootlin.com>"); 256 + MODULE_AUTHOR("Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>"); 257 + MODULE_LICENSE("GPL");
+28 -2
drivers/gpio/gpio-regmap.c
··· 32 32 unsigned int reg_dir_in_base; 33 33 unsigned int reg_dir_out_base; 34 34 35 + #ifdef CONFIG_REGMAP_IRQ 36 + int regmap_irq_line; 37 + struct regmap_irq_chip_data *irq_chip_data; 38 + #endif 39 + 35 40 int (*reg_mask_xlate)(struct gpio_regmap *gpio, unsigned int base, 36 41 unsigned int offset, unsigned int *reg, 37 42 unsigned int *mask); ··· 220 215 */ 221 216 struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config) 222 217 { 218 + struct irq_domain *irq_domain; 223 219 struct gpio_regmap *gpio; 224 220 struct gpio_chip *chip; 225 221 int ret; ··· 261 255 chip->names = config->names; 262 256 chip->label = config->label ?: dev_name(config->parent); 263 257 chip->can_sleep = regmap_might_sleep(config->regmap); 258 + chip->init_valid_mask = config->init_valid_mask; 264 259 265 260 chip->request = gpiochip_generic_request; 266 261 chip->free = gpiochip_generic_free; ··· 302 295 if (ret < 0) 303 296 goto err_free_gpio; 304 297 305 - if (config->irq_domain) { 306 - ret = gpiochip_irqchip_add_domain(chip, config->irq_domain); 298 + #ifdef CONFIG_REGMAP_IRQ 299 + if (config->regmap_irq_chip) { 300 + gpio->regmap_irq_line = config->regmap_irq_line; 301 + ret = regmap_add_irq_chip_fwnode(dev_fwnode(config->parent), config->regmap, 302 + config->regmap_irq_line, config->regmap_irq_flags, 303 + 0, config->regmap_irq_chip, &gpio->irq_chip_data); 304 + if (ret) 305 + goto err_free_gpio; 306 + 307 + irq_domain = regmap_irq_get_domain(gpio->irq_chip_data); 308 + } else 309 + #endif 310 + irq_domain = config->irq_domain; 311 + 312 + if (irq_domain) { 313 + ret = gpiochip_irqchip_add_domain(chip, irq_domain); 307 314 if (ret) 308 315 goto err_remove_gpiochip; 309 316 } ··· 338 317 */ 339 318 void gpio_regmap_unregister(struct gpio_regmap *gpio) 340 319 { 320 + #ifdef CONFIG_REGMAP_IRQ 321 + if (gpio->irq_chip_data) 322 + regmap_del_irq_chip(gpio->regmap_irq_line, gpio->irq_chip_data); 323 + #endif 324 + 341 325 gpiochip_remove(&gpio->gpio_chip); 342 326 kfree(gpio); 343 327 }
+12
drivers/input/keyboard/Kconfig
··· 422 422 To compile this driver as a module, choose M here: the 423 423 module will be called max7359_keypad. 424 424 425 + config KEYBOARD_MAX7360 426 + tristate "Maxim MAX7360 Key Switch Controller" 427 + select INPUT_MATRIXKMAP 428 + depends on I2C 429 + depends on MFD_MAX7360 430 + help 431 + If you say yes here you get support for the keypad controller on the 432 + Maxim MAX7360 I/O Expander. 433 + 434 + To compile this driver as a module, choose M here: the module will be 435 + called max7360_keypad. 436 + 425 437 config KEYBOARD_MPR121 426 438 tristate "Freescale MPR121 Touchkey" 427 439 depends on I2C
+1
drivers/input/keyboard/Makefile
··· 42 42 obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o 43 43 obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o 44 44 obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o 45 + obj-$(CONFIG_KEYBOARD_MAX7360) += max7360-keypad.o 45 46 obj-$(CONFIG_KEYBOARD_MPR121) += mpr121_touchkey.o 46 47 obj-$(CONFIG_KEYBOARD_MT6779) += mt6779-keypad.o 47 48 obj-$(CONFIG_KEYBOARD_MTK_PMIC) += mtk-pmic-keys.o
+308
drivers/input/keyboard/max7360-keypad.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright 2025 Bootlin 4 + * 5 + * Author: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> 6 + */ 7 + 8 + #include <linux/bitfield.h> 9 + #include <linux/bitops.h> 10 + #include <linux/dev_printk.h> 11 + #include <linux/device/devres.h> 12 + #include <linux/err.h> 13 + #include <linux/init.h> 14 + #include <linux/input.h> 15 + #include <linux/input/matrix_keypad.h> 16 + #include <linux/interrupt.h> 17 + #include <linux/mfd/max7360.h> 18 + #include <linux/mod_devicetable.h> 19 + #include <linux/minmax.h> 20 + #include <linux/module.h> 21 + #include <linux/property.h> 22 + #include <linux/platform_device.h> 23 + #include <linux/pm_wakeirq.h> 24 + #include <linux/regmap.h> 25 + 26 + struct max7360_keypad { 27 + struct input_dev *input; 28 + unsigned int rows; 29 + unsigned int cols; 30 + unsigned int debounce_ms; 31 + int irq; 32 + struct regmap *regmap; 33 + unsigned short keycodes[MAX7360_MAX_KEY_ROWS * MAX7360_MAX_KEY_COLS]; 34 + }; 35 + 36 + static irqreturn_t max7360_keypad_irq(int irq, void *data) 37 + { 38 + struct max7360_keypad *max7360_keypad = data; 39 + struct device *dev = max7360_keypad->input->dev.parent; 40 + unsigned int val; 41 + unsigned int row, col; 42 + unsigned int release; 43 + unsigned int code; 44 + int error; 45 + 46 + error = regmap_read(max7360_keypad->regmap, MAX7360_REG_KEYFIFO, &val); 47 + if (error) { 48 + dev_err(dev, "Failed to read MAX7360 FIFO"); 49 + return IRQ_NONE; 50 + } 51 + 52 + /* FIFO overflow: ignore it and get next event. */ 53 + if (val == MAX7360_FIFO_OVERFLOW) { 54 + dev_warn(dev, "max7360 FIFO overflow"); 55 + error = regmap_read_poll_timeout(max7360_keypad->regmap, MAX7360_REG_KEYFIFO, 56 + val, val != MAX7360_FIFO_OVERFLOW, 0, 1000); 57 + if (error) { 58 + dev_err(dev, "Failed to empty MAX7360 FIFO"); 59 + return IRQ_NONE; 60 + } 61 + } 62 + 63 + if (val == MAX7360_FIFO_EMPTY) { 64 + dev_dbg(dev, "Got a spurious interrupt"); 65 + 66 + return IRQ_NONE; 67 + } 68 + 69 + row = FIELD_GET(MAX7360_FIFO_ROW, val); 70 + col = FIELD_GET(MAX7360_FIFO_COL, val); 71 + release = val & MAX7360_FIFO_RELEASE; 72 + 73 + code = MATRIX_SCAN_CODE(row, col, get_count_order(max7360_keypad->cols)); 74 + 75 + dev_dbg(dev, "key[%d:%d] %s\n", row, col, release ? "release" : "press"); 76 + 77 + input_event(max7360_keypad->input, EV_MSC, MSC_SCAN, code); 78 + input_report_key(max7360_keypad->input, max7360_keypad->keycodes[code], !release); 79 + input_sync(max7360_keypad->input); 80 + 81 + return IRQ_HANDLED; 82 + } 83 + 84 + static int max7360_keypad_open(struct input_dev *pdev) 85 + { 86 + struct max7360_keypad *max7360_keypad = input_get_drvdata(pdev); 87 + struct device *dev = max7360_keypad->input->dev.parent; 88 + int error; 89 + 90 + /* Somebody is using the device: get out of sleep. */ 91 + error = regmap_write_bits(max7360_keypad->regmap, MAX7360_REG_CONFIG, 92 + MAX7360_CFG_SLEEP, MAX7360_CFG_SLEEP); 93 + if (error) 94 + dev_err(dev, "Failed to write max7360 configuration: %d\n", error); 95 + 96 + return error; 97 + } 98 + 99 + static void max7360_keypad_close(struct input_dev *pdev) 100 + { 101 + struct max7360_keypad *max7360_keypad = input_get_drvdata(pdev); 102 + struct device *dev = max7360_keypad->input->dev.parent; 103 + int error; 104 + 105 + /* Nobody is using the device anymore: go to sleep. */ 106 + error = regmap_write_bits(max7360_keypad->regmap, MAX7360_REG_CONFIG, MAX7360_CFG_SLEEP, 0); 107 + if (error) 108 + dev_err(dev, "Failed to write max7360 configuration: %d\n", error); 109 + } 110 + 111 + static int max7360_keypad_hw_init(struct max7360_keypad *max7360_keypad) 112 + { 113 + struct device *dev = max7360_keypad->input->dev.parent; 114 + unsigned int val; 115 + int error; 116 + 117 + val = max7360_keypad->debounce_ms - MAX7360_DEBOUNCE_MIN; 118 + error = regmap_write_bits(max7360_keypad->regmap, MAX7360_REG_DEBOUNCE, 119 + MAX7360_DEBOUNCE, 120 + FIELD_PREP(MAX7360_DEBOUNCE, val)); 121 + if (error) 122 + return dev_err_probe(dev, error, 123 + "Failed to write max7360 debounce configuration\n"); 124 + 125 + error = regmap_write_bits(max7360_keypad->regmap, MAX7360_REG_INTERRUPT, 126 + MAX7360_INTERRUPT_TIME_MASK, 127 + FIELD_PREP(MAX7360_INTERRUPT_TIME_MASK, 1)); 128 + if (error) 129 + return dev_err_probe(dev, error, 130 + "Failed to write max7360 keypad interrupt configuration\n"); 131 + 132 + return 0; 133 + } 134 + 135 + static int max7360_keypad_build_keymap(struct max7360_keypad *max7360_keypad) 136 + { 137 + struct input_dev *input_dev = max7360_keypad->input; 138 + struct device *dev = input_dev->dev.parent->parent; 139 + struct matrix_keymap_data keymap_data; 140 + const char *propname = "linux,keymap"; 141 + unsigned int max_keys; 142 + int error; 143 + int size; 144 + 145 + size = device_property_count_u32(dev, propname); 146 + if (size <= 0) { 147 + dev_err(dev, "missing or malformed property %s: %d\n", propname, size); 148 + return size < 0 ? size : -EINVAL; 149 + } 150 + 151 + max_keys = max7360_keypad->cols * max7360_keypad->rows; 152 + if (size > max_keys) { 153 + dev_err(dev, "%s size overflow (%d vs max %u)\n", propname, size, max_keys); 154 + return -EINVAL; 155 + } 156 + 157 + u32 *keys __free(kfree) = kmalloc_array(size, sizeof(*keys), GFP_KERNEL); 158 + if (!keys) 159 + return -ENOMEM; 160 + 161 + error = device_property_read_u32_array(dev, propname, keys, size); 162 + if (error) { 163 + dev_err(dev, "failed to read %s property: %d\n", propname, error); 164 + return error; 165 + } 166 + 167 + keymap_data.keymap = keys; 168 + keymap_data.keymap_size = size; 169 + error = matrix_keypad_build_keymap(&keymap_data, NULL, 170 + max7360_keypad->rows, max7360_keypad->cols, 171 + max7360_keypad->keycodes, max7360_keypad->input); 172 + if (error) 173 + return error; 174 + 175 + return 0; 176 + } 177 + 178 + static int max7360_keypad_parse_fw(struct device *dev, 179 + struct max7360_keypad *max7360_keypad, 180 + bool *autorepeat) 181 + { 182 + int error; 183 + 184 + error = matrix_keypad_parse_properties(dev->parent, &max7360_keypad->rows, 185 + &max7360_keypad->cols); 186 + if (error) 187 + return error; 188 + 189 + if (!max7360_keypad->rows || !max7360_keypad->cols || 190 + max7360_keypad->rows > MAX7360_MAX_KEY_ROWS || 191 + max7360_keypad->cols > MAX7360_MAX_KEY_COLS) { 192 + dev_err(dev, "Invalid number of columns or rows (%ux%u)\n", 193 + max7360_keypad->cols, max7360_keypad->rows); 194 + return -EINVAL; 195 + } 196 + 197 + *autorepeat = device_property_read_bool(dev->parent, "autorepeat"); 198 + 199 + max7360_keypad->debounce_ms = MAX7360_DEBOUNCE_MIN; 200 + error = device_property_read_u32(dev->parent, "keypad-debounce-delay-ms", 201 + &max7360_keypad->debounce_ms); 202 + if (error == -EINVAL) { 203 + dev_info(dev, "Using default keypad-debounce-delay-ms: %u\n", 204 + max7360_keypad->debounce_ms); 205 + } else if (error < 0) { 206 + dev_err(dev, "Failed to read keypad-debounce-delay-ms property\n"); 207 + return error; 208 + } 209 + 210 + if (!in_range(max7360_keypad->debounce_ms, MAX7360_DEBOUNCE_MIN, 211 + MAX7360_DEBOUNCE_MAX - MAX7360_DEBOUNCE_MIN + 1)) { 212 + dev_err(dev, "Invalid keypad-debounce-delay-ms: %u, should be between %u and %u.\n", 213 + max7360_keypad->debounce_ms, MAX7360_DEBOUNCE_MIN, MAX7360_DEBOUNCE_MAX); 214 + return -EINVAL; 215 + } 216 + 217 + return 0; 218 + } 219 + 220 + static int max7360_keypad_probe(struct platform_device *pdev) 221 + { 222 + struct max7360_keypad *max7360_keypad; 223 + struct device *dev = &pdev->dev; 224 + struct input_dev *input; 225 + struct regmap *regmap; 226 + bool autorepeat; 227 + int error; 228 + int irq; 229 + 230 + regmap = dev_get_regmap(dev->parent, NULL); 231 + if (!regmap) 232 + return dev_err_probe(dev, -ENODEV, "Could not get parent regmap\n"); 233 + 234 + irq = fwnode_irq_get_byname(dev_fwnode(dev->parent), "intk"); 235 + if (irq < 0) 236 + return dev_err_probe(dev, irq, "Failed to get IRQ\n"); 237 + 238 + max7360_keypad = devm_kzalloc(dev, sizeof(*max7360_keypad), GFP_KERNEL); 239 + if (!max7360_keypad) 240 + return -ENOMEM; 241 + 242 + max7360_keypad->regmap = regmap; 243 + 244 + error = max7360_keypad_parse_fw(dev, max7360_keypad, &autorepeat); 245 + if (error) 246 + return error; 247 + 248 + input = devm_input_allocate_device(dev); 249 + if (!input) 250 + return -ENOMEM; 251 + 252 + max7360_keypad->input = input; 253 + 254 + input->id.bustype = BUS_I2C; 255 + input->name = pdev->name; 256 + input->open = max7360_keypad_open; 257 + input->close = max7360_keypad_close; 258 + 259 + error = max7360_keypad_build_keymap(max7360_keypad); 260 + if (error) 261 + return dev_err_probe(dev, error, "Failed to build keymap\n"); 262 + 263 + input_set_capability(input, EV_MSC, MSC_SCAN); 264 + if (autorepeat) 265 + __set_bit(EV_REP, input->evbit); 266 + 267 + input_set_drvdata(input, max7360_keypad); 268 + 269 + error = devm_request_threaded_irq(dev, irq, NULL, max7360_keypad_irq, 270 + IRQF_ONESHOT, 271 + "max7360-keypad", max7360_keypad); 272 + if (error) 273 + return dev_err_probe(dev, error, "Failed to register interrupt\n"); 274 + 275 + error = input_register_device(input); 276 + if (error) 277 + return dev_err_probe(dev, error, "Could not register input device\n"); 278 + 279 + error = max7360_keypad_hw_init(max7360_keypad); 280 + if (error) 281 + return dev_err_probe(dev, error, "Failed to initialize max7360 keypad\n"); 282 + 283 + device_init_wakeup(dev, true); 284 + error = dev_pm_set_wake_irq(dev, irq); 285 + if (error) 286 + dev_warn(dev, "Failed to set up wakeup irq: %d\n", error); 287 + 288 + return 0; 289 + } 290 + 291 + static void max7360_keypad_remove(struct platform_device *pdev) 292 + { 293 + dev_pm_clear_wake_irq(&pdev->dev); 294 + device_init_wakeup(&pdev->dev, false); 295 + } 296 + 297 + static struct platform_driver max7360_keypad_driver = { 298 + .driver = { 299 + .name = "max7360-keypad", 300 + }, 301 + .probe = max7360_keypad_probe, 302 + .remove = max7360_keypad_remove, 303 + }; 304 + module_platform_driver(max7360_keypad_driver); 305 + 306 + MODULE_DESCRIPTION("MAX7360 Keypad driver"); 307 + MODULE_AUTHOR("Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>"); 308 + MODULE_LICENSE("GPL");
+10
drivers/input/misc/Kconfig
··· 230 230 tristate "M68k Beeper support" 231 231 depends on M68K 232 232 233 + config INPUT_MAX7360_ROTARY 234 + tristate "Maxim MAX7360 Rotary Encoder" 235 + depends on MFD_MAX7360 236 + help 237 + If you say yes here you get support for the rotary encoder on the 238 + Maxim MAX7360 I/O Expander. 239 + 240 + To compile this driver as a module, choose M here: the module will be 241 + called max7360_rotary. 242 + 233 243 config INPUT_MAX77650_ONKEY 234 244 tristate "Maxim MAX77650 ONKEY support" 235 245 depends on MFD_MAX77650
+1
drivers/input/misc/Makefile
··· 51 51 obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o 52 52 obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o 53 53 obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o 54 + obj-$(CONFIG_INPUT_MAX7360_ROTARY) += max7360-rotary.o 54 55 obj-$(CONFIG_INPUT_MAX77650_ONKEY) += max77650-onkey.o 55 56 obj-$(CONFIG_INPUT_MAX77693_HAPTIC) += max77693-haptic.o 56 57 obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o
+192
drivers/input/misc/max7360-rotary.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright 2025 Bootlin 4 + * 5 + * Author: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> 6 + */ 7 + 8 + #include <linux/bitfield.h> 9 + #include <linux/device/devres.h> 10 + #include <linux/dev_printk.h> 11 + #include <linux/init.h> 12 + #include <linux/input.h> 13 + #include <linux/interrupt.h> 14 + #include <linux/mfd/max7360.h> 15 + #include <linux/property.h> 16 + #include <linux/platform_device.h> 17 + #include <linux/pm_wakeirq.h> 18 + #include <linux/regmap.h> 19 + #include <linux/types.h> 20 + 21 + #define MAX7360_ROTARY_DEFAULT_STEPS 24 22 + 23 + struct max7360_rotary { 24 + struct input_dev *input; 25 + struct regmap *regmap; 26 + unsigned int debounce_ms; 27 + 28 + unsigned int pos; 29 + 30 + u32 steps; 31 + u32 axis; 32 + bool relative_axis; 33 + bool rollover; 34 + }; 35 + 36 + static void max7360_rotary_report_event(struct max7360_rotary *max7360_rotary, int steps) 37 + { 38 + if (max7360_rotary->relative_axis) { 39 + input_report_rel(max7360_rotary->input, max7360_rotary->axis, steps); 40 + } else { 41 + int pos = max7360_rotary->pos; 42 + int maxval = max7360_rotary->steps; 43 + 44 + /* 45 + * Add steps to the position. 46 + * Make sure added steps are always in ]-maxval; maxval[ 47 + * interval, so (pos + maxval) is always >= 0. 48 + * Then set back pos to the [0; maxval[ interval. 49 + */ 50 + pos += steps % maxval; 51 + if (max7360_rotary->rollover) 52 + pos = (pos + maxval) % maxval; 53 + else 54 + pos = clamp(pos, 0, maxval - 1); 55 + 56 + max7360_rotary->pos = pos; 57 + input_report_abs(max7360_rotary->input, max7360_rotary->axis, max7360_rotary->pos); 58 + } 59 + 60 + input_sync(max7360_rotary->input); 61 + } 62 + 63 + static irqreturn_t max7360_rotary_irq(int irq, void *data) 64 + { 65 + struct max7360_rotary *max7360_rotary = data; 66 + struct device *dev = max7360_rotary->input->dev.parent; 67 + unsigned int val; 68 + int error; 69 + 70 + error = regmap_read(max7360_rotary->regmap, MAX7360_REG_RTR_CNT, &val); 71 + if (error < 0) { 72 + dev_err(dev, "Failed to read rotary counter\n"); 73 + return IRQ_NONE; 74 + } 75 + 76 + if (val == 0) 77 + return IRQ_NONE; 78 + 79 + max7360_rotary_report_event(max7360_rotary, sign_extend32(val, 7)); 80 + 81 + return IRQ_HANDLED; 82 + } 83 + 84 + static int max7360_rotary_hw_init(struct max7360_rotary *max7360_rotary) 85 + { 86 + struct device *dev = max7360_rotary->input->dev.parent; 87 + int val; 88 + int error; 89 + 90 + val = FIELD_PREP(MAX7360_ROT_DEBOUNCE, max7360_rotary->debounce_ms) | 91 + FIELD_PREP(MAX7360_ROT_INTCNT, 1) | MAX7360_ROT_INTCNT_DLY; 92 + error = regmap_write(max7360_rotary->regmap, MAX7360_REG_RTRCFG, val); 93 + if (error) 94 + dev_err(dev, "Failed to set max7360 rotary encoder configuration\n"); 95 + 96 + return error; 97 + } 98 + 99 + static int max7360_rotary_probe(struct platform_device *pdev) 100 + { 101 + struct max7360_rotary *max7360_rotary; 102 + struct device *dev = &pdev->dev; 103 + struct input_dev *input; 104 + struct regmap *regmap; 105 + int irq; 106 + int error; 107 + 108 + regmap = dev_get_regmap(dev->parent, NULL); 109 + if (!regmap) 110 + return dev_err_probe(dev, -ENODEV, "Could not get parent regmap\n"); 111 + 112 + irq = fwnode_irq_get_byname(dev_fwnode(dev->parent), "inti"); 113 + if (irq < 0) 114 + return dev_err_probe(dev, irq, "Failed to get IRQ\n"); 115 + 116 + max7360_rotary = devm_kzalloc(dev, sizeof(*max7360_rotary), GFP_KERNEL); 117 + if (!max7360_rotary) 118 + return -ENOMEM; 119 + 120 + max7360_rotary->regmap = regmap; 121 + 122 + device_property_read_u32(dev->parent, "linux,axis", &max7360_rotary->axis); 123 + max7360_rotary->rollover = device_property_read_bool(dev->parent, 124 + "rotary-encoder,rollover"); 125 + max7360_rotary->relative_axis = 126 + device_property_read_bool(dev->parent, "rotary-encoder,relative-axis"); 127 + 128 + error = device_property_read_u32(dev->parent, "rotary-encoder,steps", 129 + &max7360_rotary->steps); 130 + if (error) 131 + max7360_rotary->steps = MAX7360_ROTARY_DEFAULT_STEPS; 132 + 133 + device_property_read_u32(dev->parent, "rotary-debounce-delay-ms", 134 + &max7360_rotary->debounce_ms); 135 + if (max7360_rotary->debounce_ms > MAX7360_ROT_DEBOUNCE_MAX) 136 + return dev_err_probe(dev, -EINVAL, "Invalid debounce timing: %u\n", 137 + max7360_rotary->debounce_ms); 138 + 139 + input = devm_input_allocate_device(dev); 140 + if (!input) 141 + return -ENOMEM; 142 + 143 + max7360_rotary->input = input; 144 + 145 + input->id.bustype = BUS_I2C; 146 + input->name = pdev->name; 147 + 148 + if (max7360_rotary->relative_axis) 149 + input_set_capability(input, EV_REL, max7360_rotary->axis); 150 + else 151 + input_set_abs_params(input, max7360_rotary->axis, 0, max7360_rotary->steps, 0, 1); 152 + 153 + error = devm_request_threaded_irq(dev, irq, NULL, max7360_rotary_irq, 154 + IRQF_ONESHOT | IRQF_SHARED, 155 + "max7360-rotary", max7360_rotary); 156 + if (error) 157 + return dev_err_probe(dev, error, "Failed to register interrupt\n"); 158 + 159 + error = input_register_device(input); 160 + if (error) 161 + return dev_err_probe(dev, error, "Could not register input device\n"); 162 + 163 + error = max7360_rotary_hw_init(max7360_rotary); 164 + if (error) 165 + return dev_err_probe(dev, error, "Failed to initialize max7360 rotary\n"); 166 + 167 + device_init_wakeup(dev, true); 168 + error = dev_pm_set_wake_irq(dev, irq); 169 + if (error) 170 + dev_warn(dev, "Failed to set up wakeup irq: %d\n", error); 171 + 172 + return 0; 173 + } 174 + 175 + static void max7360_rotary_remove(struct platform_device *pdev) 176 + { 177 + dev_pm_clear_wake_irq(&pdev->dev); 178 + device_init_wakeup(&pdev->dev, false); 179 + } 180 + 181 + static struct platform_driver max7360_rotary_driver = { 182 + .driver = { 183 + .name = "max7360-rotary", 184 + }, 185 + .probe = max7360_rotary_probe, 186 + .remove = max7360_rotary_remove, 187 + }; 188 + module_platform_driver(max7360_rotary_driver); 189 + 190 + MODULE_DESCRIPTION("MAX7360 Rotary driver"); 191 + MODULE_AUTHOR("Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>"); 192 + MODULE_LICENSE("GPL");
+14
drivers/mfd/Kconfig
··· 2496 2496 To compile this driver as a module, choose M here: the module will be 2497 2497 called upboard-fpga. 2498 2498 2499 + config MFD_MAX7360 2500 + tristate "Maxim MAX7360 I2C IO Expander" 2501 + depends on I2C 2502 + select MFD_CORE 2503 + select REGMAP_I2C 2504 + select REGMAP_IRQ 2505 + help 2506 + Say yes here to add support for Maxim MAX7360 device, embedding 2507 + keypad, rotary encoder, PWM and GPIO features. 2508 + 2509 + This driver provides common support for accessing the device; 2510 + additional drivers must be enabled in order to use the functionality 2511 + of the device. 2512 + 2499 2513 endmenu 2500 2514 endif
+1
drivers/mfd/Makefile
··· 165 165 obj-$(CONFIG_MFD_DA9150) += da9150-core.o 166 166 167 167 obj-$(CONFIG_MFD_MAX14577) += max14577.o 168 + obj-$(CONFIG_MFD_MAX7360) += max7360.o 168 169 obj-$(CONFIG_MFD_MAX77541) += max77541.o 169 170 obj-$(CONFIG_MFD_MAX77620) += max77620.o 170 171 obj-$(CONFIG_MFD_MAX77650) += max77650.o
+171
drivers/mfd/max7360.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Maxim MAX7360 Core Driver 4 + * 5 + * Copyright 2025 Bootlin 6 + * 7 + * Authors: 8 + * Kamel Bouhara <kamel.bouhara@bootlin.com> 9 + * Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> 10 + */ 11 + 12 + #include <linux/array_size.h> 13 + #include <linux/bits.h> 14 + #include <linux/delay.h> 15 + #include <linux/device/devres.h> 16 + #include <linux/dev_printk.h> 17 + #include <linux/err.h> 18 + #include <linux/i2c.h> 19 + #include <linux/interrupt.h> 20 + #include <linux/mfd/core.h> 21 + #include <linux/mfd/max7360.h> 22 + #include <linux/mod_devicetable.h> 23 + #include <linux/module.h> 24 + #include <linux/regmap.h> 25 + #include <linux/types.h> 26 + 27 + static const struct mfd_cell max7360_cells[] = { 28 + { .name = "max7360-pinctrl" }, 29 + { .name = "max7360-pwm" }, 30 + { .name = "max7360-keypad" }, 31 + { .name = "max7360-rotary" }, 32 + { 33 + .name = "max7360-gpo", 34 + .of_compatible = "maxim,max7360-gpo", 35 + }, 36 + { 37 + .name = "max7360-gpio", 38 + .of_compatible = "maxim,max7360-gpio", 39 + }, 40 + }; 41 + 42 + static const struct regmap_range max7360_volatile_ranges[] = { 43 + regmap_reg_range(MAX7360_REG_KEYFIFO, MAX7360_REG_KEYFIFO), 44 + regmap_reg_range(MAX7360_REG_I2C_TIMEOUT, MAX7360_REG_RTR_CNT), 45 + }; 46 + 47 + static const struct regmap_access_table max7360_volatile_table = { 48 + .yes_ranges = max7360_volatile_ranges, 49 + .n_yes_ranges = ARRAY_SIZE(max7360_volatile_ranges), 50 + }; 51 + 52 + static const struct regmap_config max7360_regmap_config = { 53 + .reg_bits = 8, 54 + .val_bits = 8, 55 + .max_register = MAX7360_REG_PWMCFG(MAX7360_PORT_PWM_COUNT - 1), 56 + .volatile_table = &max7360_volatile_table, 57 + .cache_type = REGCACHE_MAPLE, 58 + }; 59 + 60 + static int max7360_mask_irqs(struct regmap *regmap) 61 + { 62 + struct device *dev = regmap_get_device(regmap); 63 + unsigned int val; 64 + int ret; 65 + 66 + /* 67 + * GPIO/PWM interrupts are not masked on reset: as the MAX7360 "INTI" 68 + * interrupt line is shared between GPIOs and rotary encoder, this could 69 + * result in repeated spurious interrupts on the rotary encoder driver 70 + * if the GPIO driver is not loaded. Mask them now to avoid this 71 + * situation. 72 + */ 73 + for (unsigned int i = 0; i < MAX7360_PORT_PWM_COUNT; i++) { 74 + ret = regmap_write_bits(regmap, MAX7360_REG_PWMCFG(i), 75 + MAX7360_PORT_CFG_INTERRUPT_MASK, 76 + MAX7360_PORT_CFG_INTERRUPT_MASK); 77 + if (ret) 78 + return dev_err_probe(dev, ret, 79 + "Failed to write MAX7360 port configuration\n"); 80 + } 81 + 82 + /* Read GPIO in register, to ACK any pending IRQ. */ 83 + ret = regmap_read(regmap, MAX7360_REG_GPIOIN, &val); 84 + if (ret) 85 + return dev_err_probe(dev, ret, "Failed to read GPIO values\n"); 86 + 87 + return 0; 88 + } 89 + 90 + static int max7360_reset(struct regmap *regmap) 91 + { 92 + struct device *dev = regmap_get_device(regmap); 93 + int ret; 94 + 95 + ret = regmap_write(regmap, MAX7360_REG_GPIOCFG, MAX7360_GPIO_CFG_GPIO_RST); 96 + if (ret) { 97 + dev_err(dev, "Failed to reset GPIO configuration: %x\n", ret); 98 + return ret; 99 + } 100 + 101 + ret = regcache_drop_region(regmap, MAX7360_REG_GPIOCFG, MAX7360_REG_GPIO_LAST); 102 + if (ret) { 103 + dev_err(dev, "Failed to drop regmap cache: %x\n", ret); 104 + return ret; 105 + } 106 + 107 + ret = regmap_write(regmap, MAX7360_REG_SLEEP, 0); 108 + if (ret) { 109 + dev_err(dev, "Failed to reset autosleep configuration: %x\n", ret); 110 + return ret; 111 + } 112 + 113 + ret = regmap_write(regmap, MAX7360_REG_DEBOUNCE, 0); 114 + if (ret) 115 + dev_err(dev, "Failed to reset GPO port count: %x\n", ret); 116 + 117 + return ret; 118 + } 119 + 120 + static int max7360_probe(struct i2c_client *client) 121 + { 122 + struct device *dev = &client->dev; 123 + struct regmap *regmap; 124 + int ret; 125 + 126 + regmap = devm_regmap_init_i2c(client, &max7360_regmap_config); 127 + if (IS_ERR(regmap)) 128 + return dev_err_probe(dev, PTR_ERR(regmap), "Failed to initialise regmap\n"); 129 + 130 + ret = max7360_reset(regmap); 131 + if (ret) 132 + return dev_err_probe(dev, ret, "Failed to reset device\n"); 133 + 134 + /* Get the device out of shutdown mode. */ 135 + ret = regmap_write_bits(regmap, MAX7360_REG_GPIOCFG, 136 + MAX7360_GPIO_CFG_GPIO_EN, 137 + MAX7360_GPIO_CFG_GPIO_EN); 138 + if (ret) 139 + return dev_err_probe(dev, ret, "Failed to enable GPIO and PWM module\n"); 140 + 141 + ret = max7360_mask_irqs(regmap); 142 + if (ret) 143 + return dev_err_probe(dev, ret, "Could not mask interrupts\n"); 144 + 145 + ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, 146 + max7360_cells, ARRAY_SIZE(max7360_cells), 147 + NULL, 0, NULL); 148 + if (ret) 149 + return dev_err_probe(dev, ret, "Failed to register child devices\n"); 150 + 151 + return 0; 152 + } 153 + 154 + static const struct of_device_id max7360_dt_match[] = { 155 + { .compatible = "maxim,max7360" }, 156 + {} 157 + }; 158 + MODULE_DEVICE_TABLE(of, max7360_dt_match); 159 + 160 + static struct i2c_driver max7360_driver = { 161 + .driver = { 162 + .name = "max7360", 163 + .of_match_table = max7360_dt_match, 164 + }, 165 + .probe = max7360_probe, 166 + }; 167 + module_i2c_driver(max7360_driver); 168 + 169 + MODULE_DESCRIPTION("Maxim MAX7360 I2C IO Expander core driver"); 170 + MODULE_AUTHOR("Kamel Bouhara <kamel.bouhara@bootlin.com>"); 171 + MODULE_LICENSE("GPL");
+11
drivers/pinctrl/Kconfig
··· 358 358 help 359 359 Pinctrl driver for NXP LPC18xx/43xx System Control Unit (SCU). 360 360 361 + config PINCTRL_MAX7360 362 + tristate "MAX7360 Pincontrol support" 363 + depends on MFD_MAX7360 364 + select PINMUX 365 + select GENERIC_PINCONF 366 + help 367 + Say Y here to enable pin control support for Maxim MAX7360 keypad 368 + controller. 369 + This keypad controller has 8 GPIO pins that may work as GPIO, or PWM, 370 + or rotary encoder alternate modes. 371 + 361 372 config PINCTRL_MAX77620 362 373 tristate "MAX77620/MAX20024 Pincontrol support" 363 374 depends on MFD_MAX77620 && OF
+1
drivers/pinctrl/Makefile
··· 37 37 obj-$(CONFIG_PINCTRL_LOONGSON2) += pinctrl-loongson2.o 38 38 obj-$(CONFIG_PINCTRL_XWAY) += pinctrl-xway.o 39 39 obj-$(CONFIG_PINCTRL_LPC18XX) += pinctrl-lpc18xx.o 40 + obj-$(CONFIG_PINCTRL_MAX7360) += pinctrl-max7360.o 40 41 obj-$(CONFIG_PINCTRL_MAX77620) += pinctrl-max77620.o 41 42 obj-$(CONFIG_PINCTRL_MCP23S08_I2C) += pinctrl-mcp23s08_i2c.o 42 43 obj-$(CONFIG_PINCTRL_MCP23S08_SPI) += pinctrl-mcp23s08_spi.o
+215
drivers/pinctrl/pinctrl-max7360.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright 2025 Bootlin 4 + * 5 + * Author: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> 6 + */ 7 + 8 + #include <linux/array_size.h> 9 + #include <linux/dev_printk.h> 10 + #include <linux/device.h> 11 + #include <linux/device/devres.h> 12 + #include <linux/err.h> 13 + #include <linux/init.h> 14 + #include <linux/mfd/max7360.h> 15 + #include <linux/module.h> 16 + #include <linux/platform_device.h> 17 + #include <linux/regmap.h> 18 + #include <linux/stddef.h> 19 + 20 + #include <linux/pinctrl/pinctrl.h> 21 + #include <linux/pinctrl/pinconf-generic.h> 22 + #include <linux/pinctrl/pinmux.h> 23 + 24 + #include "core.h" 25 + #include "pinmux.h" 26 + 27 + struct max7360_pinctrl { 28 + struct pinctrl_dev *pctldev; 29 + struct pinctrl_desc pinctrl_desc; 30 + }; 31 + 32 + static const struct pinctrl_pin_desc max7360_pins[] = { 33 + PINCTRL_PIN(0, "PORT0"), 34 + PINCTRL_PIN(1, "PORT1"), 35 + PINCTRL_PIN(2, "PORT2"), 36 + PINCTRL_PIN(3, "PORT3"), 37 + PINCTRL_PIN(4, "PORT4"), 38 + PINCTRL_PIN(5, "PORT5"), 39 + PINCTRL_PIN(6, "PORT6"), 40 + PINCTRL_PIN(7, "PORT7"), 41 + }; 42 + 43 + static const unsigned int port0_pins[] = {0}; 44 + static const unsigned int port1_pins[] = {1}; 45 + static const unsigned int port2_pins[] = {2}; 46 + static const unsigned int port3_pins[] = {3}; 47 + static const unsigned int port4_pins[] = {4}; 48 + static const unsigned int port5_pins[] = {5}; 49 + static const unsigned int port6_pins[] = {6}; 50 + static const unsigned int port7_pins[] = {7}; 51 + static const unsigned int rotary_pins[] = {6, 7}; 52 + 53 + static const struct pingroup max7360_groups[] = { 54 + PINCTRL_PINGROUP("PORT0", port0_pins, ARRAY_SIZE(port0_pins)), 55 + PINCTRL_PINGROUP("PORT1", port1_pins, ARRAY_SIZE(port1_pins)), 56 + PINCTRL_PINGROUP("PORT2", port2_pins, ARRAY_SIZE(port2_pins)), 57 + PINCTRL_PINGROUP("PORT3", port3_pins, ARRAY_SIZE(port3_pins)), 58 + PINCTRL_PINGROUP("PORT4", port4_pins, ARRAY_SIZE(port4_pins)), 59 + PINCTRL_PINGROUP("PORT5", port5_pins, ARRAY_SIZE(port5_pins)), 60 + PINCTRL_PINGROUP("PORT6", port6_pins, ARRAY_SIZE(port6_pins)), 61 + PINCTRL_PINGROUP("PORT7", port7_pins, ARRAY_SIZE(port7_pins)), 62 + PINCTRL_PINGROUP("ROTARY", rotary_pins, ARRAY_SIZE(rotary_pins)), 63 + }; 64 + 65 + static int max7360_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) 66 + { 67 + return ARRAY_SIZE(max7360_groups); 68 + } 69 + 70 + static const char *max7360_pinctrl_get_group_name(struct pinctrl_dev *pctldev, 71 + unsigned int group) 72 + { 73 + return max7360_groups[group].name; 74 + } 75 + 76 + static int max7360_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, 77 + unsigned int group, 78 + const unsigned int **pins, 79 + unsigned int *num_pins) 80 + { 81 + *pins = max7360_groups[group].pins; 82 + *num_pins = max7360_groups[group].npins; 83 + return 0; 84 + } 85 + 86 + static const struct pinctrl_ops max7360_pinctrl_ops = { 87 + .get_groups_count = max7360_pinctrl_get_groups_count, 88 + .get_group_name = max7360_pinctrl_get_group_name, 89 + .get_group_pins = max7360_pinctrl_get_group_pins, 90 + #ifdef CONFIG_OF 91 + .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, 92 + .dt_free_map = pinconf_generic_dt_free_map, 93 + #endif 94 + }; 95 + 96 + static const char * const simple_groups[] = { 97 + "PORT0", "PORT1", "PORT2", "PORT3", 98 + "PORT4", "PORT5", "PORT6", "PORT7", 99 + }; 100 + 101 + static const char * const rotary_groups[] = { "ROTARY" }; 102 + 103 + #define MAX7360_PINCTRL_FN_GPIO 0 104 + #define MAX7360_PINCTRL_FN_PWM 1 105 + #define MAX7360_PINCTRL_FN_ROTARY 2 106 + static const struct pinfunction max7360_functions[] = { 107 + [MAX7360_PINCTRL_FN_GPIO] = PINCTRL_PINFUNCTION("gpio", simple_groups, 108 + ARRAY_SIZE(simple_groups)), 109 + [MAX7360_PINCTRL_FN_PWM] = PINCTRL_PINFUNCTION("pwm", simple_groups, 110 + ARRAY_SIZE(simple_groups)), 111 + [MAX7360_PINCTRL_FN_ROTARY] = PINCTRL_PINFUNCTION("rotary", rotary_groups, 112 + ARRAY_SIZE(rotary_groups)), 113 + }; 114 + 115 + static int max7360_get_functions_count(struct pinctrl_dev *pctldev) 116 + { 117 + return ARRAY_SIZE(max7360_functions); 118 + } 119 + 120 + static const char *max7360_get_function_name(struct pinctrl_dev *pctldev, unsigned int selector) 121 + { 122 + return max7360_functions[selector].name; 123 + } 124 + 125 + static int max7360_get_function_groups(struct pinctrl_dev *pctldev, unsigned int selector, 126 + const char * const **groups, 127 + unsigned int * const num_groups) 128 + { 129 + *groups = max7360_functions[selector].groups; 130 + *num_groups = max7360_functions[selector].ngroups; 131 + 132 + return 0; 133 + } 134 + 135 + static int max7360_set_mux(struct pinctrl_dev *pctldev, unsigned int selector, 136 + unsigned int group) 137 + { 138 + struct regmap *regmap = dev_get_regmap(pctldev->dev->parent, NULL); 139 + int val; 140 + 141 + /* 142 + * GPIO and PWM functions are the same: we only need to handle the 143 + * rotary encoder function, on pins 6 and 7. 144 + */ 145 + if (max7360_groups[group].pins[0] >= 6) { 146 + if (selector == MAX7360_PINCTRL_FN_ROTARY) 147 + val = MAX7360_GPIO_CFG_RTR_EN; 148 + else 149 + val = 0; 150 + 151 + return regmap_write_bits(regmap, MAX7360_REG_GPIOCFG, MAX7360_GPIO_CFG_RTR_EN, val); 152 + } 153 + 154 + return 0; 155 + } 156 + 157 + static const struct pinmux_ops max7360_pmxops = { 158 + .get_functions_count = max7360_get_functions_count, 159 + .get_function_name = max7360_get_function_name, 160 + .get_function_groups = max7360_get_function_groups, 161 + .set_mux = max7360_set_mux, 162 + .strict = true, 163 + }; 164 + 165 + static int max7360_pinctrl_probe(struct platform_device *pdev) 166 + { 167 + struct regmap *regmap; 168 + struct pinctrl_desc *pd; 169 + struct max7360_pinctrl *chip; 170 + struct device *dev = &pdev->dev; 171 + 172 + regmap = dev_get_regmap(dev->parent, NULL); 173 + if (!regmap) 174 + return dev_err_probe(dev, -ENODEV, "Could not get parent regmap\n"); 175 + 176 + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); 177 + if (!chip) 178 + return -ENOMEM; 179 + 180 + pd = &chip->pinctrl_desc; 181 + 182 + pd->pctlops = &max7360_pinctrl_ops; 183 + pd->pmxops = &max7360_pmxops; 184 + pd->name = dev_name(dev); 185 + pd->pins = max7360_pins; 186 + pd->npins = MAX7360_MAX_GPIO; 187 + pd->owner = THIS_MODULE; 188 + 189 + /* 190 + * This MFD sub-device does not have any associated device tree node: 191 + * properties are stored in the device node of the parent (MFD) device 192 + * and this same node is used in phandles of client devices. 193 + * Reuse this device tree node here, as otherwise the pinctrl subsystem 194 + * would be confused by this topology. 195 + */ 196 + device_set_of_node_from_dev(dev, dev->parent); 197 + 198 + chip->pctldev = devm_pinctrl_register(dev, pd, chip); 199 + if (IS_ERR(chip->pctldev)) 200 + return dev_err_probe(dev, PTR_ERR(chip->pctldev), "can't register controller\n"); 201 + 202 + return 0; 203 + } 204 + 205 + static struct platform_driver max7360_pinctrl_driver = { 206 + .driver = { 207 + .name = "max7360-pinctrl", 208 + }, 209 + .probe = max7360_pinctrl_probe, 210 + }; 211 + module_platform_driver(max7360_pinctrl_driver); 212 + 213 + MODULE_DESCRIPTION("MAX7360 pinctrl driver"); 214 + MODULE_AUTHOR("Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>"); 215 + MODULE_LICENSE("GPL");
+10
drivers/pwm/Kconfig
··· 432 432 To compile this driver as a module, choose M here: the module 433 433 will be called pwm-lpss-platform. 434 434 435 + config PWM_MAX7360 436 + tristate "MAX7360 PWMs" 437 + depends on MFD_MAX7360 438 + help 439 + PWM driver for Maxim Integrated MAX7360 multifunction device, with 440 + support for up to 8 PWM outputs. 441 + 442 + To compile this driver as a module, choose M here: the module 443 + will be called pwm-max7360. 444 + 435 445 config PWM_MC33XS2410 436 446 tristate "MC33XS2410 PWM support" 437 447 depends on OF
+1
drivers/pwm/Makefile
··· 38 38 obj-$(CONFIG_PWM_LPSS) += pwm-lpss.o 39 39 obj-$(CONFIG_PWM_LPSS_PCI) += pwm-lpss-pci.o 40 40 obj-$(CONFIG_PWM_LPSS_PLATFORM) += pwm-lpss-platform.o 41 + obj-$(CONFIG_PWM_MAX7360) += pwm-max7360.o 41 42 obj-$(CONFIG_PWM_MC33XS2410) += pwm-mc33xs2410.o 42 43 obj-$(CONFIG_PWM_MEDIATEK) += pwm-mediatek.o 43 44 obj-$(CONFIG_PWM_MESON) += pwm-meson.o
+209
drivers/pwm/pwm-max7360.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright 2025 Bootlin 4 + * 5 + * Author: Kamel BOUHARA <kamel.bouhara@bootlin.com> 6 + * Author: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> 7 + * 8 + * PWM functionality of the MAX7360 multi-function device. 9 + * https://www.analog.com/media/en/technical-documentation/data-sheets/MAX7360.pdf 10 + * 11 + * Limitations: 12 + * - Only supports normal polarity. 13 + * - The period is fixed to 2 ms. 14 + * - Only the duty cycle can be changed, new values are applied at the beginning 15 + * of the next cycle. 16 + * - When disabled, the output is put in Hi-Z immediately. 17 + */ 18 + #include <linux/bits.h> 19 + #include <linux/dev_printk.h> 20 + #include <linux/err.h> 21 + #include <linux/math64.h> 22 + #include <linux/mfd/max7360.h> 23 + #include <linux/minmax.h> 24 + #include <linux/mod_devicetable.h> 25 + #include <linux/module.h> 26 + #include <linux/platform_device.h> 27 + #include <linux/pwm.h> 28 + #include <linux/regmap.h> 29 + #include <linux/time.h> 30 + #include <linux/types.h> 31 + 32 + #define MAX7360_NUM_PWMS 8 33 + #define MAX7360_PWM_MAX 255 34 + #define MAX7360_PWM_STEPS 256 35 + #define MAX7360_PWM_PERIOD_NS (2 * NSEC_PER_MSEC) 36 + 37 + struct max7360_pwm_waveform { 38 + u8 duty_steps; 39 + bool enabled; 40 + }; 41 + 42 + static int max7360_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) 43 + { 44 + struct regmap *regmap = pwmchip_get_drvdata(chip); 45 + 46 + /* 47 + * Make sure we use the individual PWM configuration register and not 48 + * the global one. 49 + * We never need to use the global one, so there is no need to revert 50 + * that in the .free() callback. 51 + */ 52 + return regmap_write_bits(regmap, MAX7360_REG_PWMCFG(pwm->hwpwm), 53 + MAX7360_PORT_CFG_COMMON_PWM, 0); 54 + } 55 + 56 + static int max7360_pwm_round_waveform_tohw(struct pwm_chip *chip, 57 + struct pwm_device *pwm, 58 + const struct pwm_waveform *wf, 59 + void *_wfhw) 60 + { 61 + struct max7360_pwm_waveform *wfhw = _wfhw; 62 + u64 duty_steps; 63 + 64 + /* 65 + * Ignore user provided values for period_length_ns and duty_offset_ns: 66 + * we only support fixed period of MAX7360_PWM_PERIOD_NS and offset of 0. 67 + * Values from 0 to 254 as duty_steps will provide duty cycles of 0/256 68 + * to 254/256, while value 255 will provide a duty cycle of 100%. 69 + */ 70 + if (wf->duty_length_ns >= MAX7360_PWM_PERIOD_NS) { 71 + duty_steps = MAX7360_PWM_MAX; 72 + } else { 73 + duty_steps = (u32)wf->duty_length_ns * MAX7360_PWM_STEPS / MAX7360_PWM_PERIOD_NS; 74 + if (duty_steps == MAX7360_PWM_MAX) 75 + duty_steps = MAX7360_PWM_MAX - 1; 76 + } 77 + 78 + wfhw->duty_steps = min(MAX7360_PWM_MAX, duty_steps); 79 + wfhw->enabled = !!wf->period_length_ns; 80 + 81 + if (wf->period_length_ns && wf->period_length_ns < MAX7360_PWM_PERIOD_NS) 82 + return 1; 83 + else 84 + return 0; 85 + } 86 + 87 + static int max7360_pwm_round_waveform_fromhw(struct pwm_chip *chip, struct pwm_device *pwm, 88 + const void *_wfhw, struct pwm_waveform *wf) 89 + { 90 + const struct max7360_pwm_waveform *wfhw = _wfhw; 91 + 92 + wf->period_length_ns = wfhw->enabled ? MAX7360_PWM_PERIOD_NS : 0; 93 + wf->duty_offset_ns = 0; 94 + 95 + if (wfhw->enabled) { 96 + if (wfhw->duty_steps == MAX7360_PWM_MAX) 97 + wf->duty_length_ns = MAX7360_PWM_PERIOD_NS; 98 + else 99 + wf->duty_length_ns = DIV_ROUND_UP(wfhw->duty_steps * MAX7360_PWM_PERIOD_NS, 100 + MAX7360_PWM_STEPS); 101 + } else { 102 + wf->duty_length_ns = 0; 103 + } 104 + 105 + return 0; 106 + } 107 + 108 + static int max7360_pwm_write_waveform(struct pwm_chip *chip, 109 + struct pwm_device *pwm, 110 + const void *_wfhw) 111 + { 112 + struct regmap *regmap = pwmchip_get_drvdata(chip); 113 + const struct max7360_pwm_waveform *wfhw = _wfhw; 114 + unsigned int val; 115 + int ret; 116 + 117 + if (wfhw->enabled) { 118 + ret = regmap_write(regmap, MAX7360_REG_PWM(pwm->hwpwm), wfhw->duty_steps); 119 + if (ret) 120 + return ret; 121 + } 122 + 123 + val = wfhw->enabled ? BIT(pwm->hwpwm) : 0; 124 + return regmap_write_bits(regmap, MAX7360_REG_GPIOCTRL, BIT(pwm->hwpwm), val); 125 + } 126 + 127 + static int max7360_pwm_read_waveform(struct pwm_chip *chip, 128 + struct pwm_device *pwm, 129 + void *_wfhw) 130 + { 131 + struct regmap *regmap = pwmchip_get_drvdata(chip); 132 + struct max7360_pwm_waveform *wfhw = _wfhw; 133 + unsigned int val; 134 + int ret; 135 + 136 + ret = regmap_read(regmap, MAX7360_REG_GPIOCTRL, &val); 137 + if (ret) 138 + return ret; 139 + 140 + if (val & BIT(pwm->hwpwm)) { 141 + wfhw->enabled = true; 142 + ret = regmap_read(regmap, MAX7360_REG_PWM(pwm->hwpwm), &val); 143 + if (ret) 144 + return ret; 145 + 146 + wfhw->duty_steps = val; 147 + } else { 148 + wfhw->enabled = false; 149 + wfhw->duty_steps = 0; 150 + } 151 + 152 + return 0; 153 + } 154 + 155 + static const struct pwm_ops max7360_pwm_ops = { 156 + .request = max7360_pwm_request, 157 + .round_waveform_tohw = max7360_pwm_round_waveform_tohw, 158 + .round_waveform_fromhw = max7360_pwm_round_waveform_fromhw, 159 + .read_waveform = max7360_pwm_read_waveform, 160 + .write_waveform = max7360_pwm_write_waveform, 161 + }; 162 + 163 + static int max7360_pwm_probe(struct platform_device *pdev) 164 + { 165 + struct device *dev = &pdev->dev; 166 + struct pwm_chip *chip; 167 + struct regmap *regmap; 168 + int ret; 169 + 170 + regmap = dev_get_regmap(dev->parent, NULL); 171 + if (!regmap) 172 + return dev_err_probe(dev, -ENODEV, "Could not get parent regmap\n"); 173 + 174 + /* 175 + * This MFD sub-device does not have any associated device tree node: 176 + * properties are stored in the device node of the parent (MFD) device 177 + * and this same node is used in phandles of client devices. 178 + * Reuse this device tree node here, as otherwise the PWM subsystem 179 + * would be confused by this topology. 180 + */ 181 + device_set_of_node_from_dev(dev, dev->parent); 182 + 183 + chip = devm_pwmchip_alloc(dev, MAX7360_NUM_PWMS, 0); 184 + if (IS_ERR(chip)) 185 + return PTR_ERR(chip); 186 + chip->ops = &max7360_pwm_ops; 187 + 188 + pwmchip_set_drvdata(chip, regmap); 189 + 190 + ret = devm_pwmchip_add(dev, chip); 191 + if (ret) 192 + return dev_err_probe(dev, ret, "Failed to add PWM chip\n"); 193 + 194 + return 0; 195 + } 196 + 197 + static struct platform_driver max7360_pwm_driver = { 198 + .driver = { 199 + .name = "max7360-pwm", 200 + .probe_type = PROBE_PREFER_ASYNCHRONOUS, 201 + }, 202 + .probe = max7360_pwm_probe, 203 + }; 204 + module_platform_driver(max7360_pwm_driver); 205 + 206 + MODULE_DESCRIPTION("MAX7360 PWM driver"); 207 + MODULE_AUTHOR("Kamel BOUHARA <kamel.bouhara@bootlin.com>"); 208 + MODULE_AUTHOR("Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com>"); 209 + MODULE_LICENSE("GPL");
+18
include/linux/gpio/regmap.h
··· 6 6 struct device; 7 7 struct fwnode_handle; 8 8 struct gpio_regmap; 9 + struct gpio_chip; 9 10 struct irq_domain; 10 11 struct regmap; 11 12 ··· 41 40 * @drvdata: (Optional) Pointer to driver specific data which is 42 41 * not used by gpio-remap but is provided "as is" to the 43 42 * driver callback(s). 43 + * @init_valid_mask: (Optional) Routine to initialize @valid_mask, to be used 44 + * if not all GPIOs are valid. 45 + * @regmap_irq_chip: (Optional) Pointer on an regmap_irq_chip structure. If 46 + * set, a regmap-irq device will be created and the IRQ 47 + * domain will be set accordingly. 48 + * @regmap_irq_line (Optional) The IRQ the device uses to signal interrupts. 49 + * @regmap_irq_flags (Optional) The IRQF_ flags to use for the interrupt. 44 50 * 45 51 * The ->reg_mask_xlate translates a given base address and GPIO offset to 46 52 * register and mask pair. The base address is one of the given register ··· 86 78 int ngpio_per_reg; 87 79 struct irq_domain *irq_domain; 88 80 81 + #ifdef CONFIG_REGMAP_IRQ 82 + struct regmap_irq_chip *regmap_irq_chip; 83 + int regmap_irq_line; 84 + unsigned long regmap_irq_flags; 85 + #endif 86 + 89 87 int (*reg_mask_xlate)(struct gpio_regmap *gpio, unsigned int base, 90 88 unsigned int offset, unsigned int *reg, 91 89 unsigned int *mask); 90 + 91 + int (*init_valid_mask)(struct gpio_chip *gc, 92 + unsigned long *valid_mask, 93 + unsigned int ngpios); 92 94 93 95 void *drvdata; 94 96 };
+109
include/linux/mfd/max7360.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + 3 + #ifndef __LINUX_MFD_MAX7360_H 4 + #define __LINUX_MFD_MAX7360_H 5 + 6 + #include <linux/bits.h> 7 + 8 + #define MAX7360_MAX_KEY_ROWS 8 9 + #define MAX7360_MAX_KEY_COLS 8 10 + #define MAX7360_MAX_KEY_NUM (MAX7360_MAX_KEY_ROWS * MAX7360_MAX_KEY_COLS) 11 + #define MAX7360_ROW_SHIFT 3 12 + 13 + #define MAX7360_MAX_GPIO 8 14 + #define MAX7360_MAX_GPO 6 15 + #define MAX7360_PORT_PWM_COUNT 8 16 + #define MAX7360_PORT_RTR_PIN (MAX7360_PORT_PWM_COUNT - 1) 17 + 18 + /* 19 + * MAX7360 registers 20 + */ 21 + #define MAX7360_REG_KEYFIFO 0x00 22 + #define MAX7360_REG_CONFIG 0x01 23 + #define MAX7360_REG_DEBOUNCE 0x02 24 + #define MAX7360_REG_INTERRUPT 0x03 25 + #define MAX7360_REG_PORTS 0x04 26 + #define MAX7360_REG_KEYREP 0x05 27 + #define MAX7360_REG_SLEEP 0x06 28 + 29 + /* 30 + * MAX7360 GPIO registers 31 + * 32 + * All these registers are reset together when writing bit 3 of 33 + * MAX7360_REG_GPIOCFG. 34 + */ 35 + #define MAX7360_REG_GPIOCFG 0x40 36 + #define MAX7360_REG_GPIOCTRL 0x41 37 + #define MAX7360_REG_GPIODEB 0x42 38 + #define MAX7360_REG_GPIOCURR 0x43 39 + #define MAX7360_REG_GPIOOUTM 0x44 40 + #define MAX7360_REG_PWMCOM 0x45 41 + #define MAX7360_REG_RTRCFG 0x46 42 + #define MAX7360_REG_I2C_TIMEOUT 0x48 43 + #define MAX7360_REG_GPIOIN 0x49 44 + #define MAX7360_REG_RTR_CNT 0x4A 45 + #define MAX7360_REG_PWMBASE 0x50 46 + #define MAX7360_REG_PWMCFGBASE 0x58 47 + 48 + #define MAX7360_REG_GPIO_LAST 0x5F 49 + 50 + #define MAX7360_REG_PWM(x) (MAX7360_REG_PWMBASE + (x)) 51 + #define MAX7360_REG_PWMCFG(x) (MAX7360_REG_PWMCFGBASE + (x)) 52 + 53 + /* 54 + * Configuration register bits 55 + */ 56 + #define MAX7360_FIFO_EMPTY 0x3F 57 + #define MAX7360_FIFO_OVERFLOW 0x7F 58 + #define MAX7360_FIFO_RELEASE BIT(6) 59 + #define MAX7360_FIFO_COL GENMASK(5, 3) 60 + #define MAX7360_FIFO_ROW GENMASK(2, 0) 61 + 62 + #define MAX7360_CFG_SLEEP BIT(7) 63 + #define MAX7360_CFG_INTERRUPT BIT(5) 64 + #define MAX7360_CFG_KEY_RELEASE BIT(3) 65 + #define MAX7360_CFG_WAKEUP BIT(1) 66 + #define MAX7360_CFG_TIMEOUT BIT(0) 67 + 68 + #define MAX7360_DEBOUNCE GENMASK(4, 0) 69 + #define MAX7360_DEBOUNCE_MIN 9 70 + #define MAX7360_DEBOUNCE_MAX 40 71 + #define MAX7360_PORTS GENMASK(8, 5) 72 + 73 + #define MAX7360_INTERRUPT_TIME_MASK GENMASK(4, 0) 74 + #define MAX7360_INTERRUPT_FIFO_MASK GENMASK(7, 5) 75 + 76 + #define MAX7360_PORT_CFG_INTERRUPT_MASK BIT(7) 77 + #define MAX7360_PORT_CFG_INTERRUPT_EDGES BIT(6) 78 + #define MAX7360_PORT_CFG_COMMON_PWM BIT(5) 79 + 80 + /* 81 + * Autosleep register values 82 + */ 83 + #define MAX7360_AUTOSLEEP_8192MS 0x01 84 + #define MAX7360_AUTOSLEEP_4096MS 0x02 85 + #define MAX7360_AUTOSLEEP_2048MS 0x03 86 + #define MAX7360_AUTOSLEEP_1024MS 0x04 87 + #define MAX7360_AUTOSLEEP_512MS 0x05 88 + #define MAX7360_AUTOSLEEP_256MS 0x06 89 + 90 + #define MAX7360_GPIO_CFG_RTR_EN BIT(7) 91 + #define MAX7360_GPIO_CFG_GPIO_EN BIT(4) 92 + #define MAX7360_GPIO_CFG_GPIO_RST BIT(3) 93 + 94 + #define MAX7360_ROT_DEBOUNCE GENMASK(3, 0) 95 + #define MAX7360_ROT_DEBOUNCE_MIN 0 96 + #define MAX7360_ROT_DEBOUNCE_MAX 15 97 + #define MAX7360_ROT_INTCNT GENMASK(6, 4) 98 + #define MAX7360_ROT_INTCNT_DLY BIT(7) 99 + 100 + #define MAX7360_INT_INTI 0 101 + #define MAX7360_INT_INTK 1 102 + 103 + #define MAX7360_INT_GPIO 0 104 + #define MAX7360_INT_KEYPAD 1 105 + #define MAX7360_INT_ROTARY 2 106 + 107 + #define MAX7360_NR_INTERNAL_IRQS 3 108 + 109 + #endif