diff options
-rw-r--r-- | Documentation/devicetree/bindings/pwm/renesas,tpu-pwm.yaml | 2 | ||||
-rw-r--r-- | drivers/pwm/Kconfig | 4 | ||||
-rw-r--r-- | drivers/pwm/core.c | 9 | ||||
-rw-r--r-- | drivers/pwm/pwm-atmel.c | 1 | ||||
-rw-r--r-- | drivers/pwm/pwm-samsung.c | 30 | ||||
-rw-r--r-- | drivers/pwm/pwm-visconti.c | 14 | ||||
-rw-r--r-- | drivers/pwm/pwm-vt8500.c | 16 | ||||
-rw-r--r-- | include/linux/pwm.h | 13 |
8 files changed, 58 insertions, 31 deletions
diff --git a/Documentation/devicetree/bindings/pwm/renesas,tpu-pwm.yaml b/Documentation/devicetree/bindings/pwm/renesas,tpu-pwm.yaml index 81ccb2110162..1f5c6384182e 100644 --- a/Documentation/devicetree/bindings/pwm/renesas,tpu-pwm.yaml +++ b/Documentation/devicetree/bindings/pwm/renesas,tpu-pwm.yaml @@ -35,9 +35,11 @@ properties: - renesas,tpu-r8a7794 # R-Car E2 - renesas,tpu-r8a7795 # R-Car H3 - renesas,tpu-r8a7796 # R-Car M3-W + - renesas,tpu-r8a77961 # R-Car M3-W+ - renesas,tpu-r8a77965 # R-Car M3-N - renesas,tpu-r8a77970 # R-Car V3M - renesas,tpu-r8a77980 # R-Car V3H + - renesas,tpu-r8a779a0 # R-Car V3U - const: renesas,tpu reg: diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index aa29841bbb79..21e3b05a5153 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -476,7 +476,9 @@ config PWM_SAMSUNG depends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST depends on HAS_IOMEM help - Generic PWM framework driver for Samsung. + Generic PWM framework driver for Samsung S3C24xx, S3C64xx, S5Pv210 + and Exynos SoCs. + Choose Y here only if you build for such Samsung SoC. To compile this driver as a module, choose M here: the module will be called pwm-samsung. diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 4527f09a5c50..fb04a439462c 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -532,6 +532,15 @@ int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state) struct pwm_chip *chip; int err; + /* + * Some lowlevel driver's implementations of .apply() make use of + * mutexes, also with some drivers only returning when the new + * configuration is active calling pwm_apply_state() from atomic context + * is a bad idea. So make it explicit that calling this function might + * sleep. + */ + might_sleep(); + if (!pwm || !state || !state->period || state->duty_cycle > state->period) return -EINVAL; diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c index e748604403cc..98b34ea9f38e 100644 --- a/drivers/pwm/pwm-atmel.c +++ b/drivers/pwm/pwm-atmel.c @@ -24,7 +24,6 @@ #include <linux/err.h> #include <linux/io.h> #include <linux/module.h> -#include <linux/mutex.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/platform_device.h> diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c index dd94c4312a0c..0a4ff55fad04 100644 --- a/drivers/pwm/pwm-samsung.c +++ b/drivers/pwm/pwm-samsung.c @@ -117,6 +117,20 @@ static inline unsigned int to_tcon_channel(unsigned int channel) return (channel == 0) ? 0 : (channel + 1); } +static void __pwm_samsung_manual_update(struct samsung_pwm_chip *chip, + struct pwm_device *pwm) +{ + unsigned int tcon_chan = to_tcon_channel(pwm->hwpwm); + u32 tcon; + + tcon = readl(chip->base + REG_TCON); + tcon |= TCON_MANUALUPDATE(tcon_chan); + writel(tcon, chip->base + REG_TCON); + + tcon &= ~TCON_MANUALUPDATE(tcon_chan); + writel(tcon, chip->base + REG_TCON); +} + static void pwm_samsung_set_divisor(struct samsung_pwm_chip *pwm, unsigned int channel, u8 divisor) { @@ -276,6 +290,13 @@ static void pwm_samsung_disable(struct pwm_chip *chip, struct pwm_device *pwm) tcon &= ~TCON_AUTORELOAD(tcon_chan); writel(tcon, our_chip->base + REG_TCON); + /* + * In case the PWM is at 100% duty cycle, force a manual + * update to prevent the signal from staying high. + */ + if (readl(our_chip->base + REG_TCMPB(pwm->hwpwm)) == (u32)-1U) + __pwm_samsung_manual_update(our_chip, pwm); + our_chip->disabled_mask |= BIT(pwm->hwpwm); spin_unlock_irqrestore(&samsung_pwm_lock, flags); @@ -284,18 +305,11 @@ static void pwm_samsung_disable(struct pwm_chip *chip, struct pwm_device *pwm) static void pwm_samsung_manual_update(struct samsung_pwm_chip *chip, struct pwm_device *pwm) { - unsigned int tcon_chan = to_tcon_channel(pwm->hwpwm); - u32 tcon; unsigned long flags; spin_lock_irqsave(&samsung_pwm_lock, flags); - tcon = readl(chip->base + REG_TCON); - tcon |= TCON_MANUALUPDATE(tcon_chan); - writel(tcon, chip->base + REG_TCON); - - tcon &= ~TCON_MANUALUPDATE(tcon_chan); - writel(tcon, chip->base + REG_TCON); + __pwm_samsung_manual_update(chip, pwm); spin_unlock_irqrestore(&samsung_pwm_lock, flags); } diff --git a/drivers/pwm/pwm-visconti.c b/drivers/pwm/pwm-visconti.c index af4e37d3e3a6..927c4cbb1daf 100644 --- a/drivers/pwm/pwm-visconti.c +++ b/drivers/pwm/pwm-visconti.c @@ -144,28 +144,17 @@ static int visconti_pwm_probe(struct platform_device *pdev) if (IS_ERR(priv->base)) return PTR_ERR(priv->base); - platform_set_drvdata(pdev, priv); - priv->chip.dev = dev; priv->chip.ops = &visconti_pwm_ops; priv->chip.npwm = 4; - ret = pwmchip_add(&priv->chip); + ret = devm_pwmchip_add(&pdev->dev, &priv->chip); if (ret < 0) return dev_err_probe(&pdev->dev, ret, "Cannot register visconti PWM\n"); return 0; } -static int visconti_pwm_remove(struct platform_device *pdev) -{ - struct visconti_pwm_chip *priv = platform_get_drvdata(pdev); - - pwmchip_remove(&priv->chip); - - return 0; -} - static const struct of_device_id visconti_pwm_of_match[] = { { .compatible = "toshiba,visconti-pwm", }, { } @@ -178,7 +167,6 @@ static struct platform_driver visconti_pwm_driver = { .of_match_table = visconti_pwm_of_match, }, .probe = visconti_pwm_probe, - .remove = visconti_pwm_remove, }; module_platform_driver(visconti_pwm_driver); diff --git a/drivers/pwm/pwm-vt8500.c b/drivers/pwm/pwm-vt8500.c index ea2aa151080a..480bfc29782f 100644 --- a/drivers/pwm/pwm-vt8500.c +++ b/drivers/pwm/pwm-vt8500.c @@ -56,7 +56,7 @@ struct vt8500_chip { #define to_vt8500_chip(chip) container_of(chip, struct vt8500_chip, chip) #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t) -static inline void pwm_busy_wait(struct vt8500_chip *vt8500, int nr, u8 bitmask) +static inline void vt8500_pwm_busy_wait(struct vt8500_chip *vt8500, int nr, u8 bitmask) { int loops = msecs_to_loops(10); u32 mask = bitmask << (nr << 8); @@ -106,18 +106,18 @@ static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, dc = c; writel(prescale, vt8500->base + REG_SCALAR(pwm->hwpwm)); - pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_SCALAR_UPDATE); + vt8500_pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_SCALAR_UPDATE); writel(pv, vt8500->base + REG_PERIOD(pwm->hwpwm)); - pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_PERIOD_UPDATE); + vt8500_pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_PERIOD_UPDATE); writel(dc, vt8500->base + REG_DUTY(pwm->hwpwm)); - pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_DUTY_UPDATE); + vt8500_pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_DUTY_UPDATE); val = readl(vt8500->base + REG_CTRL(pwm->hwpwm)); val |= CTRL_AUTOLOAD; writel(val, vt8500->base + REG_CTRL(pwm->hwpwm)); - pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE); + vt8500_pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE); clk_disable(vt8500->clk); return 0; @@ -138,7 +138,7 @@ static int vt8500_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) val = readl(vt8500->base + REG_CTRL(pwm->hwpwm)); val |= CTRL_ENABLE; writel(val, vt8500->base + REG_CTRL(pwm->hwpwm)); - pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE); + vt8500_pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE); return 0; } @@ -151,7 +151,7 @@ static void vt8500_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) val = readl(vt8500->base + REG_CTRL(pwm->hwpwm)); val &= ~CTRL_ENABLE; writel(val, vt8500->base + REG_CTRL(pwm->hwpwm)); - pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE); + vt8500_pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE); clk_disable(vt8500->clk); } @@ -171,7 +171,7 @@ static int vt8500_pwm_set_polarity(struct pwm_chip *chip, val &= ~CTRL_INVERT; writel(val, vt8500->base + REG_CTRL(pwm->hwpwm)); - pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE); + vt8500_pwm_busy_wait(vt8500, pwm->hwpwm, STATUS_CTRL_UPDATE); return 0; } diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 725c9b784e60..e6dac95e4960 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -429,16 +429,19 @@ struct pwm_device *devm_fwnode_pwm_get(struct device *dev, #else static inline struct pwm_device *pwm_request(int pwm_id, const char *label) { + might_sleep(); return ERR_PTR(-ENODEV); } static inline void pwm_free(struct pwm_device *pwm) { + might_sleep(); } static inline int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state) { + might_sleep(); return -ENOTSUPP; } @@ -450,6 +453,7 @@ static inline int pwm_adjust_config(struct pwm_device *pwm) static inline int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns) { + might_sleep(); return -EINVAL; } @@ -462,11 +466,13 @@ static inline int pwm_capture(struct pwm_device *pwm, static inline int pwm_enable(struct pwm_device *pwm) { + might_sleep(); return -EINVAL; } static inline void pwm_disable(struct pwm_device *pwm) { + might_sleep(); } static inline int pwm_set_chip_data(struct pwm_device *pwm, void *data) @@ -493,12 +499,14 @@ static inline struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, unsigned int index, const char *label) { + might_sleep(); return ERR_PTR(-ENODEV); } static inline struct pwm_device *pwm_get(struct device *dev, const char *consumer) { + might_sleep(); return ERR_PTR(-ENODEV); } @@ -506,16 +514,19 @@ static inline struct pwm_device *of_pwm_get(struct device *dev, struct device_node *np, const char *con_id) { + might_sleep(); return ERR_PTR(-ENODEV); } static inline void pwm_put(struct pwm_device *pwm) { + might_sleep(); } static inline struct pwm_device *devm_pwm_get(struct device *dev, const char *consumer) { + might_sleep(); return ERR_PTR(-ENODEV); } @@ -523,6 +534,7 @@ static inline struct pwm_device *devm_of_pwm_get(struct device *dev, struct device_node *np, const char *con_id) { + might_sleep(); return ERR_PTR(-ENODEV); } @@ -530,6 +542,7 @@ static inline struct pwm_device * devm_fwnode_pwm_get(struct device *dev, struct fwnode_handle *fwnode, const char *con_id) { + might_sleep(); return ERR_PTR(-ENODEV); } #endif |