Merge tag 'pwm/for-5.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 12 Jun 2020 19:24:42 +0000 (12:24 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 12 Jun 2020 19:24:42 +0000 (12:24 -0700)
Pull pwm updates from Thierry Reding:
 "Nothing too exciting for this cycle. A couple of fixes across the
  board, and Lee volunteered to help with patch review"

* tag 'pwm/for-5.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm:
  pwm: Add missing "CONFIG_" prefix
  MAINTAINERS: Add Lee Jones as reviewer for the PWM subsystem
  pwm: imx27: Fix rounding behavior
  pwm: rockchip: Simplify rockchip_pwm_get_state()
  pwm: img: Call pm_runtime_put() in pm_runtime_get_sync() failed case
  pwm: tegra: Support dynamic clock frequency configuration
  pwm: jz4740: Add support for the JZ4725B
  pwm: jz4740: Make PWM start with the active part
  pwm: jz4740: Enhance precision in calculation of duty cycle
  pwm: jz4740: Drop dependency on MACH_INGENIC
  pwm: lpss: Fix get_state runtime-pm reference handling
  pwm: sun4i: Support direct clock output on Allwinner A64
  pwm: Add support for Azoteq IQS620A PWM generator
  dt-bindings: pwm: rcar: add r8a77961 support
  pwm: Add missing '\n' in log messages

13 files changed:
Documentation/devicetree/bindings/pwm/renesas,pwm-rcar.yaml
MAINTAINERS
drivers/pwm/Kconfig
drivers/pwm/Makefile
drivers/pwm/core.c
drivers/pwm/pwm-img.c
drivers/pwm/pwm-imx27.c
drivers/pwm/pwm-iqs620a.c [new file with mode: 0644]
drivers/pwm/pwm-jz4740.c
drivers/pwm/pwm-lpss.c
drivers/pwm/pwm-rockchip.c
drivers/pwm/pwm-sun4i.c
drivers/pwm/pwm-tegra.c

index 461afb4c1f5d20f428c58e3d8350e17fb29cd52f..daadde9ff9c4fea270284e8c3bb41b496ffa9ae4 100644 (file)
@@ -27,6 +27,7 @@ properties:
           - renesas,pwm-r8a7794   # R-Car E2
           - renesas,pwm-r8a7795   # R-Car H3
           - renesas,pwm-r8a7796   # R-Car M3-W
+          - renesas,pwm-r8a77961  # R-Car M3-W+
           - renesas,pwm-r8a77965  # R-Car M3-N
           - renesas,pwm-r8a77970  # R-Car V3M
           - renesas,pwm-r8a77980  # R-Car V3H
index b0642aaa146e4c8767f24a2df4c3fa8be80c5da0..b5cbbc2e7982dd0097014908ffab98123cf0d557 100644 (file)
@@ -13919,6 +13919,7 @@ 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>
+M:     Lee Jones <lee.jones@linaro.org>
 L:     linux-pwm@vger.kernel.org
 S:     Maintained
 Q:     https://patchwork.ozlabs.org/project/linux-pwm/list/
index eebbc917ac979a8cabc9071431ed6a693dd4a474..cb8d739067d2fa4578603a36a75b9ac1edeffebb 100644 (file)
@@ -232,9 +232,19 @@ config PWM_IMX_TPM
          To compile this driver as a module, choose M here: the module
          will be called pwm-imx-tpm.
 
+config PWM_IQS620A
+       tristate "Azoteq IQS620A PWM support"
+       depends on MFD_IQS62X || COMPILE_TEST
+       help
+         Generic PWM framework driver for the Azoteq IQS620A multi-function
+         sensor.
+
+         To compile this driver as a module, choose M here: the module will
+         be called pwm-iqs620a.
+
 config PWM_JZ4740
        tristate "Ingenic JZ47xx PWM support"
-       depends on MACH_INGENIC
+       depends on MIPS
        depends on COMMON_CLK
        select MFD_SYSCON
        help
index 9a475073dafcbb2a95d18bf499e65ff02b1a2a5d..a59c710e98c76d7348e6cb57101929ffc509a1c4 100644 (file)
@@ -20,6 +20,7 @@ obj-$(CONFIG_PWM_IMG)         += pwm-img.o
 obj-$(CONFIG_PWM_IMX1)         += pwm-imx1.o
 obj-$(CONFIG_PWM_IMX27)                += pwm-imx27.o
 obj-$(CONFIG_PWM_IMX_TPM)      += pwm-imx-tpm.o
+obj-$(CONFIG_PWM_IQS620A)      += pwm-iqs620a.o
 obj-$(CONFIG_PWM_JZ4740)       += pwm-jz4740.o
 obj-$(CONFIG_PWM_LP3943)       += pwm-lp3943.o
 obj-$(CONFIG_PWM_LPC18XX_SCT)  += pwm-lpc18xx-sct.o
index 9973c442b45557ce94a74a5b31de08788e6045b1..004b2ea9b5fde3968730260928eb60e80be21e58 100644 (file)
@@ -121,7 +121,7 @@ static int pwm_device_request(struct pwm_device *pwm, const char *label)
                pwm->chip->ops->get_state(pwm->chip, pwm, &pwm->state);
                trace_pwm_get(pwm, &pwm->state);
 
-               if (IS_ENABLED(PWM_DEBUG))
+               if (IS_ENABLED(CONFIG_PWM_DEBUG))
                        pwm->last = pwm->state;
        }
 
@@ -537,7 +537,7 @@ static void pwm_apply_state_debug(struct pwm_device *pwm,
 
        if (!state->enabled && s2.enabled && s2.duty_cycle > 0)
                dev_warn(chip->dev,
-                        "requested disabled, but yielded enabled with duty > 0");
+                        "requested disabled, but yielded enabled with duty > 0\n");
 
        /* reapply the state that the driver reported being configured. */
        err = chip->ops->apply(chip, pwm, &s1);
index c9e57bd109fbfdd581e23e490ddd1bdcb03206c3..599a0f66a38457cd8b66144b5f1b15f512f40bde 100644 (file)
@@ -129,8 +129,10 @@ static int img_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
        duty = DIV_ROUND_UP(timebase * duty_ns, period_ns);
 
        ret = pm_runtime_get_sync(chip->dev);
-       if (ret < 0)
+       if (ret < 0) {
+               pm_runtime_put_autosuspend(chip->dev);
                return ret;
+       }
 
        val = img_pwm_readl(pwm_chip, PWM_CTRL_CFG);
        val &= ~(PWM_CTRL_CFG_DIV_MASK << PWM_CTRL_CFG_DIV_SHIFT(pwm->hwpwm));
@@ -331,8 +333,10 @@ static int img_pwm_remove(struct platform_device *pdev)
        int ret;
 
        ret = pm_runtime_get_sync(&pdev->dev);
-       if (ret < 0)
+       if (ret < 0) {
+               pm_runtime_put(&pdev->dev);
                return ret;
+       }
 
        for (i = 0; i < pwm_chip->chip.npwm; i++) {
                val = img_pwm_readl(pwm_chip, PWM_CTRL_CFG);
index a6e40d4c485f370ea8bac49517630e0d26f78478..732a6f3701e8e329d3e362a5c3355baa24cfb7f7 100644 (file)
@@ -150,13 +150,12 @@ static void pwm_imx27_get_state(struct pwm_chip *chip,
 
        prescaler = MX3_PWMCR_PRESCALER_GET(val);
        pwm_clk = clk_get_rate(imx->clk_per);
-       pwm_clk = DIV_ROUND_CLOSEST_ULL(pwm_clk, prescaler);
        val = readl(imx->mmio_base + MX3_PWMPR);
        period = val >= MX3_PWMPR_MAX ? MX3_PWMPR_MAX : val;
 
        /* PWMOUT (Hz) = PWMCLK / (PWMPR + 2) */
-       tmp = NSEC_PER_SEC * (u64)(period + 2);
-       state->period = DIV_ROUND_CLOSEST_ULL(tmp, pwm_clk);
+       tmp = NSEC_PER_SEC * (u64)(period + 2) * prescaler;
+       state->period = DIV_ROUND_UP_ULL(tmp, pwm_clk);
 
        /*
         * PWMSAR can be read only if PWM is enabled. If the PWM is disabled,
@@ -167,8 +166,8 @@ static void pwm_imx27_get_state(struct pwm_chip *chip,
        else
                val = imx->duty_cycle;
 
-       tmp = NSEC_PER_SEC * (u64)(val);
-       state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, pwm_clk);
+       tmp = NSEC_PER_SEC * (u64)(val) * prescaler;
+       state->duty_cycle = DIV_ROUND_UP_ULL(tmp, pwm_clk);
 
        pwm_imx27_clk_disable_unprepare(imx);
 }
@@ -220,22 +219,23 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm,
        struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip);
        struct pwm_state cstate;
        unsigned long long c;
+       unsigned long long clkrate;
        int ret;
        u32 cr;
 
        pwm_get_state(pwm, &cstate);
 
-       c = clk_get_rate(imx->clk_per);
-       c *= state->period;
+       clkrate = clk_get_rate(imx->clk_per);
+       c = clkrate * state->period;
 
-       do_div(c, 1000000000);
+       do_div(c, NSEC_PER_SEC);
        period_cycles = c;
 
        prescale = period_cycles / 0x10000 + 1;
 
        period_cycles /= prescale;
-       c = (unsigned long long)period_cycles * state->duty_cycle;
-       do_div(c, state->period);
+       c = clkrate * state->duty_cycle;
+       do_div(c, NSEC_PER_SEC * prescale);
        duty_cycles = c;
 
        /*
diff --git a/drivers/pwm/pwm-iqs620a.c b/drivers/pwm/pwm-iqs620a.c
new file mode 100644 (file)
index 0000000..674f0e2
--- /dev/null
@@ -0,0 +1,270 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Azoteq IQS620A PWM Generator
+ *
+ * Copyright (C) 2019 Jeff LaBundy <jeff@labundy.com>
+ *
+ * Limitations:
+ * - The period is fixed to 1 ms and is generated continuously despite changes
+ *   to the duty cycle or enable/disable state.
+ * - Changes to the duty cycle or enable/disable state take effect immediately
+ *   and may result in a glitch during the period in which the change is made.
+ * - The device cannot generate a 0% duty cycle. For duty cycles below 1 / 256
+ *   ms, the output is disabled and relies upon an external pull-down resistor
+ *   to hold the GPIO3/LTX pin low.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/mfd/iqs62x.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/notifier.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define IQS620_PWR_SETTINGS                    0xD2
+#define IQS620_PWR_SETTINGS_PWM_OUT            BIT(7)
+
+#define IQS620_PWM_DUTY_CYCLE                  0xD8
+
+#define IQS620_PWM_PERIOD_NS                   1000000
+
+struct iqs620_pwm_private {
+       struct iqs62x_core *iqs62x;
+       struct pwm_chip chip;
+       struct notifier_block notifier;
+       struct mutex lock;
+       bool out_en;
+       u8 duty_val;
+};
+
+static int iqs620_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
+                           const struct pwm_state *state)
+{
+       struct iqs620_pwm_private *iqs620_pwm;
+       struct iqs62x_core *iqs62x;
+       int duty_scale, ret;
+
+       if (state->polarity != PWM_POLARITY_NORMAL)
+               return -ENOTSUPP;
+
+       if (state->period < IQS620_PWM_PERIOD_NS)
+               return -EINVAL;
+
+       iqs620_pwm = container_of(chip, struct iqs620_pwm_private, chip);
+       iqs62x = iqs620_pwm->iqs62x;
+
+       /*
+        * The duty cycle generated by the device is calculated as follows:
+        *
+        * duty_cycle = (IQS620_PWM_DUTY_CYCLE + 1) / 256 * 1 ms
+        *
+        * ...where IQS620_PWM_DUTY_CYCLE is a register value between 0 and 255
+        * (inclusive). Therefore the lowest duty cycle the device can generate
+        * while the output is enabled is 1 / 256 ms.
+        *
+        * For lower duty cycles (e.g. 0), the PWM output is simply disabled to
+        * allow an external pull-down resistor to hold the GPIO3/LTX pin low.
+        */
+       duty_scale = state->duty_cycle * 256 / IQS620_PWM_PERIOD_NS;
+
+       mutex_lock(&iqs620_pwm->lock);
+
+       if (!state->enabled || !duty_scale) {
+               ret = regmap_update_bits(iqs62x->regmap, IQS620_PWR_SETTINGS,
+                                        IQS620_PWR_SETTINGS_PWM_OUT, 0);
+               if (ret)
+                       goto err_mutex;
+       }
+
+       if (duty_scale) {
+               u8 duty_val = min(duty_scale - 1, 0xFF);
+
+               ret = regmap_write(iqs62x->regmap, IQS620_PWM_DUTY_CYCLE,
+                                  duty_val);
+               if (ret)
+                       goto err_mutex;
+
+               iqs620_pwm->duty_val = duty_val;
+       }
+
+       if (state->enabled && duty_scale) {
+               ret = regmap_update_bits(iqs62x->regmap, IQS620_PWR_SETTINGS,
+                                        IQS620_PWR_SETTINGS_PWM_OUT, 0xFF);
+               if (ret)
+                       goto err_mutex;
+       }
+
+       iqs620_pwm->out_en = state->enabled;
+
+err_mutex:
+       mutex_unlock(&iqs620_pwm->lock);
+
+       return ret;
+}
+
+static void iqs620_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
+                                struct pwm_state *state)
+{
+       struct iqs620_pwm_private *iqs620_pwm;
+
+       iqs620_pwm = container_of(chip, struct iqs620_pwm_private, chip);
+
+       mutex_lock(&iqs620_pwm->lock);
+
+       /*
+        * Since the device cannot generate a 0% duty cycle, requests to do so
+        * cause subsequent calls to iqs620_pwm_get_state to report the output
+        * as disabled with duty cycle equal to that which was in use prior to
+        * the request. This is not ideal, but is the best compromise based on
+        * the capabilities of the device.
+        */
+       state->enabled = iqs620_pwm->out_en;
+       state->duty_cycle = DIV_ROUND_UP((iqs620_pwm->duty_val + 1) *
+                                        IQS620_PWM_PERIOD_NS, 256);
+
+       mutex_unlock(&iqs620_pwm->lock);
+
+       state->period = IQS620_PWM_PERIOD_NS;
+}
+
+static int iqs620_pwm_notifier(struct notifier_block *notifier,
+                              unsigned long event_flags, void *context)
+{
+       struct iqs620_pwm_private *iqs620_pwm;
+       struct iqs62x_core *iqs62x;
+       int ret;
+
+       if (!(event_flags & BIT(IQS62X_EVENT_SYS_RESET)))
+               return NOTIFY_DONE;
+
+       iqs620_pwm = container_of(notifier, struct iqs620_pwm_private,
+                                 notifier);
+       iqs62x = iqs620_pwm->iqs62x;
+
+       mutex_lock(&iqs620_pwm->lock);
+
+       /*
+        * The parent MFD driver already prints an error message in the event
+        * of a device reset, so nothing else is printed here unless there is
+        * an additional failure.
+        */
+       ret = regmap_write(iqs62x->regmap, IQS620_PWM_DUTY_CYCLE,
+                          iqs620_pwm->duty_val);
+       if (ret)
+               goto err_mutex;
+
+       ret = regmap_update_bits(iqs62x->regmap, IQS620_PWR_SETTINGS,
+                                IQS620_PWR_SETTINGS_PWM_OUT,
+                                iqs620_pwm->out_en ? 0xFF : 0);
+
+err_mutex:
+       mutex_unlock(&iqs620_pwm->lock);
+
+       if (ret) {
+               dev_err(iqs620_pwm->chip.dev,
+                       "Failed to re-initialize device: %d\n", ret);
+               return NOTIFY_BAD;
+       }
+
+       return NOTIFY_OK;
+}
+
+static const struct pwm_ops iqs620_pwm_ops = {
+       .apply = iqs620_pwm_apply,
+       .get_state = iqs620_pwm_get_state,
+       .owner = THIS_MODULE,
+};
+
+static void iqs620_pwm_notifier_unregister(void *context)
+{
+       struct iqs620_pwm_private *iqs620_pwm = context;
+       int ret;
+
+       ret = blocking_notifier_chain_unregister(&iqs620_pwm->iqs62x->nh,
+                                                &iqs620_pwm->notifier);
+       if (ret)
+               dev_err(iqs620_pwm->chip.dev,
+                       "Failed to unregister notifier: %d\n", ret);
+}
+
+static int iqs620_pwm_probe(struct platform_device *pdev)
+{
+       struct iqs62x_core *iqs62x = dev_get_drvdata(pdev->dev.parent);
+       struct iqs620_pwm_private *iqs620_pwm;
+       unsigned int val;
+       int ret;
+
+       iqs620_pwm = devm_kzalloc(&pdev->dev, sizeof(*iqs620_pwm), GFP_KERNEL);
+       if (!iqs620_pwm)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, iqs620_pwm);
+       iqs620_pwm->iqs62x = iqs62x;
+
+       ret = regmap_read(iqs62x->regmap, IQS620_PWR_SETTINGS, &val);
+       if (ret)
+               return ret;
+       iqs620_pwm->out_en = val & IQS620_PWR_SETTINGS_PWM_OUT;
+
+       ret = regmap_read(iqs62x->regmap, IQS620_PWM_DUTY_CYCLE, &val);
+       if (ret)
+               return ret;
+       iqs620_pwm->duty_val = val;
+
+       iqs620_pwm->chip.dev = &pdev->dev;
+       iqs620_pwm->chip.ops = &iqs620_pwm_ops;
+       iqs620_pwm->chip.base = -1;
+       iqs620_pwm->chip.npwm = 1;
+
+       mutex_init(&iqs620_pwm->lock);
+
+       iqs620_pwm->notifier.notifier_call = iqs620_pwm_notifier;
+       ret = blocking_notifier_chain_register(&iqs620_pwm->iqs62x->nh,
+                                              &iqs620_pwm->notifier);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to register notifier: %d\n", ret);
+               return ret;
+       }
+
+       ret = devm_add_action_or_reset(&pdev->dev,
+                                      iqs620_pwm_notifier_unregister,
+                                      iqs620_pwm);
+       if (ret)
+               return ret;
+
+       ret = pwmchip_add(&iqs620_pwm->chip);
+       if (ret)
+               dev_err(&pdev->dev, "Failed to add device: %d\n", ret);
+
+       return ret;
+}
+
+static int iqs620_pwm_remove(struct platform_device *pdev)
+{
+       struct iqs620_pwm_private *iqs620_pwm = platform_get_drvdata(pdev);
+       int ret;
+
+       ret = pwmchip_remove(&iqs620_pwm->chip);
+       if (ret)
+               dev_err(&pdev->dev, "Failed to remove device: %d\n", ret);
+
+       return ret;
+}
+
+static struct platform_driver iqs620_pwm_platform_driver = {
+       .driver = {
+               .name = "iqs620a-pwm",
+       },
+       .probe = iqs620_pwm_probe,
+       .remove = iqs620_pwm_remove,
+};
+module_platform_driver(iqs620_pwm_platform_driver);
+
+MODULE_AUTHOR("Jeff LaBundy <jeff@labundy.com>");
+MODULE_DESCRIPTION("Azoteq IQS620A PWM Generator");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:iqs620a-pwm");
index 3cd5c054ad9aff82c7f8b7465419d9682fbce753..5830ac2bdf6acf6ba7a5d1c160fec00a9f9f4edb 100644 (file)
@@ -6,7 +6,6 @@
  * 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>
@@ -21,7 +20,9 @@
 #include <linux/pwm.h>
 #include <linux/regmap.h>
 
-#define NUM_PWM 8
+struct soc_info {
+       unsigned int num_pwms;
+};
 
 struct jz4740_pwm_chip {
        struct pwm_chip chip;
@@ -37,7 +38,7 @@ static bool jz4740_pwm_can_use_chn(struct jz4740_pwm_chip *jz,
                                   unsigned int channel)
 {
        /* Enable all TCU channels for PWM use by default except channels 0/1 */
-       u32 pwm_channels_mask = GENMASK(NUM_PWM - 1, 2);
+       u32 pwm_channels_mask = GENMASK(jz->chip.npwm - 1, 2);
 
        device_property_read_u32(jz->chip.dev->parent,
                                 "ingenic,pwm-channels-mask",
@@ -158,12 +159,12 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
        /* Calculate period value */
        tmp = (unsigned long long)rate * state->period;
        do_div(tmp, NSEC_PER_SEC);
-       period = (unsigned long)tmp;
+       period = tmp;
 
        /* Calculate duty value */
-       tmp = (unsigned long long)period * state->duty_cycle;
-       do_div(tmp, state->period);
-       duty = period - tmp;
+       tmp = (unsigned long long)rate * state->duty_cycle;
+       do_div(tmp, NSEC_PER_SEC);
+       duty = tmp;
 
        if (duty >= period)
                duty = period - 1;
@@ -189,18 +190,26 @@ static int jz4740_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
        regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
                           TCU_TCSR_PWM_SD, TCU_TCSR_PWM_SD);
 
-       /* Set polarity */
-       switch (state->polarity) {
-       case PWM_POLARITY_NORMAL:
+       /*
+        * Set polarity.
+        *
+        * The PWM starts in inactive state until the internal timer reaches the
+        * duty value, then becomes active until the timer reaches the period
+        * value. In theory, we should then use (period - duty) as the real duty
+        * value, as a high duty value would otherwise result in the PWM pin
+        * being inactive most of the time.
+        *
+        * Here, we don't do that, and instead invert the polarity of the PWM
+        * when it is active. This trick makes the PWM start with its active
+        * state instead of its inactive state.
+        */
+       if ((state->polarity == PWM_POLARITY_NORMAL) ^ state->enabled)
                regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
                                   TCU_TCSR_PWM_INITL_HIGH, 0);
-               break;
-       case PWM_POLARITY_INVERSED:
+       else
                regmap_update_bits(jz4740->map, TCU_REG_TCSRc(pwm->hwpwm),
                                   TCU_TCSR_PWM_INITL_HIGH,
                                   TCU_TCSR_PWM_INITL_HIGH);
-               break;
-       }
 
        if (state->enabled)
                jz4740_pwm_enable(chip, pwm);
@@ -219,6 +228,11 @@ static int jz4740_pwm_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct jz4740_pwm_chip *jz4740;
+       const struct soc_info *info;
+
+       info = device_get_match_data(dev);
+       if (!info)
+               return -EINVAL;
 
        jz4740 = devm_kzalloc(dev, sizeof(*jz4740), GFP_KERNEL);
        if (!jz4740)
@@ -232,7 +246,7 @@ static int jz4740_pwm_probe(struct platform_device *pdev)
 
        jz4740->chip.dev = dev;
        jz4740->chip.ops = &jz4740_pwm_ops;
-       jz4740->chip.npwm = NUM_PWM;
+       jz4740->chip.npwm = info->num_pwms;
        jz4740->chip.base = -1;
        jz4740->chip.of_xlate = of_pwm_xlate_with_flags;
        jz4740->chip.of_pwm_n_cells = 3;
@@ -249,9 +263,18 @@ static int jz4740_pwm_remove(struct platform_device *pdev)
        return pwmchip_remove(&jz4740->chip);
 }
 
+static const struct soc_info __maybe_unused jz4740_soc_info = {
+       .num_pwms = 8,
+};
+
+static const struct soc_info __maybe_unused jz4725b_soc_info = {
+       .num_pwms = 6,
+};
+
 #ifdef CONFIG_OF
 static const struct of_device_id jz4740_pwm_dt_ids[] = {
-       { .compatible = "ingenic,jz4740-pwm", },
+       { .compatible = "ingenic,jz4740-pwm", .data = &jz4740_soc_info },
+       { .compatible = "ingenic,jz4725b-pwm", .data = &jz4725b_soc_info },
        {},
 };
 MODULE_DEVICE_TABLE(of, jz4740_pwm_dt_ids);
index 75bbfe5f3bc2975479b1a27ade8eca58bab05455..9d965ffe66d1efcc57be802b452665a3bfe80dd0 100644 (file)
@@ -158,7 +158,6 @@ static int pwm_lpss_apply(struct pwm_chip *chip, struct pwm_device *pwm,
        return 0;
 }
 
-/* This function gets called once from pwmchip_add to get the initial state */
 static void pwm_lpss_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
                               struct pwm_state *state)
 {
@@ -167,6 +166,8 @@ static void pwm_lpss_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
        unsigned long long base_unit, freq, on_time_div;
        u32 ctrl;
 
+       pm_runtime_get_sync(chip->dev);
+
        base_unit_range = BIT(lpwm->info->base_unit_bits);
 
        ctrl = pwm_lpss_read(pwm);
@@ -187,8 +188,7 @@ static void pwm_lpss_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
        state->polarity = PWM_POLARITY_NORMAL;
        state->enabled = !!(ctrl & PWM_ENABLE);
 
-       if (state->enabled)
-               pm_runtime_get(chip->dev);
+       pm_runtime_put(chip->dev);
 }
 
 static const struct pwm_ops pwm_lpss_ops = {
@@ -202,7 +202,8 @@ struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, struct resource *r,
 {
        struct pwm_lpss_chip *lpwm;
        unsigned long c;
-       int ret;
+       int i, ret;
+       u32 ctrl;
 
        if (WARN_ON(info->npwm > MAX_PWMS))
                return ERR_PTR(-ENODEV);
@@ -232,6 +233,12 @@ struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, struct resource *r,
                return ERR_PTR(ret);
        }
 
+       for (i = 0; i < lpwm->info->npwm; i++) {
+               ctrl = pwm_lpss_read(&lpwm->chip.pwms[i]);
+               if (ctrl & PWM_ENABLE)
+                       pm_runtime_get(dev);
+       }
+
        return lpwm;
 }
 EXPORT_SYMBOL_GPL(pwm_lpss_probe);
index 73352e6fbccbf4040f1d2b866443f4dc4d75387f..eb8c9cb645a6c6b891c0e2dcd28d323834c13fa9 100644 (file)
@@ -83,12 +83,7 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip,
        state->duty_cycle =  DIV_ROUND_CLOSEST_ULL(tmp, clk_rate);
 
        val = readl_relaxed(pc->base + pc->data->regs.ctrl);
-       if (pc->data->supports_polarity)
-               state->enabled = ((val & enable_conf) != enable_conf) ?
-                                false : true;
-       else
-               state->enabled = ((val & enable_conf) == enable_conf) ?
-                                true : false;
+       state->enabled = (val & enable_conf) == enable_conf;
 
        if (pc->data->supports_polarity && !(val & PWM_DUTY_POSITIVE))
                state->polarity = PWM_POLARITY_INVERSED;
index 5c677c5633499898987a936041953f675b761c02..18fbbe3277d0fe1c20598205d9a2e0b53d351da9 100644 (file)
@@ -352,6 +352,12 @@ static const struct sun4i_pwm_data sun4i_pwm_single_bypass = {
        .npwm = 1,
 };
 
+static const struct sun4i_pwm_data sun50i_a64_pwm_data = {
+       .has_prescaler_bypass = true,
+       .has_direct_mod_clk_output = true,
+       .npwm = 1,
+};
+
 static const struct sun4i_pwm_data sun50i_h6_pwm_data = {
        .has_prescaler_bypass = true,
        .has_direct_mod_clk_output = true,
@@ -374,6 +380,9 @@ static const struct of_device_id sun4i_pwm_dt_ids[] = {
        }, {
                .compatible = "allwinner,sun8i-h3-pwm",
                .data = &sun4i_pwm_single_bypass,
+       }, {
+               .compatible = "allwinner,sun50i-a64-pwm",
+               .data = &sun50i_a64_pwm_data,
        }, {
                .compatible = "allwinner,sun50i-h6-pwm",
                .data = &sun50i_h6_pwm_data,
index d26ed8f579ff0b5cdd2fc8bb646955f00bed7f3e..1daf591025c0001539915348e4fbbf2d32ea00d7 100644 (file)
@@ -4,8 +4,36 @@
  *
  * Tegra pulse-width-modulation controller driver
  *
- * Copyright (c) 2010, NVIDIA Corporation.
+ * Copyright (c) 2010-2020, NVIDIA Corporation.
  * Based on arch/arm/plat-mxc/pwm.c by Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * Overview of Tegra Pulse Width Modulator Register:
+ * 1. 13-bit: Frequency division (SCALE)
+ * 2. 8-bit : Pulse division (DUTY)
+ * 3. 1-bit : Enable bit
+ *
+ * The PWM clock frequency is divided by 256 before subdividing it based
+ * on the programmable frequency division value to generate the required
+ * frequency for PWM output. The maximum output frequency that can be
+ * achieved is (max rate of source clock) / 256.
+ * e.g. if source clock rate is 408 MHz, maximum output frequency can be:
+ * 408 MHz/256 = 1.6 MHz.
+ * This 1.6 MHz frequency can further be divided using SCALE value in PWM.
+ *
+ * PWM pulse width: 8 bits are usable [23:16] for varying pulse width.
+ * To achieve 100% duty cycle, program Bit [24] of this register to
+ * 1’b1. In which case the other bits [23:16] are set to don't care.
+ *
+ * Limitations:
+ * -   When PWM is disabled, the output is driven to inactive.
+ * -   It does not allow the current PWM period to complete and
+ *     stops abruptly.
+ *
+ * -   If the register is reconfigured while PWM is running,
+ *     it does not complete the currently running period.
+ *
+ * -   If the user input duty is beyond acceptible limits,
+ *     -EINVAL is returned.
  */
 
 #include <linux/clk.h>
@@ -41,6 +69,7 @@ struct tegra_pwm_chip {
        struct reset_control*rst;
 
        unsigned long clk_rate;
+       unsigned long min_period_ns;
 
        void __iomem *regs;
 
@@ -68,7 +97,7 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 {
        struct tegra_pwm_chip *pc = to_tegra_pwm_chip(chip);
        unsigned long long c = duty_ns, hz;
-       unsigned long rate;
+       unsigned long rate, required_clk_rate;
        u32 val = 0;
        int err;
 
@@ -82,10 +111,48 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 
        val = (u32)c << PWM_DUTY_SHIFT;
 
+       /*
+        *  min period = max clock limit >> PWM_DUTY_WIDTH
+        */
+       if (period_ns < pc->min_period_ns)
+               return -EINVAL;
+
        /*
         * Compute the prescaler value for which (1 << PWM_DUTY_WIDTH)
         * cycles at the PWM clock rate will take period_ns nanoseconds.
+        *
+        * num_channels: If single instance of PWM controller has multiple
+        * channels (e.g. Tegra210 or older) then it is not possible to
+        * configure separate clock rates to each of the channels, in such
+        * case the value stored during probe will be referred.
+        *
+        * If every PWM controller instance has one channel respectively, i.e.
+        * nums_channels == 1 then only the clock rate can be modified
+        * dynamically (e.g. Tegra186 or Tegra194).
         */
+       if (pc->soc->num_channels == 1) {
+               /*
+                * Rate is multiplied with 2^PWM_DUTY_WIDTH so that it matches
+                * with the maximum possible rate that the controller can
+                * provide. Any further lower value can be derived by setting
+                * PFM bits[0:12].
+                *
+                * required_clk_rate is a reference rate for source clock and
+                * it is derived based on user requested period. By setting the
+                * source clock rate as required_clk_rate, PWM controller will
+                * be able to configure the requested period.
+                */
+               required_clk_rate =
+                       (NSEC_PER_SEC / period_ns) << PWM_DUTY_WIDTH;
+
+               err = clk_set_rate(pc->clk, required_clk_rate);
+               if (err < 0)
+                       return -EINVAL;
+
+               /* Store the new rate for further references */
+               pc->clk_rate = clk_get_rate(pc->clk);
+       }
+
        rate = pc->clk_rate >> PWM_DUTY_WIDTH;
 
        /* Consider precision in PWM_SCALE_WIDTH rate calculation */
@@ -94,7 +161,7 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
 
        /*
         * Since the actual PWM divider is the register's frequency divider
-        * field minus 1, we need to decrement to get the correct value to
+        * field plus 1, we need to decrement to get the correct value to
         * write to the register.
         */
        if (rate > 0)
@@ -205,6 +272,10 @@ static int tegra_pwm_probe(struct platform_device *pdev)
         */
        pwm->clk_rate = clk_get_rate(pwm->clk);
 
+       /* Set minimum limit of PWM period for the IP */
+       pwm->min_period_ns =
+           (NSEC_PER_SEC / (pwm->soc->max_frequency >> PWM_DUTY_WIDTH)) + 1;
+
        pwm->rst = devm_reset_control_get_exclusive(&pdev->dev, "pwm");
        if (IS_ERR(pwm->rst)) {
                ret = PTR_ERR(pwm->rst);
@@ -312,5 +383,6 @@ static struct platform_driver tegra_pwm_driver = {
 module_platform_driver(tegra_pwm_driver);
 
 MODULE_LICENSE("GPL");
-MODULE_AUTHOR("NVIDIA Corporation");
+MODULE_AUTHOR("Sandipan Patra <spatra@nvidia.com>");
+MODULE_DESCRIPTION("Tegra PWM controller driver");
 MODULE_ALIAS("platform:tegra-pwm");