Message ID | 20250113-mdb-max7360-support-v3-0-9519b4acb0b1@bootlin.com |
---|---|
Headers | show |
Series | Add support for MAX7360 | expand |
On Mon, Jan 13, 2025 at 01:42:25PM +0100, Mathieu Dubois-Briand wrote: > + interrupt-controller: true > + > + "#interrupt-cells": > + const: 2 > + > + maxim,constant-current-disable: > + $ref: /schemas/types.yaml#/definitions/uint32 > + description: > Drop > > + Bit field, each bit disables constant-current output of the associated > + GPIO, starting from the least significant bit for the first GPIO. maximum: 0xff? > + > +required: > + - compatible > + - gpio-controller > + - ngpios > + allOf: here, so you won't re-indent it later. > +if: > + properties: > + compatible: > + contains: > + enum: > + - maxim,max7360-gpio > +then: > + required: > + - interrupt-controller > +else: > + properties: > + interrupt-controller: false > + maxim,constant-current-disable: false > + > + ngpios: > + maximum: 6 > + > +additionalProperties: false > + > +examples: > + - | > + gpio { > + compatible = "maxim,max7360-gpio"; > + > + gpio-controller; > + #gpio-cells = <2>; > + ngpios = <8>; > + maxim,constant-current-disable = <0x06>; > + > + interrupt-controller; > + #interrupt-cells = <2>; > + }; ... > + interrupt-names: > + items: > + - const: inti > + - const: intk > + > + keypad-debounce-delay-ms: > + description: Keypad debounce delay in ms > + minimum: 9 > + maximum: 40 > + default: 9 > + > + autorepeat: true Drop, not needed. > + > + rotary-debounce-delay-ms: > + description: Rotary encoder debounce delay in ms > + minimum: 0 > + maximum: 15 > + default: 0 > + > + linux,axis: > + description: The input subsystem axis to map to this rotary encoder. Missing type. I guess you wanted to reference rotary encoder schema, next to input and matrix-keymap? > + > + "#pwm-cells": > + const: 3 > + > + gpio: > + $ref: /schemas/gpio/maxim,max7360-gpio.yaml# > + description: > Drop > > + PORT0 to PORT7 general purpose input/output pins configuration. > + > + gpo: > + $ref: /schemas/gpio/maxim,max7360-gpio.yaml# > + description: > Drop > > + COL2 to COL7 general purpose output pins configuration. > + Allows to use unused keypad columns as outputs. > + The MAX7360 has 8 column lines and 6 of them can be used as GPOs. Value > + of ngpios must be coherent with the value of keypad,num-columns, as their > + sum must not exceed the number of physical lines. > + > +required: > + - compatible > + - reg > + - interrupts > + - interrupt-names > + - linux,keymap > + - linux,axis > + - "#pwm-cells" gpio and gpo nodes are optional? How would the driver behave? I assume you need to define the partition between GPIOs, especially that 'ngpios' are a required property in their schema. > + > +unevaluatedProperties: false > + > +examples: > + - | > + #include <dt-bindings/input/input.h> > + #include <dt-bindings/interrupt-controller/arm-gic.h> > + > + i2c { > + #address-cells = <1>; > + #size-cells = <0>; > + > + io-expander@38 { > + compatible = "maxim,max7360"; > + reg = <0x38>; > + > + interrupt-parent = <&gpio1>; > + interrupts = <23 IRQ_TYPE_LEVEL_LOW>, > + <24 IRQ_TYPE_LEVEL_LOW>; > + interrupt-names = "inti", "intk"; > + > + keypad,num-rows = <8>; > + keypad,num-columns = <4>; > + linux,keymap = < > + MATRIX_KEY(0x00, 0x00, KEY_F5) > + MATRIX_KEY(0x01, 0x00, KEY_F4) > + MATRIX_KEY(0x02, 0x01, KEY_F6) > + >; > + keypad-debounce-delay-ms = <10>; > + autorepeat; > + > + rotary-debounce-delay-ms = <2>; > + linux,axis = <0>; /* REL_X */ > + > + #pwm-cells = <3>; > + > + max7360_gpio: gpio { > + compatible = "maxim,max7360-gpio"; > + > + gpio-controller; > + #gpio-cells = <2>; > + ngpios = <8>; > + maxim,constant-current-disable = <0x06>; > + > + interrupt-controller; > + #interrupt-cells = <0x2>; > + }; > + > + max7360_gpo: gpo { > + compatible = "maxim,max7360-gpo"; > + > + gpio-controller; > + #gpio-cells = <2>; > + ngpios = <4>; > + }; > + }; > + }; > > -- > 2.39.5 >
On Mon Jan 13, 2025 at 1:42 PM CET, Mathieu Dubois-Briand wrote: > Add driver for Maxim Integrated MAX7360 rotary encoder controller, > supporting a single rotary switch. > > Signed-off-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> > --- ... > +static irqreturn_t max7360_rotary_irq(int irq, void *data) > +{ > + struct max7360_rotary *max7360_rotary = data; > + int val; > + int ret; > + > + ret = regmap_read(max7360_rotary->regmap, MAX7360_REG_RTR_CNT, &val); > + if (ret < 0) { > + dev_err(&max7360_rotary->input->dev, > + "Failed to read rotary counter\n"); > + return IRQ_NONE; > + } > + > + if (val == 0) { > + dev_dbg(&max7360_rotary->input->dev, > + "Got a spurious interrupt\n"); > + > + return IRQ_NONE; > + } > + > + input_report_rel(max7360_rotary->input, max7360_rotary->axis, > + (int8_t)val); > + input_sync(max7360_rotary->input); > + I have a question about the type of events reported here. I used rotary_encoder.c as a reference, so I'm reporting some EV_REL events on a given axis, such as REL_X. On the other hand, I know there is an out-of-tree version of this driver that is instead reporting key events, such as KEY_DOWN or KEY_UP. I also know there are existing applications that do rely on this behaviour. So my question is, what is the best kind of events to report here ? Is there any guideline that do apply here? Should I better try to mimic the behaviour of the existing out-of-tree driver, or should I mimic the behaviour of rotary_encoder.c, so we have a similar behaviour for all in-kernel rotary encoder drivers? > + return IRQ_HANDLED; > +} > +
Hello Mathieu, On Mon, Jan 13, 2025 at 01:42:27PM +0100, mathieu.dubois-briand@bootlin.com wrote: > From: Kamel Bouhara <kamel.bouhara@bootlin.com> > > Add driver for Maxim Integrated MAX7360 PWM controller, supporting up to > 8 independent PWM outputs. > > Signed-off-by: Kamel Bouhara <kamel.bouhara@bootlin.com> > Signed-off-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> > --- > drivers/pwm/Kconfig | 11 +++ > drivers/pwm/Makefile | 1 + > drivers/pwm/pwm-max7360.c | 220 ++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 232 insertions(+) > > diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig > index 0915c1e7df16..399dc3f76e92 100644 > --- a/drivers/pwm/Kconfig > +++ b/drivers/pwm/Kconfig > @@ -745,4 +745,15 @@ config PWM_XILINX > To compile this driver as a module, choose M here: the module > will be called pwm-xilinx. > > +config PWM_MAX7360 > + tristate "MAX7360 PWMs" > + depends on MFD_MAX7360 > + depends on OF_GPIO > + help > + PWM driver for Maxim Integrated MAX7360 multifunction device, with > + support for up to 8 PWM outputs. > + > + To compile this driver as a module, choose M here: the module > + will be called pwm-max7360. > + > endif > diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile > index 9081e0c0e9e0..ae8908ffc892 100644 > --- a/drivers/pwm/Makefile > +++ b/drivers/pwm/Makefile > @@ -36,6 +36,7 @@ obj-$(CONFIG_PWM_LPC32XX) += pwm-lpc32xx.o > obj-$(CONFIG_PWM_LPSS) += pwm-lpss.o > obj-$(CONFIG_PWM_LPSS_PCI) += pwm-lpss-pci.o > obj-$(CONFIG_PWM_LPSS_PLATFORM) += pwm-lpss-platform.o > +obj-$(CONFIG_PWM_MAX7360) += pwm-max7360.o > obj-$(CONFIG_PWM_MESON) += pwm-meson.o > obj-$(CONFIG_PWM_MEDIATEK) += pwm-mediatek.o > obj-$(CONFIG_PWM_MICROCHIP_CORE) += pwm-microchip-core.o > diff --git a/drivers/pwm/pwm-max7360.c b/drivers/pwm/pwm-max7360.c > new file mode 100644 > index 000000000000..e76a8aa05fc4 > --- /dev/null > +++ b/drivers/pwm/pwm-max7360.c > @@ -0,0 +1,220 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Copyright 2024 Bootlin > + * > + * Author: Kamel BOUHARA <kamel.bouhara@bootlin.com> > + * > + * Limitations: > + * - Only supports normal polarity. > + * - The period is fixed to 2 ms. > + * - Only the duty cycle can be changed, new values are applied at the beginning > + * of the next cycle. > + * - When disabled, the output is put in Hi-Z. > + */ > +#include <linux/math.h> > +#include <linux/mfd/max7360.h> > +#include <linux/mod_devicetable.h> > +#include <linux/module.h> > +#include <linux/of.h> > +#include <linux/platform_device.h> > +#include <linux/pwm.h> > +#include <linux/regmap.h> > + > +#define MAX7360_NUM_PWMS 8 > +#define MAX7360_PWM_MAX_RES 256 > +#define MAX7360_PWM_PERIOD_NS 2000000 /* 500 Hz */ > +#define MAX7360_PWM_COMMON_PWN BIT(5) > +#define MAX7360_PWM_CTRL_ENABLE(n) BIT(n) > +#define MAX7360_PWM_PORT(n) BIT(n) > + > +struct max7360_pwm { > + struct device *parent; > + struct regmap *regmap; > +}; > + > +static inline struct max7360_pwm *to_max7360_pwm(struct pwm_chip *chip) Please stick to the common function prefix also here. So something like max7360_pwm_from_chip would work. > +{ > + return pwmchip_get_drvdata(chip); > +} > + > +static int max7360_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) > +{ > + struct max7360_pwm *max7360_pwm; > + int ret; > + > + max7360_pwm = to_max7360_pwm(chip); > + ret = max7360_port_pin_request(max7360_pwm->parent, pwm->hwpwm, > + true); The statement fits on a single line just fine. > + if (ret) { > + dev_warn(&chip->dev, "failed to request pwm-%d\n", pwm->hwpwm); > + return ret; > + } > + > + ret = regmap_write_bits(max7360_pwm->regmap, > + MAX7360_REG_PWMCFG + pwm->hwpwm, Can you make MAX7360_REG_PWMCFG a macro accepting pwm->hwpwm as parameter please? > + MAX7360_PWM_COMMON_PWN, > + 0); > + if (ret) { > + dev_warn(&chip->dev, > + "failed to write pwm-%d cfg register, error %d\n", > + pwm->hwpwm, ret); > + return ret; > + } > + > + ret = regmap_write_bits(max7360_pwm->regmap, MAX7360_REG_PORTS, > + MAX7360_PWM_PORT(pwm->hwpwm), > + MAX7360_PWM_PORT(pwm->hwpwm)); > + if (ret) { > + dev_warn(&chip->dev, > + "failed to write pwm-%d ports register, error %d\n", > + pwm->hwpwm, ret); > + return ret; > + } > + > + return 0; > +} > + > +static void max7360_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) > +{ > + struct max7360_pwm *max7360_pwm; > + int ret; > + > + max7360_pwm = to_max7360_pwm(chip); > + ret = regmap_write_bits(max7360_pwm->regmap, MAX7360_REG_GPIOCTRL, > + MAX7360_PWM_CTRL_ENABLE(pwm->hwpwm), > + 0); > + if (ret) > + dev_warn(&chip->dev, "failed to disable pwm-%d , error %d\n", > + pwm->hwpwm, ret); .free is not supposed to stop the PWM. Though I admit this concept is a bit fuzzy, because when unconfiguring the pin function this is kind of moot. Still it's the responsibility of the consumer to stop the PWM before pwm_put(). Also s/ ,/,/ and use %pe for error codes. > + max7360_port_pin_request(max7360_pwm->parent, pwm->hwpwm, > + false); > +} > + > +static int max7360_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, > + const struct pwm_state *state) > +{ > + struct max7360_pwm *max7360_pwm; > + u64 duty_steps; > + int ret; > + > + if (state->polarity != PWM_POLARITY_NORMAL) > + return -EINVAL; > + > + if (state->period != MAX7360_PWM_PERIOD_NS) { > + dev_warn(&chip->dev, > + "unsupported pwm period: %llu, should be %u\n", > + state->period, MAX7360_PWM_PERIOD_NS); > + return -EINVAL; Please don't emit error messages in .apply(). Also a driver is supposed to round down .period, so any value >= MAX7360_PWM_PERIOD_NS should be accepted. Also note that you might want to implement the waveform callbacks instead of .apply() and .get_state() for the more modern abstraction (with slightly different rounding rules). > + } > + > + duty_steps = mul_u64_u64_div_u64(state->duty_cycle, MAX7360_PWM_MAX_RES, > + MAX7360_PWM_PERIOD_NS); > + > + max7360_pwm = to_max7360_pwm(chip); > + ret = regmap_write_bits(max7360_pwm->regmap, MAX7360_REG_GPIOCTRL, > + MAX7360_PWM_CTRL_ENABLE(pwm->hwpwm), > + MAX7360_PWM_CTRL_ENABLE(pwm->hwpwm)); > + if (ret) { > + dev_warn(&chip->dev, "failed to enable pwm-%d , error %d\n", > + pwm->hwpwm, ret); > + return ret; > + } > + > + ret = regmap_write(max7360_pwm->regmap, MAX7360_REG_PWMBASE + pwm->hwpwm, > + duty_steps >= 255 ? 255 : duty_steps); > + if (ret) { > + dev_warn(&chip->dev, > + "failed to apply pwm duty_cycle %llu on pwm-%d, error %d\n", > + duty_steps, pwm->hwpwm, ret); > + return ret; > + } > + > + return 0; > +} > + > +static int max7360_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, > + struct pwm_state *state) > +{ > + struct max7360_pwm *max7360_pwm; > + unsigned int val; > + int ret; > + > + max7360_pwm = to_max7360_pwm(chip); > + > + state->period = MAX7360_PWM_PERIOD_NS; > + state->polarity = PWM_POLARITY_NORMAL; > + > + ret = regmap_read(max7360_pwm->regmap, MAX7360_REG_GPIOCTRL, &val); > + if (ret) { > + dev_warn(&chip->dev, > + "failed to read pwm configuration on pwm-%d, error %d\n", > + pwm->hwpwm, ret); Similar to .apply() please no messages in .get_state(). Just fail silently. > + return ret; > + } > + state->enabled = !!(val & MAX7360_PWM_CTRL_ENABLE(pwm->hwpwm)); > + > + ret = regmap_read(max7360_pwm->regmap, MAX7360_REG_PWMBASE + pwm->hwpwm, > + &val); > + if (ret) { > + dev_warn(&chip->dev, > + "failed to read pwm duty_cycle on pwm-%d, error %d\n", > + pwm->hwpwm, ret); > + return ret; > + } > + state->duty_cycle = mul_u64_u64_div_u64(val, MAX7360_PWM_PERIOD_NS, > + MAX7360_PWM_MAX_RES); You have to round up here. I would expect that the checks in the core (with PWM_DEBUG=1) help you catching this type of error. In your case changing the configuration to .period = 2000000, .duty_cycle = 234379, should yield some hint in the kernel log. > + return 0; > +} Best regards Uwe
On Fri Jan 17, 2025 at 10:33 AM CET, Uwe Kleine-König wrote: > Hello Mathieu, > > On Mon, Jan 13, 2025 at 01:42:27PM +0100, mathieu.dubois-briand@bootlin.com wrote: > > From: Kamel Bouhara <kamel.bouhara@bootlin.com> ... > > +static int max7360_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, > > + const struct pwm_state *state) > > +{ > > + struct max7360_pwm *max7360_pwm; > > + u64 duty_steps; > > + int ret; > > + > > + if (state->polarity != PWM_POLARITY_NORMAL) > > + return -EINVAL; > > + > > + if (state->period != MAX7360_PWM_PERIOD_NS) { > > + dev_warn(&chip->dev, > > + "unsupported pwm period: %llu, should be %u\n", > > + state->period, MAX7360_PWM_PERIOD_NS); > > + return -EINVAL; > > Please don't emit error messages in .apply(). Also a driver is supposed > to round down .period, so any value >= MAX7360_PWM_PERIOD_NS should be > accepted. > > Also note that you might want to implement the waveform callbacks > instead of .apply() and .get_state() for the more modern abstraction > (with slightly different rounding rules). > Sure, I just switched to the waveform callbacks, it was quite straightforward. > > +static int max7360_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, > > + struct pwm_state *state) > > +{ ... > > + state->duty_cycle = mul_u64_u64_div_u64(val, MAX7360_PWM_PERIOD_NS, > > + MAX7360_PWM_MAX_RES); > > You have to round up here. I would expect that the checks in the core > (with PWM_DEBUG=1) help you catching this type of error. In your case > changing the configuration to > > .period = 2000000, > .duty_cycle = 234379, > > should yield some hint in the kernel log. > Thanks for the reproduce steps: I saw the bug and fixed it. Also MAX7360_PWM_MAX_RES had to be set to 255 and not 256... > > + return 0; > > +} > > Best regards > Uwe I also fixed all other points mentioned in your mail. Thanks again for your review.
On Fri, Jan 17, 2025 at 03:11:29PM +0100, Mathieu Dubois-Briand wrote: > On Fri Jan 17, 2025 at 10:33 AM CET, Uwe Kleine-König wrote: > > Hello Mathieu, > > > > On Mon, Jan 13, 2025 at 01:42:27PM +0100, mathieu.dubois-briand@bootlin.com wrote: > > > From: Kamel Bouhara <kamel.bouhara@bootlin.com> > ... > > > +static int max7360_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, > > > + const struct pwm_state *state) > > > +{ > > > + struct max7360_pwm *max7360_pwm; > > > + u64 duty_steps; > > > + int ret; > > > + > > > + if (state->polarity != PWM_POLARITY_NORMAL) > > > + return -EINVAL; > > > + > > > + if (state->period != MAX7360_PWM_PERIOD_NS) { > > > + dev_warn(&chip->dev, > > > + "unsupported pwm period: %llu, should be %u\n", > > > + state->period, MAX7360_PWM_PERIOD_NS); > > > + return -EINVAL; > > > > Please don't emit error messages in .apply(). Also a driver is supposed > > to round down .period, so any value >= MAX7360_PWM_PERIOD_NS should be > > accepted. > > > > Also note that you might want to implement the waveform callbacks > > instead of .apply() and .get_state() for the more modern abstraction > > (with slightly different rounding rules). > > > > Sure, I just switched to the waveform callbacks, it was quite > straightforward. sounds great. Note that the detail in rounding that is different for waveforms is that a value that cannot be round down to a valid value (because it's too small) is round up. This is a bit ugly in the drivers but simplifies usage considerably. So you never return -EINVAL because the values don't fit. > Thanks for the reproduce steps: I saw the bug and fixed it. Also > MAX7360_PWM_MAX_RES had to be set to 255 and not 256... A good test (for a driver doing .apply/.get_state) is a sequence of increasing settings. So something like: for p in range(1000, 10000): pwm_apply(period=p, duty_cycle=0, ...) and also do the same for duty_cycle and also try decreasing series. Best regards Uwe
On Fri Jan 17, 2025 at 3:40 PM CET, Uwe Kleine-König wrote: > On Fri, Jan 17, 2025 at 03:11:29PM +0100, Mathieu Dubois-Briand wrote: > > On Fri Jan 17, 2025 at 10:33 AM CET, Uwe Kleine-König wrote: > > > Hello Mathieu, > > > > > > On Mon, Jan 13, 2025 at 01:42:27PM +0100, mathieu.dubois-briand@bootlin.com wrote: > > > > From: Kamel Bouhara <kamel.bouhara@bootlin.com> > > ... > > > > +static int max7360_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, > > > > + const struct pwm_state *state) > > > > +{ > > > > + struct max7360_pwm *max7360_pwm; > > > > + u64 duty_steps; > > > > + int ret; > > > > + > > > > + if (state->polarity != PWM_POLARITY_NORMAL) > > > > + return -EINVAL; > > > > + > > > > + if (state->period != MAX7360_PWM_PERIOD_NS) { > > > > + dev_warn(&chip->dev, > > > > + "unsupported pwm period: %llu, should be %u\n", > > > > + state->period, MAX7360_PWM_PERIOD_NS); > > > > + return -EINVAL; > > > > > > Please don't emit error messages in .apply(). Also a driver is supposed > > > to round down .period, so any value >= MAX7360_PWM_PERIOD_NS should be > > > accepted. > > > > > > Also note that you might want to implement the waveform callbacks > > > instead of .apply() and .get_state() for the more modern abstraction > > > (with slightly different rounding rules). > > > > > > > Sure, I just switched to the waveform callbacks, it was quite > > straightforward. > > sounds great. Note that the detail in rounding that is different for > waveforms is that a value that cannot be round down to a valid value > (because it's too small) is round up. This is a bit ugly in the drivers > but simplifies usage considerably. So you never return -EINVAL because > the values don't fit. > Sorry, I'm not sure I got it right. Does this affect the three members of pwm_waveform (period_length_ns, duty_offset_ns, duty_length_ns) ? So on this device where the period is fixed and I cannot define an offset, does that mean I will silently accept any value for period_length_ns and duty_offset_ns ? Best regards, Mathieu
Hello Mathieu, On Fri, Jan 17, 2025 at 04:47:45PM +0100, Mathieu Dubois-Briand wrote: > On Fri Jan 17, 2025 at 3:40 PM CET, Uwe Kleine-König wrote: > > sounds great. Note that the detail in rounding that is different for > > waveforms is that a value that cannot be round down to a valid value > > (because it's too small) is round up. This is a bit ugly in the drivers > > but simplifies usage considerably. So you never return -EINVAL because > > the values don't fit. > > Sorry, I'm not sure I got it right. Does this affect the three members > of pwm_waveform (period_length_ns, duty_offset_ns, duty_length_ns) ? So > on this device where the period is fixed and I cannot define an offset, > does that mean I will silently accept any value for period_length_ns and > duty_offset_ns ? Yes. The fromhw callback obviously always fills the respective constants into .period_length_ns and .duty_offset_ns and the tohw callback essentially only looks at .duty_length_ns. Best regards Uwe
On Mon Jan 20, 2025 at 3:13 PM CET, Uwe Kleine-König wrote: > Hello Mathieu, > > On Fri, Jan 17, 2025 at 04:47:45PM +0100, Mathieu Dubois-Briand wrote: > > On Fri Jan 17, 2025 at 3:40 PM CET, Uwe Kleine-König wrote: > > > sounds great. Note that the detail in rounding that is different for > > > waveforms is that a value that cannot be round down to a valid value > > > (because it's too small) is round up. This is a bit ugly in the drivers > > > but simplifies usage considerably. So you never return -EINVAL because > > > the values don't fit. > > > > Sorry, I'm not sure I got it right. Does this affect the three members > > of pwm_waveform (period_length_ns, duty_offset_ns, duty_length_ns) ? So > > on this device where the period is fixed and I cannot define an offset, > > does that mean I will silently accept any value for period_length_ns and > > duty_offset_ns ? > > Yes. The fromhw callback obviously always fills the respective constants > into .period_length_ns and .duty_offset_ns and the tohw callback > essentially only looks at .duty_length_ns. > > Best regards > Uwe Ok, thanks! I will make these changes for the next version. Best regards, Mathieu
This series implements a set of drivers allowing to support the Maxim Integrated MAX7360 device. The MAX7360 is an I2C key-switch and led controller, with following functionalities: - Keypad controller for a key matrix of up to 8 rows and 8 columns. - Rotary encoder support, for a single rotary encoder. - Up to 8 PWM outputs. - Up to 8 GPIOs with support for interrupts and 6 GPOs. Chipset pins are shared between all functionalities, so all cannot be used at the same time. Signed-off-by: Mathieu Dubois-Briand <mathieu.dubois-briand@bootlin.com> --- Changes in v3: - Fix MFD device tree binding to add gpio child nodes. - Fix various small issues in device tree bindings. - Add missing line returns in error messages. - Use dev_err_probe() when possible. - Link to v2: https://lore.kernel.org/r/20241223-mdb-max7360-support-v2-0-37a8d22c36ed@bootlin.com Changes in v2: - Removing device tree subnodes for keypad, rotary encoder and pwm functionalities. - Fixed dt-bindings syntax and naming. - Fixed missing handling of requested period in PWM driver. - Cleanup of the code - Link to v1: https://lore.kernel.org/r/20241219-mdb-max7360-support-v1-0-8e8317584121@bootlin.com --- Kamel Bouhara (2): mfd: Add max7360 support pwm: max7360: Add MAX7360 PWM support Mathieu Dubois-Briand (5): dt-bindings: mfd: gpio: Add MAX7360 gpio: max7360: Add MAX7360 gpio support input: keyboard: Add support for MAX7360 keypad input: misc: Add support for MAX7360 rotary MAINTAINERS: Add entry on MAX7360 driver .../bindings/gpio/maxim,max7360-gpio.yaml | 90 ++++ .../devicetree/bindings/mfd/maxim,max7360.yaml | 140 +++++++ MAINTAINERS | 12 + drivers/gpio/Kconfig | 11 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-max7360.c | 454 +++++++++++++++++++++ drivers/input/keyboard/Kconfig | 12 + drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/max7360-keypad.c | 284 +++++++++++++ drivers/input/misc/Kconfig | 11 + drivers/input/misc/Makefile | 1 + drivers/input/misc/max7360-rotary.c | 183 +++++++++ drivers/mfd/Kconfig | 12 + drivers/mfd/Makefile | 1 + drivers/mfd/max7360.c | 296 ++++++++++++++ drivers/pwm/Kconfig | 11 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-max7360.c | 220 ++++++++++ include/linux/mfd/max7360.h | 109 +++++ 19 files changed, 1850 insertions(+) --- base-commit: 78d4f34e2115b517bcbfe7ec0d018bbbb6f9b0b8 change-id: 20241219-mdb-max7360-support-223a8ce45ba3 Best regards,