Merge tag 'pwm/for-5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 27 Sep 2019 19:19:47 +0000 (12:19 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 27 Sep 2019 19:19:47 +0000 (12:19 -0700)
Pull pwm updates from Thierry Reding:
 "Besides one new driver being added for the PWM controller found in
  various Spreadtrum SoCs, this series of changes brings a slew of,
  mostly minor, fixes and cleanups for existing drivers, as well as some
  enhancements to the core code.

  Lastly, Uwe is added to the PWM subsystem entry of the MAINTAINERS
  file, making official his role as a reviewer"

* tag 'pwm/for-5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm: (34 commits)
  MAINTAINERS: Add myself as reviewer for the PWM subsystem
  MAINTAINERS: Add patchwork link for PWM entry
  MAINTAINERS: Add a selection of PWM related keywords to the PWM entry
  pwm: mediatek: Add MT7629 compatible string
  dt-bindings: pwm: Update bindings for MT7629 SoC
  pwm: mediatek: Update license and switch to SPDX tag
  pwm: mediatek: Use pwm_mediatek as common prefix
  pwm: mediatek: Allocate the clks array dynamically
  pwm: mediatek: Remove the has_clks field
  pwm: mediatek: Drop the check for of_device_get_match_data()
  pwm: atmel: Consolidate driver data initialization
  pwm: atmel: Remove unneeded check for match data
  pwm: atmel: Remove platform_device_id and use only dt bindings
  pwm: stm32-lp: Add check in case requested period cannot be achieved
  pwm: Ensure pwm_apply_state() doesn't modify the state argument
  pwm: fsl-ftm: Don't update the state for the caller of pwm_apply_state()
  pwm: sun4i: Don't update the state for the caller of pwm_apply_state()
  pwm: rockchip: Don't update the state for the caller of pwm_apply_state()
  pwm: Let pwm_get_state() return the last implemented state
  pwm: Introduce local struct pwm_chip in pwm_apply_state()
  ...

31 files changed:
Documentation/devicetree/bindings/pwm/pwm-mediatek.txt
Documentation/devicetree/bindings/pwm/pwm-sprd.txt [new file with mode: 0644]
MAINTAINERS
drivers/gpio/gpio-mvebu.c
drivers/pwm/Kconfig
drivers/pwm/Makefile
drivers/pwm/core.c
drivers/pwm/pwm-atmel-hlcdc.c
drivers/pwm/pwm-atmel.c
drivers/pwm/pwm-bcm-iproc.c
drivers/pwm/pwm-bcm2835.c
drivers/pwm/pwm-cros-ec.c
drivers/pwm/pwm-fsl-ftm.c
drivers/pwm/pwm-hibvt.c
drivers/pwm/pwm-imx-tpm.c
drivers/pwm/pwm-imx27.c
drivers/pwm/pwm-jz4740.c
drivers/pwm/pwm-lpss.c
drivers/pwm/pwm-mediatek.c
drivers/pwm/pwm-meson.c
drivers/pwm/pwm-mxs.c
drivers/pwm/pwm-rcar.c
drivers/pwm/pwm-rockchip.c
drivers/pwm/pwm-sifive.c
drivers/pwm/pwm-sprd.c [new file with mode: 0644]
drivers/pwm/pwm-sti.c
drivers/pwm/pwm-stm32-lp.c
drivers/pwm/pwm-stm32.c
drivers/pwm/pwm-sun4i.c
drivers/pwm/pwm-zx.c
include/linux/pwm.h

index 991728cb46cb8cf24a2793ecc1ec8a417aa2df0b..c8501530173ca5440ef02386b2472e01222023df 100644 (file)
@@ -6,6 +6,8 @@ Required properties:
    - "mediatek,mt7622-pwm": found on mt7622 SoC.
    - "mediatek,mt7623-pwm": found on mt7623 SoC.
    - "mediatek,mt7628-pwm": found on mt7628 SoC.
+   - "mediatek,mt7629-pwm", "mediatek,mt7622-pwm": found on mt7629 SoC.
+   - "mediatek,mt8516-pwm": found on mt8516 SoC.
  - reg: physical base address and length of the controller's registers.
  - #pwm-cells: must be 2. See pwm.txt in this directory for a description of
    the cell format.
diff --git a/Documentation/devicetree/bindings/pwm/pwm-sprd.txt b/Documentation/devicetree/bindings/pwm/pwm-sprd.txt
new file mode 100644 (file)
index 0000000..16fa5a0
--- /dev/null
@@ -0,0 +1,40 @@
+Spreadtrum PWM controller
+
+Spreadtrum SoCs PWM controller provides 4 PWM channels.
+
+Required properties:
+- compatible : Should be "sprd,ums512-pwm".
+- reg: Physical base address and length of the controller's registers.
+- clocks: The phandle and specifier referencing the controller's clocks.
+- clock-names: Should contain following entries:
+  "pwmn": used to derive the functional clock for PWM channel n (n range: 0 ~ 3).
+  "enablen": for PWM channel n enable clock (n range: 0 ~ 3).
+- #pwm-cells: Should be 2. See pwm.txt in this directory for a description of
+  the cells format.
+
+Optional properties:
+- assigned-clocks: Reference to the PWM clock entries.
+- assigned-clock-parents: The phandle of the parent clock of PWM clock.
+
+Example:
+       pwms: pwm@32260000 {
+               compatible = "sprd,ums512-pwm";
+               reg = <0 0x32260000 0 0x10000>;
+               clock-names = "pwm0", "enable0",
+                       "pwm1", "enable1",
+                       "pwm2", "enable2",
+                       "pwm3", "enable3";
+               clocks = <&aon_clk CLK_PWM0>, <&aonapb_gate CLK_PWM0_EB>,
+                      <&aon_clk CLK_PWM1>, <&aonapb_gate CLK_PWM1_EB>,
+                      <&aon_clk CLK_PWM2>, <&aonapb_gate CLK_PWM2_EB>,
+                      <&aon_clk CLK_PWM3>, <&aonapb_gate CLK_PWM3_EB>;
+               assigned-clocks = <&aon_clk CLK_PWM0>,
+                       <&aon_clk CLK_PWM1>,
+                       <&aon_clk CLK_PWM2>,
+                       <&aon_clk CLK_PWM3>;
+               assigned-clock-parents = <&ext_26m>,
+                       <&ext_26m>,
+                       <&ext_26m>,
+                       <&ext_26m>;
+               #pwm-cells = <2>;
+       };
index ee3971d8d3354f829b197f7d7f31b2d1443ac66f..f6ff768646496e93dc094c186970dd8f7e0349e2 100644 (file)
@@ -13246,9 +13246,11 @@ F:     drivers/media/rc/pwm-ir-tx.c
 
 PWM SUBSYSTEM
 M:     Thierry Reding <thierry.reding@gmail.com>
+R:     Uwe Kleine-K├Ânig <u.kleine-koenig@pengutronix.de>
 L:     linux-pwm@vger.kernel.org
 S:     Maintained
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm.git
+Q:     https://patchwork.ozlabs.org/project/linux-pwm/list/
 F:     Documentation/driver-api/pwm.rst
 F:     Documentation/devicetree/bindings/pwm/
 F:     include/linux/pwm.h
@@ -13257,6 +13259,7 @@ F:      drivers/video/backlight/pwm_bl.c
 F:     include/linux/pwm_backlight.h
 F:     drivers/gpio/gpio-mvebu.c
 F:     Documentation/devicetree/bindings/gpio/gpio-mvebu.txt
+K:     pwm_(config|apply_state|ops)
 
 PXA GPIO DRIVER
 M:     Robert Jarzmik <robert.jarzmik@free.fr>
index 869d47f895996efe4e73c78bc8e7dfe38df3668b..6c06876943412bc974999e247d6ffcd91301a7a3 100644 (file)
@@ -694,7 +694,7 @@ static void mvebu_pwm_get_state(struct pwm_chip *chip,
 }
 
 static int mvebu_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
-                          struct pwm_state *state)
+                          const struct pwm_state *state)
 {
        struct mvebu_pwm *mvpwm = to_mvebu_pwm(chip);
        struct mvebu_gpio_chip *mvchip = mvpwm->mvchip;
index b0e632ba85908d47ac661741e4fa61d8e31c1dc0..e3a2518503ed3ca769545dfe25df839204562b28 100644 (file)
@@ -44,7 +44,7 @@ config PWM_AB8500
 
 config PWM_ATMEL
        tristate "Atmel PWM support"
-       depends on ARCH_AT91
+       depends on ARCH_AT91 && OF
        help
          Generic PWM framework driver for Atmel SoC.
 
@@ -423,6 +423,17 @@ config PWM_SPEAR
          To compile this driver as a module, choose M here: the module
          will be called pwm-spear.
 
+config PWM_SPRD
+       tristate "Spreadtrum PWM support"
+       depends on ARCH_SPRD || COMPILE_TEST
+       depends on HAS_IOMEM
+       help
+         Generic PWM framework driver for the PWM controller on
+         Spreadtrum SoCs.
+
+         To compile this driver as a module, choose M here: the module
+         will be called pwm-sprd.
+
 config PWM_STI
        tristate "STiH4xx PWM support"
        depends on ARCH_STI
index 76b555b518878d2731f376a917b551e2125879bf..26326adf71d7bd088365ba2672923fb18302c2bb 100644 (file)
@@ -41,6 +41,7 @@ obj-$(CONFIG_PWM_ROCKCHIP)    += pwm-rockchip.o
 obj-$(CONFIG_PWM_SAMSUNG)      += pwm-samsung.o
 obj-$(CONFIG_PWM_SIFIVE)       += pwm-sifive.o
 obj-$(CONFIG_PWM_SPEAR)                += pwm-spear.o
+obj-$(CONFIG_PWM_SPRD)         += pwm-sprd.o
 obj-$(CONFIG_PWM_STI)          += pwm-sti.o
 obj-$(CONFIG_PWM_STM32)                += pwm-stm32.o
 obj-$(CONFIG_PWM_STM32_LP)     += pwm-stm32-lp.o
index 8edfac17364e9141ba4fd7fed8d1a3e0f4df6d1a..6ad51aa60c03dd1cf8afd00410a53a459f4d0a35 100644 (file)
@@ -448,36 +448,44 @@ EXPORT_SYMBOL_GPL(pwm_free);
 /**
  * pwm_apply_state() - atomically apply a new state to a PWM device
  * @pwm: PWM device
- * @state: new state to apply. This can be adjusted by the PWM driver
- *        if the requested config is not achievable, for example,
- *        ->duty_cycle and ->period might be approximated.
+ * @state: new state to apply
  */
-int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state)
+int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state)
 {
+       struct pwm_chip *chip;
        int err;
 
        if (!pwm || !state || !state->period ||
            state->duty_cycle > state->period)
                return -EINVAL;
 
+       chip = pwm->chip;
+
        if (state->period == pwm->state.period &&
            state->duty_cycle == pwm->state.duty_cycle &&
            state->polarity == pwm->state.polarity &&
            state->enabled == pwm->state.enabled)
                return 0;
 
-       if (pwm->chip->ops->apply) {
-               err = pwm->chip->ops->apply(pwm->chip, pwm, state);
+       if (chip->ops->apply) {
+               err = chip->ops->apply(chip, pwm, state);
                if (err)
                        return err;
 
-               pwm->state = *state;
+               /*
+                * .apply might have to round some values in *state, if possible
+                * read the actually implemented value back.
+                */
+               if (chip->ops->get_state)
+                       chip->ops->get_state(chip, pwm, &pwm->state);
+               else
+                       pwm->state = *state;
        } else {
                /*
                 * FIXME: restore the initial state in case of error.
                 */
                if (state->polarity != pwm->state.polarity) {
-                       if (!pwm->chip->ops->set_polarity)
+                       if (!chip->ops->set_polarity)
                                return -ENOTSUPP;
 
                        /*
@@ -486,12 +494,12 @@ int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state)
                         * ->apply().
                         */
                        if (pwm->state.enabled) {
-                               pwm->chip->ops->disable(pwm->chip, pwm);
+                               chip->ops->disable(chip, pwm);
                                pwm->state.enabled = false;
                        }
 
-                       err = pwm->chip->ops->set_polarity(pwm->chip, pwm,
-                                                          state->polarity);
+                       err = chip->ops->set_polarity(chip, pwm,
+                                                     state->polarity);
                        if (err)
                                return err;
 
@@ -500,9 +508,9 @@ int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state)
 
                if (state->period != pwm->state.period ||
                    state->duty_cycle != pwm->state.duty_cycle) {
-                       err = pwm->chip->ops->config(pwm->chip, pwm,
-                                                    state->duty_cycle,
-                                                    state->period);
+                       err = chip->ops->config(pwm->chip, pwm,
+                                               state->duty_cycle,
+                                               state->period);
                        if (err)
                                return err;
 
@@ -512,11 +520,11 @@ int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state)
 
                if (state->enabled != pwm->state.enabled) {
                        if (state->enabled) {
-                               err = pwm->chip->ops->enable(pwm->chip, pwm);
+                               err = chip->ops->enable(chip, pwm);
                                if (err)
                                        return err;
                        } else {
-                               pwm->chip->ops->disable(pwm->chip, pwm);
+                               chip->ops->disable(chip, pwm);
                        }
 
                        pwm->state.enabled = state->enabled;
index d13a83f430ace7a5ca1010285bdd24018fa65214..dcbc0489dfd401c5aad3032bafb8057748c7d4dc 100644 (file)
@@ -39,7 +39,7 @@ static inline struct atmel_hlcdc_pwm *to_atmel_hlcdc_pwm(struct pwm_chip *chip)
 }
 
 static int atmel_hlcdc_pwm_apply(struct pwm_chip *c, struct pwm_device *pwm,
-                                struct pwm_state *state)
+                                const struct pwm_state *state)
 {
        struct atmel_hlcdc_pwm *chip = to_atmel_hlcdc_pwm(c);
        struct atmel_hlcdc *hlcdc = chip->hlcdc;
index e5e1eaf372fa7a6e96912ad27ebae9b38bce0bcd..9ba733467e262c543053d5f0040874abf872baca 100644 (file)
@@ -209,7 +209,7 @@ static void atmel_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm,
 }
 
 static int atmel_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
-                          struct pwm_state *state)
+                          const struct pwm_state *state)
 {
        struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip);
        struct pwm_state cstate;
@@ -318,19 +318,6 @@ static const struct atmel_pwm_data mchp_sam9x60_pwm_data = {
        },
 };
 
-static const struct platform_device_id atmel_pwm_devtypes[] = {
-       {
-               .name = "at91sam9rl-pwm",
-               .driver_data = (kernel_ulong_t)&atmel_sam9rl_pwm_data,
-       }, {
-               .name = "sama5d3-pwm",
-               .driver_data = (kernel_ulong_t)&atmel_sama5_pwm_data,
-       }, {
-               /* sentinel */
-       },
-};
-MODULE_DEVICE_TABLE(platform, atmel_pwm_devtypes);
-
 static const struct of_device_id atmel_pwm_dt_ids[] = {
        {
                .compatible = "atmel,at91sam9rl-pwm",
@@ -350,34 +337,20 @@ static const struct of_device_id atmel_pwm_dt_ids[] = {
 };
 MODULE_DEVICE_TABLE(of, atmel_pwm_dt_ids);
 
-static inline const struct atmel_pwm_data *
-atmel_pwm_get_driver_data(struct platform_device *pdev)
-{
-       const struct platform_device_id *id;
-
-       if (pdev->dev.of_node)
-               return of_device_get_match_data(&pdev->dev);
-
-       id = platform_get_device_id(pdev);
-
-       return (struct atmel_pwm_data *)id->driver_data;
-}
-
 static int atmel_pwm_probe(struct platform_device *pdev)
 {
-       const struct atmel_pwm_data *data;
        struct atmel_pwm_chip *atmel_pwm;
        struct resource *res;
        int ret;
 
-       data = atmel_pwm_get_driver_data(pdev);
-       if (!data)
-               return -ENODEV;
-
        atmel_pwm = devm_kzalloc(&pdev->dev, sizeof(*atmel_pwm), GFP_KERNEL);
        if (!atmel_pwm)
                return -ENOMEM;
 
+       mutex_init(&atmel_pwm->isr_lock);
+       atmel_pwm->data = of_device_get_match_data(&pdev->dev);
+       atmel_pwm->updated_pwms = 0;
+
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        atmel_pwm->base = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(atmel_pwm->base))
@@ -395,17 +368,10 @@ static int atmel_pwm_probe(struct platform_device *pdev)
 
        atmel_pwm->chip.dev = &pdev->dev;
        atmel_pwm->chip.ops = &atmel_pwm_ops;
-
-       if (pdev->dev.of_node) {
-               atmel_pwm->chip.of_xlate = of_pwm_xlate_with_flags;
-               atmel_pwm->chip.of_pwm_n_cells = 3;
-       }
-
+       atmel_pwm->chip.of_xlate = of_pwm_xlate_with_flags;
+       atmel_pwm->chip.of_pwm_n_cells = 3;
        atmel_pwm->chip.base = -1;
        atmel_pwm->chip.npwm = 4;
-       atmel_pwm->data = data;
-       atmel_pwm->updated_pwms = 0;
-       mutex_init(&atmel_pwm->isr_lock);
 
        ret = pwmchip_add(&atmel_pwm->chip);
        if (ret < 0) {
@@ -437,7 +403,6 @@ static struct platform_driver atmel_pwm_driver = {
                .name = "atmel-pwm",
                .of_match_table = of_match_ptr(atmel_pwm_dt_ids),
        },
-       .id_table = atmel_pwm_devtypes,
        .probe = atmel_pwm_probe,
        .remove = atmel_pwm_remove,
 };
index d961a8207b1cbe660cfb6f38d21b7383f1834847..56c38cfae92ca9285d69e6bd6596cd80d95fcae7 100644 (file)
@@ -115,7 +115,7 @@ static void iproc_pwmc_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
 }
 
 static int iproc_pwmc_apply(struct pwm_chip *chip, struct pwm_device *pwm,
-                           struct pwm_state *state)
+                           const struct pwm_state *state)
 {
        unsigned long prescale = IPROC_PWM_PRESCALE_MIN;
        struct iproc_pwmc *ip = to_iproc_pwmc(chip);
index f6fe0b922e1e8685418c08cfe26e53c61c7f4df0..91e24f01b54ed0e0285414194a2ddd8049f8e7a5 100644 (file)
@@ -21,7 +21,7 @@
 #define PERIOD(x)              (((x) * 0x10) + 0x10)
 #define DUTY(x)                        (((x) * 0x10) + 0x14)
 
-#define MIN_PERIOD             108             /* 9.2 MHz max. PWM clock */
+#define PERIOD_MIN             0x2
 
 struct bcm2835_pwm {
        struct pwm_chip chip;
@@ -64,6 +64,7 @@ static int bcm2835_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
        struct bcm2835_pwm *pc = to_bcm2835_pwm(chip);
        unsigned long rate = clk_get_rate(pc->clk);
        unsigned long scaler;
+       u32 period;
 
        if (!rate) {
                dev_err(pc->dev, "failed to get clock rate\n");
@@ -71,17 +72,14 @@ static int bcm2835_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
        }
 
        scaler = DIV_ROUND_CLOSEST(NSEC_PER_SEC, rate);
+       period = DIV_ROUND_CLOSEST(period_ns, scaler);
 
-       if (period_ns <= MIN_PERIOD) {
-               dev_err(pc->dev, "period %d not supported, minimum %d\n",
-                       period_ns, MIN_PERIOD);
+       if (period < PERIOD_MIN)
                return -EINVAL;
-       }
 
        writel(DIV_ROUND_CLOSEST(duty_ns, scaler),
               pc->base + DUTY(pwm->hwpwm));
-       writel(DIV_ROUND_CLOSEST(period_ns, scaler),
-              pc->base + PERIOD(pwm->hwpwm));
+       writel(period, pc->base + PERIOD(pwm->hwpwm));
 
        return 0;
 }
@@ -155,8 +153,11 @@ static int bcm2835_pwm_probe(struct platform_device *pdev)
 
        pc->clk = devm_clk_get(&pdev->dev, NULL);
        if (IS_ERR(pc->clk)) {
-               dev_err(&pdev->dev, "clock not found: %ld\n", PTR_ERR(pc->clk));
-               return PTR_ERR(pc->clk);
+               ret = PTR_ERR(pc->clk);
+               if (ret != -EPROBE_DEFER)
+                       dev_err(&pdev->dev, "clock not found: %d\n", ret);
+
+               return ret;
        }
 
        ret = clk_prepare_enable(pc->clk);
index 85bea2d40b7d2610d1e65f82059eab37ef5d43a3..89497448d2177597c1955b9cfdb7ac3d489bc83a 100644 (file)
@@ -93,7 +93,7 @@ static int cros_ec_pwm_get_duty(struct cros_ec_device *ec, u8 index)
 }
 
 static int cros_ec_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
-                            struct pwm_state *state)
+                            const struct pwm_state *state)
 {
        struct cros_ec_pwm_device *ec_pwm = pwm_to_cros_ec_pwm(chip);
        int duty_cycle;
index 9d31a217111d03f3fb41783e11eab84c24fb4b03..59272a92047933b3c87b12f392ab3e9fa04d40bc 100644 (file)
@@ -227,7 +227,7 @@ static bool fsl_pwm_is_other_pwm_enabled(struct fsl_pwm_chip *fpc,
 
 static int fsl_pwm_apply_config(struct fsl_pwm_chip *fpc,
                                struct pwm_device *pwm,
-                               struct pwm_state *newstate)
+                               const struct pwm_state *newstate)
 {
        unsigned int duty;
        u32 reg_polarity;
@@ -292,17 +292,13 @@ static int fsl_pwm_apply_config(struct fsl_pwm_chip *fpc,
 
        regmap_update_bits(fpc->regmap, FTM_POL, BIT(pwm->hwpwm), reg_polarity);
 
-       newstate->period = fsl_pwm_ticks_to_ns(fpc,
-                                              fpc->period.mod_period + 1);
-       newstate->duty_cycle = fsl_pwm_ticks_to_ns(fpc, duty);
-
        ftm_set_write_protection(fpc);
 
        return 0;
 }
 
 static int fsl_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
-                        struct pwm_state *newstate)
+                        const struct pwm_state *newstate)
 {
        struct fsl_pwm_chip *fpc = to_fsl_chip(chip);
        struct pwm_state *oldstate = &pwm->state;
index 753bd58111e4658258d448ad0df9671f79efc126..ad205fdad3722d46193c3a6a5099cd2878cf127a 100644 (file)
@@ -149,7 +149,7 @@ static void hibvt_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
 }
 
 static int hibvt_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
-                               struct pwm_state *state)
+                          const struct pwm_state *state)
 {
        struct hibvt_pwm_chip *hi_pwm_chip = to_hibvt_pwm_chip(chip);
 
index e8385c1cf34280dce0f280a66a1058ff53e1e15b..9145f61606497cb0aa90aafd9e903c26e83a1374 100644 (file)
@@ -89,7 +89,7 @@ to_imx_tpm_pwm_chip(struct pwm_chip *chip)
 static int pwm_imx_tpm_round_state(struct pwm_chip *chip,
                                   struct imx_tpm_pwm_param *p,
                                   struct pwm_state *real_state,
-                                  struct pwm_state *state)
+                                  const struct pwm_state *state)
 {
        struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip);
        u32 rate, prescale, period_count, clock_unit;
@@ -289,7 +289,7 @@ static int pwm_imx_tpm_apply_hw(struct pwm_chip *chip,
 
 static int pwm_imx_tpm_apply(struct pwm_chip *chip,
                             struct pwm_device *pwm,
-                            struct pwm_state *state)
+                            const struct pwm_state *state)
 {
        struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip);
        struct imx_tpm_pwm_param param;
index 434a351fb626be9a790f1a8cafa1e77595bd9dfd..ae11d8577f1866030173c8e47d513d12da590aae 100644 (file)
@@ -3,6 +3,10 @@
  * simple driver for PWM (Pulse Width Modulator) controller
  *
  * Derived from pxa PWM driver by eric miao <eric.miao@marvell.com>
+ *
+ * Limitations:
+ * - When disabled the output is driven to 0 independent of the configured
+ *   polarity.
  */
 
 #include <linux/bitfield.h>
@@ -205,7 +209,7 @@ static void pwm_imx27_wait_fifo_slot(struct pwm_chip *chip,
 }
 
 static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm,
-                          struct pwm_state *state)
+                          const struct pwm_state *state)
 {
        unsigned long period_cycles, duty_cycles, prescale;
        struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
index f901e8a0d33dc2d86a25f6f46655de80e4fe1f86..9d78cc21cb12793bca321c6e941d607930e362cd 100644 (file)
@@ -2,6 +2,11 @@
 /*
  *  Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
  *  JZ4740 platform PWM support
+ *
+ * Limitations:
+ * - The .apply callback doesn't complete the currently running period before
+ *   reconfiguring the hardware.
+ * - Each period starts with the inactive part.
  */
 
 #include <linux/clk.h>
@@ -83,7 +88,7 @@ static void jz4740_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 }
 
 static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
-                           struct pwm_state *state)
+                           const struct pwm_state *state)
 {
        struct jz4740_pwm_chip *jz4740 = to_jz4740(pwm->chip);
        unsigned long long tmp;
index 4098a4601691b048b3304f33814ba3d59ebfc6b4..75bbfe5f3bc2975479b1a27ade8eca58bab05455 100644 (file)
@@ -122,7 +122,7 @@ static inline void pwm_lpss_cond_enable(struct pwm_device *pwm, bool cond)
 }
 
 static int pwm_lpss_apply(struct pwm_chip *chip, struct pwm_device *pwm,
-                         struct pwm_state *state)
+                         const struct pwm_state *state)
 {
        struct pwm_lpss_chip *lpwm = to_lpwm(chip);
        int ret;
index eb6674ce995ff1bda4da866cd06edb553f78a59a..b94e0d09c300f8bdc2cb23f85c39223d2968948f 100644 (file)
@@ -1,12 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
- * Mediatek Pulse Width Modulator driver
+ * MediaTek Pulse Width Modulator driver
  *
  * Copyright (C) 2015 John Crispin <blogic@openwrt.org>
  * Copyright (C) 2017 Zhi Mao <zhi.mao@mediatek.com>
  *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
  */
 
 #include <linux/err.h>
 
 #define PWM_CLK_DIV_MAX                7
 
-enum {
-       MTK_CLK_MAIN = 0,
-       MTK_CLK_TOP,
-       MTK_CLK_PWM1,
-       MTK_CLK_PWM2,
-       MTK_CLK_PWM3,
-       MTK_CLK_PWM4,
-       MTK_CLK_PWM5,
-       MTK_CLK_PWM6,
-       MTK_CLK_PWM7,
-       MTK_CLK_PWM8,
-       MTK_CLK_MAX,
-};
-
-static const char * const mtk_pwm_clk_name[MTK_CLK_MAX] = {
-       "main", "top", "pwm1", "pwm2", "pwm3", "pwm4", "pwm5", "pwm6", "pwm7",
-       "pwm8"
-};
-
-struct mtk_pwm_platform_data {
+struct pwm_mediatek_of_data {
        unsigned int num_pwms;
        bool pwm45_fixup;
-       bool has_clks;
 };
 
 /**
- * struct mtk_pwm_chip - struct representing PWM chip
+ * struct pwm_mediatek_chip - struct representing PWM chip
  * @chip: linux PWM chip representation
  * @regs: base address of PWM chip
- * @clks: list of clocks
+ * @clk_top: the top clock generator
+ * @clk_main: the clock used by PWM core
+ * @clk_pwms: the clock used by each PWM channel
+ * @clk_freq: the fix clock frequency of legacy MIPS SoC
  */
-struct mtk_pwm_chip {
+struct pwm_mediatek_chip {
        struct pwm_chip chip;
        void __iomem *regs;
-       struct clk *clks[MTK_CLK_MAX];
-       const struct mtk_pwm_platform_data *soc;
+       struct clk *clk_top;
+       struct clk *clk_main;
+       struct clk **clk_pwms;
+       const struct pwm_mediatek_of_data *soc;
 };
 
-static const unsigned int mtk_pwm_reg_offset[] = {
+static const unsigned int pwm_mediatek_reg_offset[] = {
        0x0010, 0x0050, 0x0090, 0x00d0, 0x0110, 0x0150, 0x0190, 0x0220
 };
 
-static inline struct mtk_pwm_chip *to_mtk_pwm_chip(struct pwm_chip *chip)
+static inline struct pwm_mediatek_chip *
+to_pwm_mediatek_chip(struct pwm_chip *chip)
 {
-       return container_of(chip, struct mtk_pwm_chip, chip);
+       return container_of(chip, struct pwm_mediatek_chip, chip);
 }
 
-static int mtk_pwm_clk_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+static int pwm_mediatek_clk_enable(struct pwm_chip *chip,
+                                  struct pwm_device *pwm)
 {
-       struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip);
+       struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
        int ret;
 
-       if (!pc->soc->has_clks)
-               return 0;
-
-       ret = clk_prepare_enable(pc->clks[MTK_CLK_TOP]);
+       ret = clk_prepare_enable(pc->clk_top);
        if (ret < 0)
                return ret;
 
-       ret = clk_prepare_enable(pc->clks[MTK_CLK_MAIN]);
+       ret = clk_prepare_enable(pc->clk_main);
        if (ret < 0)
                goto disable_clk_top;
 
-       ret = clk_prepare_enable(pc->clks[MTK_CLK_PWM1 + pwm->hwpwm]);
+       ret = clk_prepare_enable(pc->clk_pwms[pwm->hwpwm]);
        if (ret < 0)
                goto disable_clk_main;
 
        return 0;
 
 disable_clk_main:
-       clk_disable_unprepare(pc->clks[MTK_CLK_MAIN]);
+       clk_disable_unprepare(pc->clk_main);
 disable_clk_top:
-       clk_disable_unprepare(pc->clks[MTK_CLK_TOP]);
+       clk_disable_unprepare(pc->clk_top);
 
        return ret;
 }
 
-static void mtk_pwm_clk_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+static void pwm_mediatek_clk_disable(struct pwm_chip *chip,
+                                    struct pwm_device *pwm)
 {
-       struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip);
-
-       if (!pc->soc->has_clks)
-               return;
+       struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
 
-       clk_disable_unprepare(pc->clks[MTK_CLK_PWM1 + pwm->hwpwm]);
-       clk_disable_unprepare(pc->clks[MTK_CLK_MAIN]);
-       clk_disable_unprepare(pc->clks[MTK_CLK_TOP]);
+       clk_disable_unprepare(pc->clk_pwms[pwm->hwpwm]);
+       clk_disable_unprepare(pc->clk_main);
+       clk_disable_unprepare(pc->clk_top);
 }
 
-static inline u32 mtk_pwm_readl(struct mtk_pwm_chip *chip, unsigned int num,
-                               unsigned int offset)
+static inline u32 pwm_mediatek_readl(struct pwm_mediatek_chip *chip,
+                                    unsigned int num, unsigned int offset)
 {
-       return readl(chip->regs + mtk_pwm_reg_offset[num] + offset);
+       return readl(chip->regs + pwm_mediatek_reg_offset[num] + offset);
 }
 
-static inline void mtk_pwm_writel(struct mtk_pwm_chip *chip,
-                                 unsigned int num, unsigned int offset,
-                                 u32 value)
+static inline void pwm_mediatek_writel(struct pwm_mediatek_chip *chip,
+                                      unsigned int num, unsigned int offset,
+                                      u32 value)
 {
-       writel(value, chip->regs + mtk_pwm_reg_offset[num] + offset);
+       writel(value, chip->regs + pwm_mediatek_reg_offset[num] + offset);
 }
 
-static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
-                         int duty_ns, int period_ns)
+static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm,
+                              int duty_ns, int period_ns)
 {
-       struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip);
-       struct clk *clk = pc->clks[MTK_CLK_PWM1 + pwm->hwpwm];
+       struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
        u32 clkdiv = 0, cnt_period, cnt_duty, reg_width = PWMDWIDTH,
            reg_thres = PWMTHRES;
        u64 resolution;
        int ret;
 
-       ret = mtk_pwm_clk_enable(chip, pwm);
+       ret = pwm_mediatek_clk_enable(chip, pwm);
+
        if (ret < 0)
                return ret;
 
        /* Using resolution in picosecond gets accuracy higher */
        resolution = (u64)NSEC_PER_SEC * 1000;
-       do_div(resolution, clk_get_rate(clk));
+       do_div(resolution, clk_get_rate(pc->clk_pwms[pwm->hwpwm]));
 
        cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, resolution);
        while (cnt_period > 8191) {
@@ -164,7 +144,7 @@ static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
        }
 
        if (clkdiv > PWM_CLK_DIV_MAX) {
-               mtk_pwm_clk_disable(chip, pwm);
+               pwm_mediatek_clk_disable(chip, pwm);
                dev_err(chip->dev, "period %d not supported\n", period_ns);
                return -EINVAL;
        }
@@ -179,22 +159,22 @@ static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
        }
 
        cnt_duty = DIV_ROUND_CLOSEST_ULL((u64)duty_ns * 1000, resolution);
-       mtk_pwm_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv);
-       mtk_pwm_writel(pc, pwm->hwpwm, reg_width, cnt_period);
-       mtk_pwm_writel(pc, pwm->hwpwm, reg_thres, cnt_duty);
+       pwm_mediatek_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv);
+       pwm_mediatek_writel(pc, pwm->hwpwm, reg_width, cnt_period);
+       pwm_mediatek_writel(pc, pwm->hwpwm, reg_thres, cnt_duty);
 
-       mtk_pwm_clk_disable(chip, pwm);
+       pwm_mediatek_clk_disable(chip, pwm);
 
        return 0;
 }
 
-static int mtk_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
+static int pwm_mediatek_enable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-       struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip);
+       struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
        u32 value;
        int ret;
 
-       ret = mtk_pwm_clk_enable(chip, pwm);
+       ret = pwm_mediatek_clk_enable(chip, pwm);
        if (ret < 0)
                return ret;
 
@@ -205,29 +185,28 @@ static int mtk_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
        return 0;
 }
 
-static void mtk_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
+static void pwm_mediatek_disable(struct pwm_chip *chip, struct pwm_device *pwm)
 {
-       struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip);
+       struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
        u32 value;
 
        value = readl(pc->regs);
        value &= ~BIT(pwm->hwpwm);
        writel(value, pc->regs);
 
-       mtk_pwm_clk_disable(chip, pwm);
+       pwm_mediatek_clk_disable(chip, pwm);
 }
 
-static const struct pwm_ops mtk_pwm_ops = {
-       .config = mtk_pwm_config,
-       .enable = mtk_pwm_enable,
-       .disable = mtk_pwm_disable,
+static const struct pwm_ops pwm_mediatek_ops = {
+       .config = pwm_mediatek_config,
+       .enable = pwm_mediatek_enable,
+       .disable = pwm_mediatek_disable,
        .owner = THIS_MODULE,
 };
 
-static int mtk_pwm_probe(struct platform_device *pdev)
+static int pwm_mediatek_probe(struct platform_device *pdev)
 {
-       const struct mtk_pwm_platform_data *data;
-       struct mtk_pwm_chip *pc;
+       struct pwm_mediatek_chip *pc;
        struct resource *res;
        unsigned int i;
        int ret;
@@ -236,31 +215,51 @@ static int mtk_pwm_probe(struct platform_device *pdev)
        if (!pc)
                return -ENOMEM;
 
-       data = of_device_get_match_data(&pdev->dev);
-       if (data == NULL)
-               return -EINVAL;
-       pc->soc = data;
+       pc->soc = of_device_get_match_data(&pdev->dev);
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        pc->regs = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(pc->regs))
                return PTR_ERR(pc->regs);
 
-       for (i = 0; i < data->num_pwms + 2 && pc->soc->has_clks; i++) {
-               pc->clks[i] = devm_clk_get(&pdev->dev, mtk_pwm_clk_name[i]);
-               if (IS_ERR(pc->clks[i])) {
+       pc->clk_pwms = devm_kcalloc(&pdev->dev, pc->soc->num_pwms,
+                                   sizeof(*pc->clk_pwms), GFP_KERNEL);
+       if (!pc->clk_pwms)
+               return -ENOMEM;
+
+       pc->clk_top = devm_clk_get(&pdev->dev, "top");
+       if (IS_ERR(pc->clk_top)) {
+               dev_err(&pdev->dev, "clock: top fail: %ld\n",
+                       PTR_ERR(pc->clk_top));
+               return PTR_ERR(pc->clk_top);
+       }
+
+       pc->clk_main = devm_clk_get(&pdev->dev, "main");
+       if (IS_ERR(pc->clk_main)) {
+               dev_err(&pdev->dev, "clock: main fail: %ld\n",
+                       PTR_ERR(pc->clk_main));
+               return PTR_ERR(pc->clk_main);
+       }
+
+       for (i = 0; i < pc->soc->num_pwms; i++) {
+               char name[8];
+
+               snprintf(name, sizeof(name), "pwm%d", i + 1);
+
+               pc->clk_pwms[i] = devm_clk_get(&pdev->dev, name);
+               if (IS_ERR(pc->clk_pwms[i])) {
                        dev_err(&pdev->dev, "clock: %s fail: %ld\n",
-                               mtk_pwm_clk_name[i], PTR_ERR(pc->clks[i]));
-                       return PTR_ERR(pc->clks[i]);
+                               name, PTR_ERR(pc->clk_pwms[i]));
+                       return PTR_ERR(pc->clk_pwms[i]);
                }
        }
 
        platform_set_drvdata(pdev, pc);
 
        pc->chip.dev = &pdev->dev;
-       pc->chip.ops = &mtk_pwm_ops;
+       pc->chip.ops = &pwm_mediatek_ops;
        pc->chip.base = -1;
-       pc->chip.npwm = data->num_pwms;
+       pc->chip.npwm = pc->soc->num_pwms;
 
        ret = pwmchip_add(&pc->chip);
        if (ret < 0) {
@@ -271,55 +270,63 @@ static int mtk_pwm_probe(struct platform_device *pdev)
        return 0;
 }
 
-static int mtk_pwm_remove(struct platform_device *pdev)
+static int pwm_mediatek_remove(struct platform_device *pdev)
 {
-       struct mtk_pwm_chip *pc = platform_get_drvdata(pdev);
+       struct pwm_mediatek_chip *pc = platform_get_drvdata(pdev);
 
        return pwmchip_remove(&pc->chip);
 }
 
-static const struct mtk_pwm_platform_data mt2712_pwm_data = {
+static const struct pwm_mediatek_of_data mt2712_pwm_data = {
        .num_pwms = 8,
        .pwm45_fixup = false,
-       .has_clks = true,
 };
 
-static const struct mtk_pwm_platform_data mt7622_pwm_data = {
+static const struct pwm_mediatek_of_data mt7622_pwm_data = {
        .num_pwms = 6,
        .pwm45_fixup = false,
-       .has_clks = true,
 };
 
-static const struct mtk_pwm_platform_data mt7623_pwm_data = {
+static const struct pwm_mediatek_of_data mt7623_pwm_data = {
        .num_pwms = 5,
        .pwm45_fixup = true,
-       .has_clks = true,
 };
 
-static const struct mtk_pwm_platform_data mt7628_pwm_data = {
+static const struct pwm_mediatek_of_data mt7628_pwm_data = {
        .num_pwms = 4,
        .pwm45_fixup = true,
-       .has_clks = false,
 };
 
-static const struct of_device_id mtk_pwm_of_match[] = {
+static const struct pwm_mediatek_of_data mt7629_pwm_data = {
+       .num_pwms = 1,
+       .pwm45_fixup = false,
+};
+
+static const struct pwm_mediatek_of_data mt8516_pwm_data = {
+       .num_pwms = 5,
+       .pwm45_fixup = false,
+};
+
+static const struct of_device_id pwm_mediatek_of_match[] = {
        { .compatible = "mediatek,mt2712-pwm", .data = &mt2712_pwm_data },
        { .compatible = "mediatek,mt7622-pwm", .data = &mt7622_pwm_data },
        { .compatible = "mediatek,mt7623-pwm", .data = &mt7623_pwm_data },
        { .compatible = "mediatek,mt7628-pwm", .data = &mt7628_pwm_data },
+       { .compatible = "mediatek,mt7629-pwm", .data = &mt7629_pwm_data },
+       { .compatible = "mediatek,mt8516-pwm", .data = &mt8516_pwm_data },
        { },
 };
-MODULE_DEVICE_TABLE(of, mtk_pwm_of_match);
+MODULE_DEVICE_TABLE(of, pwm_mediatek_of_match);
 
-static struct platform_driver mtk_pwm_driver = {
+static struct platform_driver pwm_mediatek_driver = {
        .driver = {
-               .name = "mtk-pwm",
-               .of_match_table = mtk_pwm_of_match,
+               .name = "pwm-mediatek",
+               .of_match_table = pwm_mediatek_of_match,
        },
-       .probe = mtk_pwm_probe,
-       .remove = mtk_pwm_remove,
+       .probe = pwm_mediatek_probe,
+       .remove = pwm_mediatek_remove,
 };
-module_platform_driver(mtk_pwm_driver);
+module_platform_driver(pwm_mediatek_driver);
 
 MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
index 3cbff5cbb789a5646a51ec0f91717d2d39578ca6..6245bbdb6e6c1fab35dcff18c605bb5f24f38848 100644 (file)
@@ -159,7 +159,7 @@ static void meson_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
 }
 
 static int meson_pwm_calc(struct meson_pwm *meson, struct pwm_device *pwm,
-                         struct pwm_state *state)
+                         const struct pwm_state *state)
 {
        struct meson_pwm_channel *channel = pwm_get_chip_data(pwm);
        unsigned int duty, period, pre_div, cnt, duty_cnt;
@@ -265,7 +265,7 @@ static void meson_pwm_disable(struct meson_pwm *meson, struct pwm_device *pwm)
 }
 
 static int meson_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
-                          struct pwm_state *state)
+                          const struct pwm_state *state)
 {
        struct meson_pwm_channel *channel = pwm_get_chip_data(pwm);
        struct meson_pwm *meson = to_meson_pwm(chip);
index 04c0f6b95c1a6c669dcc191777d01b5e9c698942..b14376b47ac8c1bde7b5330bbd32be24d9c1a183 100644 (file)
@@ -126,15 +126,13 @@ static int mxs_pwm_probe(struct platform_device *pdev)
 {
        struct device_node *np = pdev->dev.of_node;
        struct mxs_pwm_chip *mxs;
-       struct resource *res;
        int ret;
 
        mxs = devm_kzalloc(&pdev->dev, sizeof(*mxs), GFP_KERNEL);
        if (!mxs)
                return -ENOMEM;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       mxs->base = devm_ioremap_resource(&pdev->dev, res);
+       mxs->base = devm_platform_ioremap_resource(pdev, 0);
        if (IS_ERR(mxs->base))
                return PTR_ERR(mxs->base);
 
index 5b2b8ecc354c4bfd03528c414d442f039c274129..852eb2347954d128669a176b0f6ca4fd6a48b84a 100644 (file)
@@ -158,7 +158,7 @@ static void rcar_pwm_disable(struct rcar_pwm_chip *rp)
 }
 
 static int rcar_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
-                         struct pwm_state *state)
+                         const struct pwm_state *state)
 {
        struct rcar_pwm_chip *rp = to_rcar_pwm_chip(chip);
        struct pwm_state cur_state;
@@ -187,7 +187,7 @@ static int rcar_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
        /* The SYNC should be set to 0 even if rcar_pwm_set_counter failed */
        rcar_pwm_update(rp, RCAR_PWMCR_SYNC, 0, RCAR_PWMCR);
 
-       if (!ret && state->enabled)
+       if (!ret)
                ret = rcar_pwm_enable(rp);
 
        return ret;
index 51b96cb7dd252cc3b2df78a1f11a9e0bc4ceb4fe..73352e6fbccbf4040f1d2b866443f4dc4d75387f 100644 (file)
@@ -90,16 +90,16 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip,
                state->enabled = ((val & enable_conf) == enable_conf) ?
                                 true : false;
 
-       if (pc->data->supports_polarity) {
-               if (!(val & PWM_DUTY_POSITIVE))
-                       state->polarity = PWM_POLARITY_INVERSED;
-       }
+       if (pc->data->supports_polarity && !(val & PWM_DUTY_POSITIVE))
+               state->polarity = PWM_POLARITY_INVERSED;
+       else
+               state->polarity = PWM_POLARITY_NORMAL;
 
        clk_disable(pc->pclk);
 }
 
 static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
-                              struct pwm_state *state)
+                              const struct pwm_state *state)
 {
        struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
        unsigned long period, duty;
@@ -183,7 +183,7 @@ static int rockchip_pwm_enable(struct pwm_chip *chip,
 }
 
 static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
-                             struct pwm_state *state)
+                             const struct pwm_state *state)
 {
        struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
        struct pwm_state curstate;
@@ -212,12 +212,6 @@ static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
                        goto out;
        }
 
-       /*
-        * Update the state with the real hardware, which can differ a bit
-        * because of period/duty_cycle approximation.
-        */
-       rockchip_pwm_get_state(chip, pwm, state);
-
 out:
        clk_disable(pc->pclk);
 
index a7c107f19e6631264784108baad21e828c1b686d..cc63f9baa481942f03e8a8228ce0b312ae15be25 100644 (file)
@@ -147,7 +147,7 @@ static int pwm_sifive_enable(struct pwm_chip *chip, bool enable)
 }
 
 static int pwm_sifive_apply(struct pwm_chip *chip, struct pwm_device *pwm,
-                           struct pwm_state *state)
+                           const struct pwm_state *state)
 {
        struct pwm_sifive_ddata *ddata = pwm_sifive_chip_to_ddata(chip);
        struct pwm_state cur_state;
@@ -250,10 +250,8 @@ static int pwm_sifive_probe(struct platform_device *pdev)
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        ddata->regs = devm_ioremap_resource(dev, res);
-       if (IS_ERR(ddata->regs)) {
-               dev_err(dev, "Unable to map IO resources\n");
+       if (IS_ERR(ddata->regs))
                return PTR_ERR(ddata->regs);
-       }
 
        ddata->clk = devm_clk_get(dev, NULL);
        if (IS_ERR(ddata->clk)) {
diff --git a/drivers/pwm/pwm-sprd.c b/drivers/pwm/pwm-sprd.c
new file mode 100644 (file)
index 0000000..be23942
--- /dev/null
@@ -0,0 +1,309 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Spreadtrum Communications Inc.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+
+#define SPRD_PWM_PRESCALE      0x0
+#define SPRD_PWM_MOD           0x4
+#define SPRD_PWM_DUTY          0x8
+#define SPRD_PWM_ENABLE                0x18
+
+#define SPRD_PWM_MOD_MAX       GENMASK(7, 0)
+#define SPRD_PWM_DUTY_MSK      GENMASK(15, 0)
+#define SPRD_PWM_PRESCALE_MSK  GENMASK(7, 0)
+#define SPRD_PWM_ENABLE_BIT    BIT(0)
+
+#define SPRD_PWM_CHN_NUM       4
+#define SPRD_PWM_REGS_SHIFT    5
+#define SPRD_PWM_CHN_CLKS_NUM  2
+#define SPRD_PWM_CHN_OUTPUT_CLK        1
+
+struct sprd_pwm_chn {
+       struct clk_bulk_data clks[SPRD_PWM_CHN_CLKS_NUM];
+       u32 clk_rate;
+};
+
+struct sprd_pwm_chip {
+       void __iomem *base;
+       struct device *dev;
+       struct pwm_chip chip;
+       int num_pwms;
+       struct sprd_pwm_chn chn[SPRD_PWM_CHN_NUM];
+};
+
+/*
+ * The list of clocks required by PWM channels, and each channel has 2 clocks:
+ * enable clock and pwm clock.
+ */
+static const char * const sprd_pwm_clks[] = {
+       "enable0", "pwm0",
+       "enable1", "pwm1",
+       "enable2", "pwm2",
+       "enable3", "pwm3",
+};
+
+static u32 sprd_pwm_read(struct sprd_pwm_chip *spc, u32 hwid, u32 reg)
+{
+       u32 offset = reg + (hwid << SPRD_PWM_REGS_SHIFT);
+
+       return readl_relaxed(spc->base + offset);
+}
+
+static void sprd_pwm_write(struct sprd_pwm_chip *spc, u32 hwid,
+                          u32 reg, u32 val)
+{
+       u32 offset = reg + (hwid << SPRD_PWM_REGS_SHIFT);
+
+       writel_relaxed(val, spc->base + offset);
+}
+
+static void sprd_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+                              struct pwm_state *state)
+{
+       struct sprd_pwm_chip *spc =
+               container_of(chip, struct sprd_pwm_chip, chip);
+       struct sprd_pwm_chn *chn = &spc->chn[pwm->hwpwm];
+       u32 val, duty, prescale;
+       u64 tmp;
+       int ret;
+
+       /*
+        * The clocks to PWM channel has to be enabled first before
+        * reading to the registers.
+        */
+       ret = clk_bulk_prepare_enable(SPRD_PWM_CHN_CLKS_NUM, chn->clks);
+       if (ret) {
+               dev_err(spc->dev, "failed to enable pwm%u clocks\n",
+                       pwm->hwpwm);
+               return;
+       }
+
+       val = sprd_pwm_read(spc, pwm->hwpwm, SPRD_PWM_ENABLE);
+       if (val & SPRD_PWM_ENABLE_BIT)
+               state->enabled = true;
+       else
+               state->enabled = false;
+
+       /*
+        * The hardware provides a counter that is feed by the source clock.
+        * The period length is (PRESCALE + 1) * MOD counter steps.
+        * The duty cycle length is (PRESCALE + 1) * DUTY counter steps.
+        * Thus the period_ns and duty_ns calculation formula should be:
+        * period_ns = NSEC_PER_SEC * (prescale + 1) * mod / clk_rate
+        * duty_ns = NSEC_PER_SEC * (prescale + 1) * duty / clk_rate
+        */
+       val = sprd_pwm_read(spc, pwm->hwpwm, SPRD_PWM_PRESCALE);
+       prescale = val & SPRD_PWM_PRESCALE_MSK;
+       tmp = (prescale + 1) * NSEC_PER_SEC * SPRD_PWM_MOD_MAX;
+       state->period = DIV_ROUND_CLOSEST_ULL(tmp, chn->clk_rate);
+
+       val = sprd_pwm_read(spc, pwm->hwpwm, SPRD_PWM_DUTY);
+       duty = val & SPRD_PWM_DUTY_MSK;
+       tmp = (prescale + 1) * NSEC_PER_SEC * duty;
+       state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, chn->clk_rate);
+
+       /* Disable PWM clocks if the PWM channel is not in enable state. */
+       if (!state->enabled)
+               clk_bulk_disable_unprepare(SPRD_PWM_CHN_CLKS_NUM, chn->clks);
+}
+
+static int sprd_pwm_config(struct sprd_pwm_chip *spc, struct pwm_device *pwm,
+                          int duty_ns, int period_ns)
+{
+       struct sprd_pwm_chn *chn = &spc->chn[pwm->hwpwm];
+       u32 prescale, duty;
+       u64 tmp;
+
+       /*
+        * The hardware provides a counter that is feed by the source clock.
+        * The period length is (PRESCALE + 1) * MOD counter steps.
+        * The duty cycle length is (PRESCALE + 1) * DUTY counter steps.
+        *
+        * To keep the maths simple we're always using MOD = SPRD_PWM_MOD_MAX.
+        * The value for PRESCALE is selected such that the resulting period
+        * gets the maximal length not bigger than the requested one with the
+        * given settings (MOD = SPRD_PWM_MOD_MAX and input clock).
+        */
+       duty = duty_ns * SPRD_PWM_MOD_MAX / period_ns;
+
+       tmp = (u64)chn->clk_rate * period_ns;
+       do_div(tmp, NSEC_PER_SEC);
+       prescale = DIV_ROUND_CLOSEST_ULL(tmp, SPRD_PWM_MOD_MAX) - 1;
+       if (prescale > SPRD_PWM_PRESCALE_MSK)
+               prescale = SPRD_PWM_PRESCALE_MSK;
+
+       /*
+        * Note: Writing DUTY triggers the hardware to actually apply the
+        * values written to MOD and DUTY to the output, so must keep writing
+        * DUTY last.
+        *
+        * The hardware can ensures that current running period is completed
+        * before changing a new configuration to avoid mixed settings.
+        */
+       sprd_pwm_write(spc, pwm->hwpwm, SPRD_PWM_PRESCALE, prescale);
+       sprd_pwm_write(spc, pwm->hwpwm, SPRD_PWM_MOD, SPRD_PWM_MOD_MAX);
+       sprd_pwm_write(spc, pwm->hwpwm, SPRD_PWM_DUTY, duty);
+
+       return 0;
+}
+
+static int sprd_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+                         const struct pwm_state *state)
+{
+       struct sprd_pwm_chip *spc =
+               container_of(chip, struct sprd_pwm_chip, chip);
+       struct sprd_pwm_chn *chn = &spc->chn[pwm->hwpwm];
+       struct pwm_state *cstate = &pwm->state;
+       int ret;
+
+       if (state->enabled) {
+               if (!cstate->enabled) {
+                       /*
+                        * The clocks to PWM channel has to be enabled first
+                        * before writing to the registers.
+                        */
+                       ret = clk_bulk_prepare_enable(SPRD_PWM_CHN_CLKS_NUM,
+                                                     chn->clks);
+                       if (ret) {
+                               dev_err(spc->dev,
+                                       "failed to enable pwm%u clocks\n",
+                                       pwm->hwpwm);
+                               return ret;
+                       }
+               }
+
+               if (state->period != cstate->period ||
+                   state->duty_cycle != cstate->duty_cycle) {
+                       ret = sprd_pwm_config(spc, pwm, state->duty_cycle,
+                                             state->period);
+                       if (ret)
+                               return ret;
+               }
+
+               sprd_pwm_write(spc, pwm->hwpwm, SPRD_PWM_ENABLE, 1);
+       } else if (cstate->enabled) {
+               /*
+                * Note: After setting SPRD_PWM_ENABLE to zero, the controller
+                * will not wait for current period to be completed, instead it
+                * will stop the PWM channel immediately.
+                */
+               sprd_pwm_write(spc, pwm->hwpwm, SPRD_PWM_ENABLE, 0);
+
+               clk_bulk_disable_unprepare(SPRD_PWM_CHN_CLKS_NUM, chn->clks);
+       }
+
+       return 0;
+}
+
+static const struct pwm_ops sprd_pwm_ops = {
+       .apply = sprd_pwm_apply,
+       .get_state = sprd_pwm_get_state,
+       .owner = THIS_MODULE,
+};
+
+static int sprd_pwm_clk_init(struct sprd_pwm_chip *spc)
+{
+       struct clk *clk_pwm;
+       int ret, i;
+
+       for (i = 0; i < SPRD_PWM_CHN_NUM; i++) {
+               struct sprd_pwm_chn *chn = &spc->chn[i];
+               int j;
+
+               for (j = 0; j < SPRD_PWM_CHN_CLKS_NUM; ++j)
+                       chn->clks[j].id =
+                               sprd_pwm_clks[i * SPRD_PWM_CHN_CLKS_NUM + j];
+
+               ret = devm_clk_bulk_get(spc->dev, SPRD_PWM_CHN_CLKS_NUM,
+                                       chn->clks);
+               if (ret) {
+                       if (ret == -ENOENT)
+                               break;
+
+                       if (ret != -EPROBE_DEFER)
+                               dev_err(spc->dev,
+                                       "failed to get channel clocks\n");
+
+                       return ret;
+               }
+
+               clk_pwm = chn->clks[SPRD_PWM_CHN_OUTPUT_CLK].clk;
+               chn->clk_rate = clk_get_rate(clk_pwm);
+       }
+
+       if (!i) {
+               dev_err(spc->dev, "no available PWM channels\n");
+               return -ENODEV;
+       }
+
+       spc->num_pwms = i;
+
+       return 0;
+}
+
+static int sprd_pwm_probe(struct platform_device *pdev)
+{
+       struct sprd_pwm_chip *spc;
+       int ret;
+
+       spc = devm_kzalloc(&pdev->dev, sizeof(*spc), GFP_KERNEL);
+       if (!spc)
+               return -ENOMEM;
+
+       spc->base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(spc->base))
+               return PTR_ERR(spc->base);
+
+       spc->dev = &pdev->dev;
+       platform_set_drvdata(pdev, spc);
+
+       ret = sprd_pwm_clk_init(spc);
+       if (ret)
+               return ret;
+
+       spc->chip.dev = &pdev->dev;
+       spc->chip.ops = &sprd_pwm_ops;
+       spc->chip.base = -1;
+       spc->chip.npwm = spc->num_pwms;
+
+       ret = pwmchip_add(&spc->chip);
+       if (ret)
+               dev_err(&pdev->dev, "failed to add PWM chip\n");
+
+       return ret;
+}
+
+static int sprd_pwm_remove(struct platform_device *pdev)
+{
+       struct sprd_pwm_chip *spc = platform_get_drvdata(pdev);
+
+       return pwmchip_remove(&spc->chip);
+}
+
+static const struct of_device_id sprd_pwm_of_match[] = {
+       { .compatible = "sprd,ums512-pwm", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, sprd_pwm_of_match);
+
+static struct platform_driver sprd_pwm_driver = {
+       .driver = {
+               .name = "sprd-pwm",
+               .of_match_table = sprd_pwm_of_match,
+       },
+       .probe = sprd_pwm_probe,
+       .remove = sprd_pwm_remove,
+};
+
+module_platform_driver(sprd_pwm_driver);
+
+MODULE_DESCRIPTION("Spreadtrum PWM Driver");
+MODULE_LICENSE("GPL v2");
index 20450e34ad57a622895345292d0a311c56dde975..1508616d794cd9b8c04f243753cfe037dc4fc335 100644 (file)
@@ -564,10 +564,8 @@ static int sti_pwm_probe(struct platform_device *pdev)
                return PTR_ERR(pc->regmap);
 
        irq = platform_get_irq(pdev, 0);
-       if (irq < 0) {
-               dev_err(&pdev->dev, "Failed to obtain IRQ\n");
+       if (irq < 0)
                return irq;
-       }
 
        ret = devm_request_irq(&pdev->dev, irq, sti_pwm_interrupt, 0,
                               pdev->name, pc);
index 2211a642066dbad0fe2288c6ebbfb56d19968c23..67fca62524dc243fd16ba736b62c34bd0559d7ea 100644 (file)
@@ -32,7 +32,7 @@ static inline struct stm32_pwm_lp *to_stm32_pwm_lp(struct pwm_chip *chip)
 #define STM32_LPTIM_MAX_PRESCALER      128
 
 static int stm32_pwm_lp_apply(struct pwm_chip *chip, struct pwm_device *pwm,
-                             struct pwm_state *state)
+                             const struct pwm_state *state)
 {
        struct stm32_pwm_lp *priv = to_stm32_pwm_lp(chip);
        unsigned long long prd, div, dty;
@@ -59,6 +59,12 @@ static int stm32_pwm_lp_apply(struct pwm_chip *chip, struct pwm_device *pwm,
        /* Calculate the period and prescaler value */
        div = (unsigned long long)clk_get_rate(priv->clk) * state->period;
        do_div(div, NSEC_PER_SEC);
+       if (!div) {
+               /* Clock is too slow to achieve requested period. */
+               dev_dbg(priv->chip.dev, "Can't reach %u ns\n",  state->period);
+               return -EINVAL;
+       }
+
        prd = div;
        while (div > STM32_LPTIM_MAX_ARR) {
                presc++;
index 740e2dec8313aa2f28b45276f1e56c738b972be0..359b08596d9e3262c00196412c5ce14df6661658 100644 (file)
@@ -440,7 +440,7 @@ static void stm32_pwm_disable(struct stm32_pwm *priv, int ch)
 }
 
 static int stm32_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
-                          struct pwm_state *state)
+                          const struct pwm_state *state)
 {
        bool enabled;
        struct stm32_pwm *priv = to_stm32_pwm_dev(chip);
@@ -468,7 +468,7 @@ static int stm32_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
 }
 
 static int stm32_pwm_apply_locked(struct pwm_chip *chip, struct pwm_device *pwm,
-                                 struct pwm_state *state)
+                                 const struct pwm_state *state)
 {
        struct stm32_pwm *priv = to_stm32_pwm_dev(chip);
        int ret;
index de78c824bbfd7cdd4f276b31f81fde79ff1cbcda..6f5840a1a82dc1909aad7e6d61632aea8a7b8271 100644 (file)
@@ -145,7 +145,7 @@ static void sun4i_pwm_get_state(struct pwm_chip *chip,
 }
 
 static int sun4i_pwm_calculate(struct sun4i_pwm_chip *sun4i_pwm,
-                              struct pwm_state *state,
+                              const struct pwm_state *state,
                               u32 *dty, u32 *prd, unsigned int *prsclr)
 {
        u64 clk_rate, div = 0;
@@ -192,17 +192,11 @@ static int sun4i_pwm_calculate(struct sun4i_pwm_chip *sun4i_pwm,
        *dty = div;
        *prsclr = prescaler;
 
-       div = (u64)pval * NSEC_PER_SEC * *prd;
-       state->period = DIV_ROUND_CLOSEST_ULL(div, clk_rate);
-
-       div = (u64)pval * NSEC_PER_SEC * *dty;
-       state->duty_cycle = DIV_ROUND_CLOSEST_ULL(div, clk_rate);
-
        return 0;
 }
 
 static int sun4i_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
-                          struct pwm_state *state)
+                          const struct pwm_state *state)
 {
        struct sun4i_pwm_chip *sun4i_pwm = to_sun4i_pwm_chip(chip);
        struct pwm_state cstate;
index e24f4be35316e2218595f02753df11978affbc9d..e2c21cc34a96aadc968b8df8c9de296e1de45cc9 100644 (file)
@@ -148,7 +148,7 @@ static int zx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 }
 
 static int zx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
-                       struct pwm_state *state)
+                       const struct pwm_state *state)
 {
        struct zx_pwm_chip *zpc = to_zx_pwm_chip(chip);
        struct pwm_state cstate;
index 24632a7a7d11ccf55d43e52993d44e72e3604c16..b2c9c460947d13ec3a724fab9fd36bf0f7a77b98 100644 (file)
@@ -262,7 +262,7 @@ struct pwm_ops {
        int (*capture)(struct pwm_chip *chip, struct pwm_device *pwm,
                       struct pwm_capture *result, unsigned long timeout);
        int (*apply)(struct pwm_chip *chip, struct pwm_device *pwm,
-                    struct pwm_state *state);
+                    const struct pwm_state *state);
        void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm,
                          struct pwm_state *state);
        struct module *owner;
@@ -316,7 +316,7 @@ struct pwm_capture {
 /* PWM user APIs */
 struct pwm_device *pwm_request(int pwm_id, const char *label);
 void pwm_free(struct pwm_device *pwm);
-int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state);
+int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state);
 int pwm_adjust_config(struct pwm_device *pwm);
 
 /**