Message ID | 20250113-mdb-max7360-support-v3-3-9519b4acb0b1@bootlin.com |
---|---|
State | New |
Headers | show |
Series | Add support for MAX7360 | expand |
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
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) +{ + 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); + 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, + 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); + + 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; + } + + 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); + 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); + + return 0; +} + +static const struct pwm_ops max7360_pwm_ops = { + .request = max7360_pwm_request, + .free = max7360_pwm_free, + .apply = max7360_pwm_apply, + .get_state = max7360_pwm_get_state, +}; + +static int max7360_pwm_probe(struct platform_device *pdev) +{ + struct max7360_pwm *max7360_pwm; + struct pwm_chip *chip; + int ret; + + if (!pdev->dev.parent) + return dev_err_probe(&pdev->dev, -ENODEV, "no parent device\n"); + + chip = devm_pwmchip_alloc(pdev->dev.parent, MAX7360_NUM_PWMS, + sizeof(*max7360_pwm)); + if (IS_ERR(chip)) + return PTR_ERR(chip); + chip->ops = &max7360_pwm_ops; + + max7360_pwm = to_max7360_pwm(chip); + max7360_pwm->parent = pdev->dev.parent; + + max7360_pwm->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!max7360_pwm->regmap) + return dev_err_probe(&pdev->dev, -ENODEV, + "could not get parent regmap\n"); + + ret = devm_pwmchip_add(&pdev->dev, chip); + if (ret != 0) + return dev_err_probe(&pdev->dev, ret, + "failed to add PWM chip\n"); + + return 0; +} + +static struct platform_driver max7360_pwm_driver = { + .driver = { + .name = MAX7360_DRVNAME_PWM, + }, + .probe = max7360_pwm_probe, +}; +module_platform_driver(max7360_pwm_driver); + +MODULE_DESCRIPTION("MAX7360 PWM driver"); +MODULE_AUTHOR("Kamel BOUHARA <kamel.bouhara@bootlin.com>"); +MODULE_ALIAS("platform:" MAX7360_DRVNAME_PWM); +MODULE_LICENSE("GPL");