From cf38c978cf1d2a28deaf34842aef692a959fa9e6 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 10 May 2021 16:06:37 +0200 Subject: pwm: Make of_pwm_xlate_with_flags() work with #pwm-cells = <2> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The two functions of_pwm_simple_xlate() and of_pwm_xlate_with_flags() are quite similar. of_pwm_simple_xlate() only supports two-cell PWM specifiers while of_pwm_xlate_with_flags() only supports PWM specifiers with 3 or more cells. The latter can easily be modified to behave identically to of_pwm_simple_xlate() for two-cell PWM specifiers. This is implemented here and allows to drop of_pwm_simple_xlate() in the next commit. There is a small detail that is different now in the two-cell specifier case in of_pwm_xlate_with_flags(): pwm->args.polarity is unconditionally initialized to PWM_POLARITY_NORMAL in the latter. I didn't find a case where this matters and doing that explicitly is the more robust approach. Signed-off-by: Uwe Kleine-König [thierry.reding@gmail.com: fix up checkpatch warnings] Signed-off-by: Thierry Reding --- drivers/pwm/core.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index c4d5c0667137..6d3a1c84b053 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -126,8 +126,7 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args) { struct pwm_device *pwm; - /* check, whether the driver supports a third cell for flags */ - if (pc->of_pwm_n_cells < 3) + if (pc->of_pwm_n_cells < 2) return ERR_PTR(-EINVAL); /* flags in the third cell are optional */ @@ -144,8 +143,10 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args) pwm->args.period = args->args[1]; pwm->args.polarity = PWM_POLARITY_NORMAL; - if (args->args_count > 2 && args->args[2] & PWM_POLARITY_INVERTED) - pwm->args.polarity = PWM_POLARITY_INVERSED; + if (pc->of_pwm_n_cells >= 3) { + if (args->args_count > 2 && args->args[2] & PWM_POLARITY_INVERTED) + pwm->args.polarity = PWM_POLARITY_INVERSED; + } return pwm; } -- cgit v1.2.3 From 5447e7833629ee4208b7d41862ab0249d6b50077 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 10 May 2021 16:06:38 +0200 Subject: pwm: Drop of_pwm_simple_xlate() in favour of of_pwm_xlate_with_flags() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the previous commit the latter function can do everything that the former does. So simplify accordingly. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/core.c | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 6d3a1c84b053..821ee1959816 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -152,38 +152,13 @@ of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args) } EXPORT_SYMBOL_GPL(of_pwm_xlate_with_flags); -static struct pwm_device * -of_pwm_simple_xlate(struct pwm_chip *pc, const struct of_phandle_args *args) -{ - struct pwm_device *pwm; - - /* sanity check driver support */ - if (pc->of_pwm_n_cells < 2) - return ERR_PTR(-EINVAL); - - /* all cells are required */ - if (args->args_count != pc->of_pwm_n_cells) - return ERR_PTR(-EINVAL); - - if (args->args[0] >= pc->npwm) - return ERR_PTR(-EINVAL); - - pwm = pwm_request_from_chip(pc, args->args[0], NULL); - if (IS_ERR(pwm)) - return pwm; - - pwm->args.period = args->args[1]; - - return pwm; -} - static void of_pwmchip_add(struct pwm_chip *chip) { if (!chip->dev || !chip->dev->of_node) return; if (!chip->of_xlate) { - chip->of_xlate = of_pwm_simple_xlate; + chip->of_xlate = of_pwm_xlate_with_flags; chip->of_pwm_n_cells = 2; } -- cgit v1.2.3 From 69230cfac3d02c1b8d78aa7bdb29a1710147d49b Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 10 May 2021 16:06:39 +0200 Subject: pwm: Autodetect default value for of_pwm_n_cells from device tree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows to simplify all drivers that use three pwm-cells. The only ugly side effect is that if a driver specified of_pwm_n_cells = 2 it suddenly supports device trees that use #pwm-cells = <3>. This however isn't a bad thing because the driver doesn't need explicit support for three cells as the core handles all the details. Also there is no such in-tree driver. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/core.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 821ee1959816..c165c5822703 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -158,8 +158,14 @@ static void of_pwmchip_add(struct pwm_chip *chip) return; if (!chip->of_xlate) { + u32 pwm_cells; + + if (of_property_read_u32(chip->dev->of_node, "#pwm-cells", + &pwm_cells)) + pwm_cells = 2; + chip->of_xlate = of_pwm_xlate_with_flags; - chip->of_pwm_n_cells = 2; + chip->of_pwm_n_cells = pwm_cells; } of_node_get(chip->dev->of_node); -- cgit v1.2.3 From e9fdf122cf3442573d811c0cb664d6b5c30b7780 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 10 May 2021 16:06:40 +0200 Subject: pwm: Simplify all drivers with explicit of_pwm_n_cells = 3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the previous commit there is no need for the lowlevel driver any more to specify it it uses two or three cells. So simplify accordingly. The only non-trival change affects the pwm-rockchip driver: It used to only support three cells if the hardware supports polarity. Now the default number depends on the device tree which has to match hardware anyhow (and if it doesn't the error is just a bit delayed as a PWM handle with an inverted setting is catched when pwm_apply_state() is called). Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-atmel-hlcdc.c | 2 -- drivers/pwm/pwm-atmel-tcb.c | 2 -- drivers/pwm/pwm-atmel.c | 2 -- drivers/pwm/pwm-bcm-iproc.c | 2 -- drivers/pwm/pwm-bcm-kona.c | 2 -- drivers/pwm/pwm-bcm2835.c | 2 -- drivers/pwm/pwm-berlin.c | 2 -- drivers/pwm/pwm-fsl-ftm.c | 2 -- drivers/pwm/pwm-hibvt.c | 2 -- drivers/pwm/pwm-imx-tpm.c | 2 -- drivers/pwm/pwm-imx27.c | 3 --- drivers/pwm/pwm-jz4740.c | 2 -- drivers/pwm/pwm-lpc18xx-sct.c | 2 -- drivers/pwm/pwm-meson.c | 2 -- drivers/pwm/pwm-mxs.c | 2 -- drivers/pwm/pwm-omap-dmtimer.c | 2 -- drivers/pwm/pwm-renesas-tpu.c | 2 -- drivers/pwm/pwm-rockchip.c | 5 ----- drivers/pwm/pwm-samsung.c | 3 --- drivers/pwm/pwm-sifive.c | 2 -- drivers/pwm/pwm-stm32-lp.c | 2 -- drivers/pwm/pwm-stm32.c | 2 -- drivers/pwm/pwm-sun4i.c | 2 -- drivers/pwm/pwm-tiecap.c | 2 -- drivers/pwm/pwm-tiehrpwm.c | 2 -- drivers/pwm/pwm-vt8500.c | 2 -- 26 files changed, 57 deletions(-) diff --git a/drivers/pwm/pwm-atmel-hlcdc.c b/drivers/pwm/pwm-atmel-hlcdc.c index 6ab597e54005..4459325d3650 100644 --- a/drivers/pwm/pwm-atmel-hlcdc.c +++ b/drivers/pwm/pwm-atmel-hlcdc.c @@ -266,8 +266,6 @@ static int atmel_hlcdc_pwm_probe(struct platform_device *pdev) chip->chip.ops = &atmel_hlcdc_pwm_ops; chip->chip.dev = dev; chip->chip.npwm = 1; - chip->chip.of_xlate = of_pwm_xlate_with_flags; - chip->chip.of_pwm_n_cells = 3; ret = pwmchip_add(&chip->chip); if (ret) { diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c index 8451d3e846be..bf398f21484d 100644 --- a/drivers/pwm/pwm-atmel-tcb.c +++ b/drivers/pwm/pwm-atmel-tcb.c @@ -469,8 +469,6 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev) tcbpwm->chip.dev = &pdev->dev; tcbpwm->chip.ops = &atmel_tcb_pwm_ops; - tcbpwm->chip.of_xlate = of_pwm_xlate_with_flags; - tcbpwm->chip.of_pwm_n_cells = 3; tcbpwm->chip.npwm = NPWM; tcbpwm->channel = channel; tcbpwm->regmap = regmap; diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c index 29b5ad03f715..a8162bae3e8a 100644 --- a/drivers/pwm/pwm-atmel.c +++ b/drivers/pwm/pwm-atmel.c @@ -436,8 +436,6 @@ static int atmel_pwm_probe(struct platform_device *pdev) atmel_pwm->chip.dev = &pdev->dev; atmel_pwm->chip.ops = &atmel_pwm_ops; - atmel_pwm->chip.of_xlate = of_pwm_xlate_with_flags; - atmel_pwm->chip.of_pwm_n_cells = 3; atmel_pwm->chip.npwm = 4; ret = pwmchip_add(&atmel_pwm->chip); diff --git a/drivers/pwm/pwm-bcm-iproc.c b/drivers/pwm/pwm-bcm-iproc.c index edd2ce1760ab..0226bf697f09 100644 --- a/drivers/pwm/pwm-bcm-iproc.c +++ b/drivers/pwm/pwm-bcm-iproc.c @@ -210,8 +210,6 @@ static int iproc_pwmc_probe(struct platform_device *pdev) ip->chip.dev = &pdev->dev; ip->chip.ops = &iproc_pwm_ops; ip->chip.npwm = 4; - ip->chip.of_xlate = of_pwm_xlate_with_flags; - ip->chip.of_pwm_n_cells = 3; ip->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ip->base)) diff --git a/drivers/pwm/pwm-bcm-kona.c b/drivers/pwm/pwm-bcm-kona.c index 800b9edf2e71..8c85c66ea5c9 100644 --- a/drivers/pwm/pwm-bcm-kona.c +++ b/drivers/pwm/pwm-bcm-kona.c @@ -272,8 +272,6 @@ static int kona_pwmc_probe(struct platform_device *pdev) kp->chip.dev = &pdev->dev; kp->chip.ops = &kona_pwm_ops; kp->chip.npwm = 6; - kp->chip.of_xlate = of_pwm_xlate_with_flags; - kp->chip.of_pwm_n_cells = 3; kp->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(kp->base)) diff --git a/drivers/pwm/pwm-bcm2835.c b/drivers/pwm/pwm-bcm2835.c index fc240d5b8121..50b8594be31d 100644 --- a/drivers/pwm/pwm-bcm2835.c +++ b/drivers/pwm/pwm-bcm2835.c @@ -159,8 +159,6 @@ static int bcm2835_pwm_probe(struct platform_device *pdev) pc->chip.dev = &pdev->dev; pc->chip.ops = &bcm2835_pwm_ops; pc->chip.npwm = 2; - pc->chip.of_xlate = of_pwm_xlate_with_flags; - pc->chip.of_pwm_n_cells = 3; platform_set_drvdata(pdev, pc); diff --git a/drivers/pwm/pwm-berlin.c b/drivers/pwm/pwm-berlin.c index acb6fbc3cc32..a7fa6a4bf1c9 100644 --- a/drivers/pwm/pwm-berlin.c +++ b/drivers/pwm/pwm-berlin.c @@ -207,8 +207,6 @@ static int berlin_pwm_probe(struct platform_device *pdev) pwm->chip.dev = &pdev->dev; pwm->chip.ops = &berlin_pwm_ops; pwm->chip.npwm = 4; - pwm->chip.of_xlate = of_pwm_xlate_with_flags; - pwm->chip.of_pwm_n_cells = 3; ret = pwmchip_add(&pwm->chip); if (ret < 0) { diff --git a/drivers/pwm/pwm-fsl-ftm.c b/drivers/pwm/pwm-fsl-ftm.c index 0e1ae9469eda..96ccd772280c 100644 --- a/drivers/pwm/pwm-fsl-ftm.c +++ b/drivers/pwm/pwm-fsl-ftm.c @@ -451,8 +451,6 @@ static int fsl_pwm_probe(struct platform_device *pdev) fpc->chip.ops = &fsl_pwm_ops; - fpc->chip.of_xlate = of_pwm_xlate_with_flags; - fpc->chip.of_pwm_n_cells = 3; fpc->chip.npwm = 8; ret = pwmchip_add(&fpc->chip); diff --git a/drivers/pwm/pwm-hibvt.c b/drivers/pwm/pwm-hibvt.c index 82d17fc75c21..4a6e9ad3c0ff 100644 --- a/drivers/pwm/pwm-hibvt.c +++ b/drivers/pwm/pwm-hibvt.c @@ -206,8 +206,6 @@ static int hibvt_pwm_probe(struct platform_device *pdev) pwm_chip->chip.ops = &hibvt_pwm_ops; pwm_chip->chip.dev = &pdev->dev; pwm_chip->chip.npwm = soc->num_pwms; - pwm_chip->chip.of_xlate = of_pwm_xlate_with_flags; - pwm_chip->chip.of_pwm_n_cells = 3; pwm_chip->soc = soc; pwm_chip->base = devm_platform_ioremap_resource(pdev, 0); diff --git a/drivers/pwm/pwm-imx-tpm.c b/drivers/pwm/pwm-imx-tpm.c index 97c9133b6876..dbb50493abdd 100644 --- a/drivers/pwm/pwm-imx-tpm.c +++ b/drivers/pwm/pwm-imx-tpm.c @@ -363,8 +363,6 @@ static int pwm_imx_tpm_probe(struct platform_device *pdev) tpm->chip.dev = &pdev->dev; tpm->chip.ops = &imx_tpm_pwm_ops; - tpm->chip.of_xlate = of_pwm_xlate_with_flags; - tpm->chip.of_pwm_n_cells = 3; /* get number of channels */ val = readl(tpm->base + PWM_IMX_TPM_PARAM); diff --git a/drivers/pwm/pwm-imx27.c b/drivers/pwm/pwm-imx27.c index ba695115c160..f6588a96fbd9 100644 --- a/drivers/pwm/pwm-imx27.c +++ b/drivers/pwm/pwm-imx27.c @@ -329,9 +329,6 @@ static int pwm_imx27_probe(struct platform_device *pdev) imx->chip.dev = &pdev->dev; imx->chip.npwm = 1; - imx->chip.of_xlate = of_pwm_xlate_with_flags; - imx->chip.of_pwm_n_cells = 3; - imx->mmio_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(imx->mmio_base)) return PTR_ERR(imx->mmio_base); diff --git a/drivers/pwm/pwm-jz4740.c b/drivers/pwm/pwm-jz4740.c index 5b6bdcdcecf5..990e7904c7f1 100644 --- a/drivers/pwm/pwm-jz4740.c +++ b/drivers/pwm/pwm-jz4740.c @@ -244,8 +244,6 @@ static int jz4740_pwm_probe(struct platform_device *pdev) jz4740->chip.dev = dev; jz4740->chip.ops = &jz4740_pwm_ops; jz4740->chip.npwm = info->num_pwms; - jz4740->chip.of_xlate = of_pwm_xlate_with_flags; - jz4740->chip.of_pwm_n_cells = 3; platform_set_drvdata(pdev, jz4740); diff --git a/drivers/pwm/pwm-lpc18xx-sct.c b/drivers/pwm/pwm-lpc18xx-sct.c index b643ac61a2e7..8e461f3baa05 100644 --- a/drivers/pwm/pwm-lpc18xx-sct.c +++ b/drivers/pwm/pwm-lpc18xx-sct.c @@ -371,8 +371,6 @@ static int lpc18xx_pwm_probe(struct platform_device *pdev) lpc18xx_pwm->chip.dev = &pdev->dev; lpc18xx_pwm->chip.ops = &lpc18xx_pwm_ops; lpc18xx_pwm->chip.npwm = 16; - lpc18xx_pwm->chip.of_xlate = of_pwm_xlate_with_flags; - lpc18xx_pwm->chip.of_pwm_n_cells = 3; /* SCT counter must be in unify (32 bit) mode */ lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_CONFIG, diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c index 9eb060613cb4..595afec53a2d 100644 --- a/drivers/pwm/pwm-meson.c +++ b/drivers/pwm/pwm-meson.c @@ -551,8 +551,6 @@ static int meson_pwm_probe(struct platform_device *pdev) meson->chip.dev = &pdev->dev; meson->chip.ops = &meson_pwm_ops; meson->chip.npwm = MESON_NUM_PWMS; - meson->chip.of_xlate = of_pwm_xlate_with_flags; - meson->chip.of_pwm_n_cells = 3; meson->data = of_device_get_match_data(&pdev->dev); diff --git a/drivers/pwm/pwm-mxs.c b/drivers/pwm/pwm-mxs.c index 0266e84e982c..a22180803bd7 100644 --- a/drivers/pwm/pwm-mxs.c +++ b/drivers/pwm/pwm-mxs.c @@ -138,8 +138,6 @@ static int mxs_pwm_probe(struct platform_device *pdev) mxs->chip.dev = &pdev->dev; mxs->chip.ops = &mxs_pwm_ops; - mxs->chip.of_xlate = of_pwm_xlate_with_flags; - mxs->chip.of_pwm_n_cells = 3; ret = of_property_read_u32(np, "fsl,pwm-number", &mxs->chip.npwm); if (ret < 0) { diff --git a/drivers/pwm/pwm-omap-dmtimer.c b/drivers/pwm/pwm-omap-dmtimer.c index 612b3c859295..507a2d945b90 100644 --- a/drivers/pwm/pwm-omap-dmtimer.c +++ b/drivers/pwm/pwm-omap-dmtimer.c @@ -404,8 +404,6 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev) omap->chip.dev = &pdev->dev; omap->chip.ops = &pwm_omap_dmtimer_ops; omap->chip.npwm = 1; - omap->chip.of_xlate = of_pwm_xlate_with_flags; - omap->chip.of_pwm_n_cells = 3; mutex_init(&omap->mutex); diff --git a/drivers/pwm/pwm-renesas-tpu.c b/drivers/pwm/pwm-renesas-tpu.c index e2959fae0969..b853e7942605 100644 --- a/drivers/pwm/pwm-renesas-tpu.c +++ b/drivers/pwm/pwm-renesas-tpu.c @@ -408,8 +408,6 @@ static int tpu_probe(struct platform_device *pdev) tpu->chip.dev = &pdev->dev; tpu->chip.ops = &tpu_pwm_ops; - tpu->chip.of_xlate = of_pwm_xlate_with_flags; - tpu->chip.of_pwm_n_cells = 3; tpu->chip.npwm = TPU_CHANNEL_MAX; pm_runtime_enable(&pdev->dev); diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c index 301785fa293e..cbe900877724 100644 --- a/drivers/pwm/pwm-rockchip.c +++ b/drivers/pwm/pwm-rockchip.c @@ -354,11 +354,6 @@ static int rockchip_pwm_probe(struct platform_device *pdev) pc->chip.ops = &rockchip_pwm_ops; pc->chip.npwm = 1; - if (pc->data->supports_polarity) { - pc->chip.of_xlate = of_pwm_xlate_with_flags; - pc->chip.of_pwm_n_cells = 3; - } - enable_conf = pc->data->enable_conf; ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl); enabled = (ctrl & enable_conf) == enable_conf; diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c index 515489fa4f6d..f6c528f02d43 100644 --- a/drivers/pwm/pwm-samsung.c +++ b/drivers/pwm/pwm-samsung.c @@ -526,9 +526,6 @@ static int pwm_samsung_probe(struct platform_device *pdev) ret = pwm_samsung_parse_dt(chip); if (ret) return ret; - - chip->chip.of_xlate = of_pwm_xlate_with_flags; - chip->chip.of_pwm_n_cells = 3; } else { if (!pdev->dev.platform_data) { dev_err(&pdev->dev, "no platform data specified\n"); diff --git a/drivers/pwm/pwm-sifive.c b/drivers/pwm/pwm-sifive.c index 688737f091ac..420edc4aa94a 100644 --- a/drivers/pwm/pwm-sifive.c +++ b/drivers/pwm/pwm-sifive.c @@ -242,8 +242,6 @@ static int pwm_sifive_probe(struct platform_device *pdev) chip = &ddata->chip; chip->dev = dev; chip->ops = &pwm_sifive_ops; - chip->of_xlate = of_pwm_xlate_with_flags; - chip->of_pwm_n_cells = 3; chip->npwm = 4; ddata->regs = devm_platform_ioremap_resource(pdev, 0); diff --git a/drivers/pwm/pwm-stm32-lp.c b/drivers/pwm/pwm-stm32-lp.c index af08f564ef1d..93dd03618465 100644 --- a/drivers/pwm/pwm-stm32-lp.c +++ b/drivers/pwm/pwm-stm32-lp.c @@ -208,8 +208,6 @@ static int stm32_pwm_lp_probe(struct platform_device *pdev) priv->chip.dev = &pdev->dev; priv->chip.ops = &stm32_pwm_lp_ops; priv->chip.npwm = 1; - priv->chip.of_xlate = of_pwm_xlate_with_flags; - priv->chip.of_pwm_n_cells = 3; ret = pwmchip_add(&priv->chip); if (ret < 0) diff --git a/drivers/pwm/pwm-stm32.c b/drivers/pwm/pwm-stm32.c index c46fb90036ab..794ca5b02968 100644 --- a/drivers/pwm/pwm-stm32.c +++ b/drivers/pwm/pwm-stm32.c @@ -621,8 +621,6 @@ static int stm32_pwm_probe(struct platform_device *pdev) priv->regmap = ddata->regmap; priv->clk = ddata->clk; priv->max_arr = ddata->max_arr; - priv->chip.of_xlate = of_pwm_xlate_with_flags; - priv->chip.of_pwm_n_cells = 3; if (!priv->regmap || !priv->clk) return -EINVAL; diff --git a/drivers/pwm/pwm-sun4i.c b/drivers/pwm/pwm-sun4i.c index e01becd102c0..c952604e91f3 100644 --- a/drivers/pwm/pwm-sun4i.c +++ b/drivers/pwm/pwm-sun4i.c @@ -460,8 +460,6 @@ static int sun4i_pwm_probe(struct platform_device *pdev) pwm->chip.dev = &pdev->dev; pwm->chip.ops = &sun4i_pwm_ops; pwm->chip.npwm = pwm->data->npwm; - pwm->chip.of_xlate = of_pwm_xlate_with_flags; - pwm->chip.of_pwm_n_cells = 3; spin_lock_init(&pwm->ctrl_lock); diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c index b9a17ab0c202..f40975fcb195 100644 --- a/drivers/pwm/pwm-tiecap.c +++ b/drivers/pwm/pwm-tiecap.c @@ -224,8 +224,6 @@ static int ecap_pwm_probe(struct platform_device *pdev) pc->chip.dev = &pdev->dev; pc->chip.ops = &ecap_pwm_ops; - pc->chip.of_xlate = of_pwm_xlate_with_flags; - pc->chip.of_pwm_n_cells = 3; pc->chip.npwm = 1; pc->mmio_base = devm_platform_ioremap_resource(pdev, 0); diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c index 90095a19bf2d..17909fa53211 100644 --- a/drivers/pwm/pwm-tiehrpwm.c +++ b/drivers/pwm/pwm-tiehrpwm.c @@ -447,8 +447,6 @@ static int ehrpwm_pwm_probe(struct platform_device *pdev) pc->chip.dev = &pdev->dev; pc->chip.ops = &ehrpwm_pwm_ops; - pc->chip.of_xlate = of_pwm_xlate_with_flags; - pc->chip.of_pwm_n_cells = 3; pc->chip.npwm = NUM_PWM_CHANNEL; pc->mmio_base = devm_platform_ioremap_resource(pdev, 0); diff --git a/drivers/pwm/pwm-vt8500.c b/drivers/pwm/pwm-vt8500.c index 52fe5d19473a..f9eb36be9088 100644 --- a/drivers/pwm/pwm-vt8500.c +++ b/drivers/pwm/pwm-vt8500.c @@ -207,8 +207,6 @@ static int vt8500_pwm_probe(struct platform_device *pdev) chip->chip.dev = &pdev->dev; chip->chip.ops = &vt8500_pwm_ops; - chip->chip.of_xlate = of_pwm_xlate_with_flags; - chip->chip.of_pwm_n_cells = 3; chip->chip.npwm = VT8500_NR_PWMS; chip->clk = devm_clk_get(&pdev->dev, NULL); -- cgit v1.2.3 From 937efa29e70f7f8424b74631375dcb35d82a4614 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 26 Apr 2021 17:03:50 +0200 Subject: pwm: visconti: Fix and simplify period calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the original code a request for period = 65536000 ns and period = 32768000 ns yields the same register settings (which results in 32768000 ns) because the value for pwmc0 was miscalculated. Also simplify using that fls(0) is 0. Fixes: 721b595744f1 ("pwm: visconti: Add Toshiba Visconti SoC PWM support") Signed-off-by: Uwe Kleine-König Acked-by: Nobuhiro Iwamatsu Signed-off-by: Thierry Reding --- drivers/pwm/pwm-visconti.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/pwm/pwm-visconti.c b/drivers/pwm/pwm-visconti.c index 46d903786366..af4e37d3e3a6 100644 --- a/drivers/pwm/pwm-visconti.c +++ b/drivers/pwm/pwm-visconti.c @@ -82,17 +82,14 @@ static int visconti_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, return -ERANGE; /* - * PWMC controls a divider that divides the input clk by a - * power of two between 1 and 8. As a smaller divider yields - * higher precision, pick the smallest possible one. + * PWMC controls a divider that divides the input clk by a power of two + * between 1 and 8. As a smaller divider yields higher precision, pick + * the smallest possible one. As period is at most 0xffff << 3, pwmc0 is + * in the intended range [0..3]. */ - if (period > 0xffff) { - pwmc0 = ilog2(period >> 16); - if (WARN_ON(pwmc0 > 3)) - return -EINVAL; - } else { - pwmc0 = 0; - } + pwmc0 = fls(period >> 16); + if (WARN_ON(pwmc0 > 3)) + return -EINVAL; period >>= pwmc0; duty_cycle >>= pwmc0; -- cgit v1.2.3 From b601a18f12383001e7a8da238de7ca1559ebc450 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 28 Apr 2021 11:05:24 +0200 Subject: pwm: spear: Don't modify HW state in .remove callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A consumer is expected to disable a PWM before calling pwm_put(). And if they didn't there is hopefully a good reason (or the consumer needs fixing). Also if disabling an enabled PWM was the right thing to do, this should better be done in the framework instead of in each low level driver. So drop the hardware modification from the .remove() callback. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-spear.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/pwm/pwm-spear.c b/drivers/pwm/pwm-spear.c index 1a1cedfd11ce..6879b49581b3 100644 --- a/drivers/pwm/pwm-spear.c +++ b/drivers/pwm/pwm-spear.c @@ -228,10 +228,6 @@ static int spear_pwm_probe(struct platform_device *pdev) static int spear_pwm_remove(struct platform_device *pdev) { struct spear_pwm_chip *pc = platform_get_drvdata(pdev); - int i; - - for (i = 0; i < NUM_PWM; i++) - pwm_disable(&pc->chip.pwms[i]); /* clk was prepared in probe, hence unprepare it here */ clk_unprepare(pc->clk); -- cgit v1.2.3 From da0dea8912697f725d5f1386a38cb035222e7468 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 28 Apr 2021 11:05:25 +0200 Subject: pwm: spear: Free resources only after pwmchip_remove() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before pwmchip_remove() returns the PWM is expected to be functional. So remove the pwmchip before disabling the clocks. The check for pwmchip_remove()'s return value is dropped as this function returns effectively always 0 and returning an error in a remove callback is useless anyhow (as the device core ignores it and drops devm allocated resources). Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-spear.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-spear.c b/drivers/pwm/pwm-spear.c index 6879b49581b3..05a19e70d570 100644 --- a/drivers/pwm/pwm-spear.c +++ b/drivers/pwm/pwm-spear.c @@ -229,9 +229,12 @@ static int spear_pwm_remove(struct platform_device *pdev) { struct spear_pwm_chip *pc = platform_get_drvdata(pdev); + pwmchip_remove(&pc->chip); + /* clk was prepared in probe, hence unprepare it here */ clk_unprepare(pc->clk); - return pwmchip_remove(&pc->chip); + + return 0; } static const struct of_device_id spear_pwm_of_match[] = { -- cgit v1.2.3 From 98761ce4b91b77e0602b1551d11925e817e8a9a5 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 28 Apr 2021 11:05:26 +0200 Subject: pwm: spear: Implement .apply() callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just using the previous callbacks to implment a similar procedure as the legacy handling in the core. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-spear.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/drivers/pwm/pwm-spear.c b/drivers/pwm/pwm-spear.c index 05a19e70d570..48c31dac2f32 100644 --- a/drivers/pwm/pwm-spear.c +++ b/drivers/pwm/pwm-spear.c @@ -75,7 +75,7 @@ static inline void spear_pwm_writel(struct spear_pwm_chip *chip, } static int spear_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) + u64 duty_ns, u64 period_ns) { struct spear_pwm_chip *pc = to_spear_pwm_chip(chip); u64 val, div, clk_rate; @@ -163,10 +163,35 @@ static void spear_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) clk_disable(pc->clk); } +static int spear_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + int err; + + if (state->polarity != PWM_POLARITY_NORMAL) + return -EINVAL; + + if (!state->enabled) { + if (pwm->state.enabled) + spear_pwm_disable(chip, pwm); + return 0; + } + + if (state->period != pwm->state.period || + state->duty_cycle != pwm->state.duty_cycle) { + err = spear_pwm_config(chip, pwm, state->duty_cycle, state->period); + if (err) + return err; + } + + if (!pwm->state.enabled) + return spear_pwm_enable(chip, pwm); + + return 0; +} + static const struct pwm_ops spear_pwm_ops = { - .config = spear_pwm_config, - .enable = spear_pwm_enable, - .disable = spear_pwm_disable, + .apply = spear_pwm_apply, .owner = THIS_MODULE, }; -- cgit v1.2.3 From 9e40ee18a1dc1623a5368d6232aaed52fd29dada Mon Sep 17 00:00:00 2001 From: Clemens Gruber Date: Fri, 7 May 2021 15:18:42 +0200 Subject: pwm: core: Support new usage_power setting in PWM state If usage_power is set, the PWM driver is only required to maintain the power output but has more freedom regarding signal form. If supported, the signal can be optimized, for example to improve EMI by phase shifting individual channels. Signed-off-by: Clemens Gruber Signed-off-by: Thierry Reding --- Documentation/driver-api/pwm.rst | 4 ++++ drivers/pwm/core.c | 6 +++++- include/linux/pwm.h | 7 +++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Documentation/driver-api/pwm.rst b/Documentation/driver-api/pwm.rst index a7ca4f58305a..750734a7f874 100644 --- a/Documentation/driver-api/pwm.rst +++ b/Documentation/driver-api/pwm.rst @@ -48,6 +48,10 @@ After being requested, a PWM has to be configured using:: This API controls both the PWM period/duty_cycle config and the enable/disable state. +There is also a usage_power setting: If set, the PWM driver is only required to +maintain the power output but has more freedom regarding signal form. +If supported by the driver, the signal can be optimized, for example to improve +EMI by phase shifting the individual channels of a chip. The pwm_config(), pwm_enable() and pwm_disable() functions are just wrappers around pwm_apply_state() and should not be used if the user wants to change diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index c165c5822703..a42999f877d2 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -536,7 +536,8 @@ int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state) if (state->period == pwm->state.period && state->duty_cycle == pwm->state.duty_cycle && state->polarity == pwm->state.polarity && - state->enabled == pwm->state.enabled) + state->enabled == pwm->state.enabled && + state->usage_power == pwm->state.usage_power) return 0; if (chip->ops->apply) { @@ -1241,6 +1242,9 @@ static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s) seq_printf(s, " polarity: %s", state.polarity ? "inverse" : "normal"); + if (state.usage_power) + seq_puts(s, " usage_power"); + seq_puts(s, "\n"); } } diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 5bb90af4997e..5a73251d28e3 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -54,12 +54,17 @@ enum { * @duty_cycle: PWM duty cycle (in nanoseconds) * @polarity: PWM polarity * @enabled: PWM enabled status + * @usage_power: If set, the PWM driver is only required to maintain the power + * output but has more freedom regarding signal form. + * If supported, the signal can be optimized, for example to + * improve EMI by phase shifting individual channels. */ struct pwm_state { u64 period; u64 duty_cycle; enum pwm_polarity polarity; bool enabled; + bool usage_power; }; /** @@ -188,6 +193,7 @@ static inline void pwm_init_state(const struct pwm_device *pwm, state->period = args.period; state->polarity = args.polarity; state->duty_cycle = 0; + state->usage_power = false; } /** @@ -558,6 +564,7 @@ static inline void pwm_apply_args(struct pwm_device *pwm) state.enabled = false; state.polarity = pwm->args.polarity; state.period = pwm->args.period; + state.usage_power = false; pwm_apply_state(pwm, &state); } -- cgit v1.2.3 From ae16db1fd3a1b8d1713ba6af5cf27be32918d2b8 Mon Sep 17 00:00:00 2001 From: Clemens Gruber Date: Fri, 7 May 2021 15:18:43 +0200 Subject: pwm: pca9685: Support new usage_power setting in PWM state If usage_power is set, the pca9685 driver will phase shift the individual channels relative to their channel number. This improves EMI because the enabled channels no longer turn on at the same time, while still maintaining the configured duty cycle / power output. Signed-off-by: Clemens Gruber Signed-off-by: Thierry Reding --- drivers/pwm/pwm-pca9685.c | 61 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c index 7c9f174de64e..20dd579297e6 100644 --- a/drivers/pwm/pwm-pca9685.c +++ b/drivers/pwm/pwm-pca9685.c @@ -93,48 +93,77 @@ static inline struct pca9685 *to_pca(struct pwm_chip *chip) /* Helper function to set the duty cycle ratio to duty/4096 (e.g. duty=2048 -> 50%) */ static void pca9685_pwm_set_duty(struct pca9685 *pca, int channel, unsigned int duty) { + struct pwm_device *pwm = &pca->chip.pwms[channel]; + unsigned int on, off; + if (duty == 0) { /* Set the full OFF bit, which has the highest precedence */ regmap_write(pca->regmap, REG_OFF_H(channel), LED_FULL); + return; } else if (duty >= PCA9685_COUNTER_RANGE) { /* Set the full ON bit and clear the full OFF bit */ regmap_write(pca->regmap, REG_ON_H(channel), LED_FULL); regmap_write(pca->regmap, REG_OFF_H(channel), 0); - } else { - /* Set OFF time (clears the full OFF bit) */ - regmap_write(pca->regmap, REG_OFF_L(channel), duty & 0xff); - regmap_write(pca->regmap, REG_OFF_H(channel), (duty >> 8) & 0xf); - /* Clear the full ON bit */ - regmap_write(pca->regmap, REG_ON_H(channel), 0); + return; } + + + if (pwm->state.usage_power && channel < PCA9685_MAXCHAN) { + /* + * If usage_power is set, the pca9685 driver will phase shift + * the individual channels relative to their channel number. + * This improves EMI because the enabled channels no longer + * turn on at the same time, while still maintaining the + * configured duty cycle / power output. + */ + on = channel * PCA9685_COUNTER_RANGE / PCA9685_MAXCHAN; + } else + on = 0; + + off = (on + duty) % PCA9685_COUNTER_RANGE; + + /* Set ON time (clears full ON bit) */ + regmap_write(pca->regmap, REG_ON_L(channel), on & 0xff); + regmap_write(pca->regmap, REG_ON_H(channel), (on >> 8) & 0xf); + /* Set OFF time (clears full OFF bit) */ + regmap_write(pca->regmap, REG_OFF_L(channel), off & 0xff); + regmap_write(pca->regmap, REG_OFF_H(channel), (off >> 8) & 0xf); } static unsigned int pca9685_pwm_get_duty(struct pca9685 *pca, int channel) { - unsigned int off_h = 0, val = 0; + struct pwm_device *pwm = &pca->chip.pwms[channel]; + unsigned int off = 0, on = 0, val = 0; if (WARN_ON(channel >= PCA9685_MAXCHAN)) { /* HW does not support reading state of "all LEDs" channel */ return 0; } - regmap_read(pca->regmap, LED_N_OFF_H(channel), &off_h); - if (off_h & LED_FULL) { + regmap_read(pca->regmap, LED_N_OFF_H(channel), &off); + if (off & LED_FULL) { /* Full OFF bit is set */ return 0; } - regmap_read(pca->regmap, LED_N_ON_H(channel), &val); - if (val & LED_FULL) { + regmap_read(pca->regmap, LED_N_ON_H(channel), &on); + if (on & LED_FULL) { /* Full ON bit is set */ return PCA9685_COUNTER_RANGE; } - if (regmap_read(pca->regmap, LED_N_OFF_L(channel), &val)) { - /* Reset val to 0 in case reading LED_N_OFF_L failed */ + regmap_read(pca->regmap, LED_N_OFF_L(channel), &val); + off = ((off & 0xf) << 8) | (val & 0xff); + if (!pwm->state.usage_power) + return off; + + /* Read ON register to calculate duty cycle of staggered output */ + if (regmap_read(pca->regmap, LED_N_ON_L(channel), &val)) { + /* Reset val to 0 in case reading LED_N_ON_L failed */ val = 0; } - return ((off_h & 0xf) << 8) | (val & 0xff); + on = ((on & 0xf) << 8) | (val & 0xff); + return (off - on) & (PCA9685_COUNTER_RANGE - 1); } #if IS_ENABLED(CONFIG_GPIOLIB) @@ -441,9 +470,11 @@ static int pca9685_pwm_probe(struct i2c_client *client, reg &= ~(MODE1_ALLCALL | MODE1_SUB1 | MODE1_SUB2 | MODE1_SUB3); regmap_write(pca->regmap, PCA9685_MODE1, reg); - /* Reset OFF registers to POR default */ + /* Reset OFF/ON registers to POR default */ regmap_write(pca->regmap, PCA9685_ALL_LED_OFF_L, LED_FULL); regmap_write(pca->regmap, PCA9685_ALL_LED_OFF_H, LED_FULL); + regmap_write(pca->regmap, PCA9685_ALL_LED_ON_L, 0); + regmap_write(pca->regmap, PCA9685_ALL_LED_ON_H, 0); pca->chip.ops = &pca9685_pwm_ops; /* Add an extra channel for ALL_LED */ -- cgit v1.2.3 From 6d6e7050276d40b5de97aa950d5d71057f2e2a25 Mon Sep 17 00:00:00 2001 From: Clemens Gruber Date: Fri, 7 May 2021 15:18:44 +0200 Subject: pwm: pca9685: Restrict period change for enabled PWMs Previously, the last used PWM channel could change the global prescale setting, even if other channels are already in use. Fix it by only allowing the first enabled PWM to change the global chip-wide prescale setting. If there is more than one channel in use, the prescale settings resulting from the chosen periods must match. GPIOs do not count as enabled PWMs as they are not using the prescaler and can't change it. Signed-off-by: Clemens Gruber Signed-off-by: Thierry Reding --- drivers/pwm/pwm-pca9685.c | 74 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 64 insertions(+), 10 deletions(-) diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c index 20dd579297e6..f70162114197 100644 --- a/drivers/pwm/pwm-pca9685.c +++ b/drivers/pwm/pwm-pca9685.c @@ -23,11 +23,11 @@ #include /* - * Because the PCA9685 has only one prescaler per chip, changing the period of - * one channel affects the period of all 16 PWM outputs! - * However, the ratio between each configured duty cycle and the chip-wide - * period remains constant, because the OFF time is set in proportion to the - * counter range. + * Because the PCA9685 has only one prescaler per chip, only the first channel + * that is enabled is allowed to change the prescale register. + * PWM channels requested afterwards must use a period that results in the same + * prescale setting as the one set by the first requested channel. + * GPIOs do not count as enabled PWMs as they are not using the prescaler. */ #define PCA9685_MODE1 0x00 @@ -78,8 +78,9 @@ struct pca9685 { struct pwm_chip chip; struct regmap *regmap; -#if IS_ENABLED(CONFIG_GPIOLIB) struct mutex lock; + DECLARE_BITMAP(pwms_enabled, PCA9685_MAXCHAN + 1); +#if IS_ENABLED(CONFIG_GPIOLIB) struct gpio_chip gpio; DECLARE_BITMAP(pwms_inuse, PCA9685_MAXCHAN + 1); #endif @@ -90,6 +91,22 @@ static inline struct pca9685 *to_pca(struct pwm_chip *chip) return container_of(chip, struct pca9685, chip); } +/* This function is supposed to be called with the lock mutex held */ +static bool pca9685_prescaler_can_change(struct pca9685 *pca, int channel) +{ + /* No PWM enabled: Change allowed */ + if (bitmap_empty(pca->pwms_enabled, PCA9685_MAXCHAN + 1)) + return true; + /* More than one PWM enabled: Change not allowed */ + if (bitmap_weight(pca->pwms_enabled, PCA9685_MAXCHAN + 1) > 1) + return false; + /* + * Only one PWM enabled: Change allowed if the PWM about to + * be changed is the one that is already enabled + */ + return test_bit(channel, pca->pwms_enabled); +} + /* Helper function to set the duty cycle ratio to duty/4096 (e.g. duty=2048 -> 50%) */ static void pca9685_pwm_set_duty(struct pca9685 *pca, int channel, unsigned int duty) { @@ -269,8 +286,6 @@ static int pca9685_pwm_gpio_probe(struct pca9685 *pca) { struct device *dev = pca->chip.dev; - mutex_init(&pca->lock); - pca->gpio.label = dev_name(dev); pca->gpio.parent = dev; pca->gpio.request = pca9685_pwm_gpio_request; @@ -314,8 +329,8 @@ static void pca9685_set_sleep_mode(struct pca9685 *pca, bool enable) } } -static int pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, - const struct pwm_state *state) +static int __pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) { struct pca9685 *pca = to_pca(chip); unsigned long long duty, prescale; @@ -338,6 +353,12 @@ static int pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, regmap_read(pca->regmap, PCA9685_PRESCALE, &val); if (prescale != val) { + if (!pca9685_prescaler_can_change(pca, pwm->hwpwm)) { + dev_err(chip->dev, + "pwm not changed: periods of enabled pwms must match!\n"); + return -EBUSY; + } + /* * Putting the chip briefly into SLEEP mode * at this point won't interfere with the @@ -360,6 +381,25 @@ static int pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, return 0; } +static int pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct pca9685 *pca = to_pca(chip); + int ret; + + mutex_lock(&pca->lock); + ret = __pca9685_pwm_apply(chip, pwm, state); + if (ret == 0) { + if (state->enabled) + set_bit(pwm->hwpwm, pca->pwms_enabled); + else + clear_bit(pwm->hwpwm, pca->pwms_enabled); + } + mutex_unlock(&pca->lock); + + return ret; +} + static void pca9685_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state) { @@ -401,6 +441,14 @@ static int pca9685_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) if (pca9685_pwm_test_and_set_inuse(pca, pwm->hwpwm)) return -EBUSY; + + if (pwm->hwpwm < PCA9685_MAXCHAN) { + /* PWMs - except the "all LEDs" channel - default to enabled */ + mutex_lock(&pca->lock); + set_bit(pwm->hwpwm, pca->pwms_enabled); + mutex_unlock(&pca->lock); + } + pm_runtime_get_sync(chip->dev); return 0; @@ -410,7 +458,11 @@ static void pca9685_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) { struct pca9685 *pca = to_pca(chip); + mutex_lock(&pca->lock); pca9685_pwm_set_duty(pca, pwm->hwpwm, 0); + clear_bit(pwm->hwpwm, pca->pwms_enabled); + mutex_unlock(&pca->lock); + pm_runtime_put(chip->dev); pca9685_pwm_clear_inuse(pca, pwm->hwpwm); } @@ -451,6 +503,8 @@ static int pca9685_pwm_probe(struct i2c_client *client, i2c_set_clientdata(client, pca); + mutex_init(&pca->lock); + regmap_read(pca->regmap, PCA9685_MODE2, ®); if (device_property_read_bool(&client->dev, "invert")) -- cgit v1.2.3 From 79dd354fe1769ebec695dacfee007eafb1538d0c Mon Sep 17 00:00:00 2001 From: Clemens Gruber Date: Fri, 7 May 2021 15:18:45 +0200 Subject: pwm: pca9685: Add error messages for failed regmap calls Regmap operations can fail if the underlying subsystem is not working properly (e.g. hogged I2C bus, etc.) As this is useful information for the user, print an error message if it happens. Let probe fail if the first regmap_read or the first regmap_write fails. Signed-off-by: Clemens Gruber Signed-off-by: Thierry Reding --- drivers/pwm/pwm-pca9685.c | 83 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 24 deletions(-) diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c index f70162114197..42ed770b432c 100644 --- a/drivers/pwm/pwm-pca9685.c +++ b/drivers/pwm/pwm-pca9685.c @@ -107,6 +107,30 @@ static bool pca9685_prescaler_can_change(struct pca9685 *pca, int channel) return test_bit(channel, pca->pwms_enabled); } +static int pca9685_read_reg(struct pca9685 *pca, unsigned int reg, unsigned int *val) +{ + struct device *dev = pca->chip.dev; + int err; + + err = regmap_read(pca->regmap, reg, val); + if (err) + dev_err(dev, "regmap_read of register 0x%x failed: %pe\n", reg, ERR_PTR(err)); + + return err; +} + +static int pca9685_write_reg(struct pca9685 *pca, unsigned int reg, unsigned int val) +{ + struct device *dev = pca->chip.dev; + int err; + + err = regmap_write(pca->regmap, reg, val); + if (err) + dev_err(dev, "regmap_write to register 0x%x failed: %pe\n", reg, ERR_PTR(err)); + + return err; +} + /* Helper function to set the duty cycle ratio to duty/4096 (e.g. duty=2048 -> 50%) */ static void pca9685_pwm_set_duty(struct pca9685 *pca, int channel, unsigned int duty) { @@ -115,12 +139,12 @@ static void pca9685_pwm_set_duty(struct pca9685 *pca, int channel, unsigned int if (duty == 0) { /* Set the full OFF bit, which has the highest precedence */ - regmap_write(pca->regmap, REG_OFF_H(channel), LED_FULL); + pca9685_write_reg(pca, REG_OFF_H(channel), LED_FULL); return; } else if (duty >= PCA9685_COUNTER_RANGE) { /* Set the full ON bit and clear the full OFF bit */ - regmap_write(pca->regmap, REG_ON_H(channel), LED_FULL); - regmap_write(pca->regmap, REG_OFF_H(channel), 0); + pca9685_write_reg(pca, REG_ON_H(channel), LED_FULL); + pca9685_write_reg(pca, REG_OFF_H(channel), 0); return; } @@ -140,11 +164,11 @@ static void pca9685_pwm_set_duty(struct pca9685 *pca, int channel, unsigned int off = (on + duty) % PCA9685_COUNTER_RANGE; /* Set ON time (clears full ON bit) */ - regmap_write(pca->regmap, REG_ON_L(channel), on & 0xff); - regmap_write(pca->regmap, REG_ON_H(channel), (on >> 8) & 0xf); + pca9685_write_reg(pca, REG_ON_L(channel), on & 0xff); + pca9685_write_reg(pca, REG_ON_H(channel), (on >> 8) & 0xf); /* Set OFF time (clears full OFF bit) */ - regmap_write(pca->regmap, REG_OFF_L(channel), off & 0xff); - regmap_write(pca->regmap, REG_OFF_H(channel), (off >> 8) & 0xf); + pca9685_write_reg(pca, REG_OFF_L(channel), off & 0xff); + pca9685_write_reg(pca, REG_OFF_H(channel), (off >> 8) & 0xf); } static unsigned int pca9685_pwm_get_duty(struct pca9685 *pca, int channel) @@ -157,25 +181,25 @@ static unsigned int pca9685_pwm_get_duty(struct pca9685 *pca, int channel) return 0; } - regmap_read(pca->regmap, LED_N_OFF_H(channel), &off); + pca9685_read_reg(pca, LED_N_OFF_H(channel), &off); if (off & LED_FULL) { /* Full OFF bit is set */ return 0; } - regmap_read(pca->regmap, LED_N_ON_H(channel), &on); + pca9685_read_reg(pca, LED_N_ON_H(channel), &on); if (on & LED_FULL) { /* Full ON bit is set */ return PCA9685_COUNTER_RANGE; } - regmap_read(pca->regmap, LED_N_OFF_L(channel), &val); + pca9685_read_reg(pca, LED_N_OFF_L(channel), &val); off = ((off & 0xf) << 8) | (val & 0xff); if (!pwm->state.usage_power) return off; /* Read ON register to calculate duty cycle of staggered output */ - if (regmap_read(pca->regmap, LED_N_ON_L(channel), &val)) { + if (pca9685_read_reg(pca, LED_N_ON_L(channel), &val)) { /* Reset val to 0 in case reading LED_N_ON_L failed */ val = 0; } @@ -321,8 +345,15 @@ static inline int pca9685_pwm_gpio_probe(struct pca9685 *pca) static void pca9685_set_sleep_mode(struct pca9685 *pca, bool enable) { - regmap_update_bits(pca->regmap, PCA9685_MODE1, - MODE1_SLEEP, enable ? MODE1_SLEEP : 0); + struct device *dev = pca->chip.dev; + int err = regmap_update_bits(pca->regmap, PCA9685_MODE1, + MODE1_SLEEP, enable ? MODE1_SLEEP : 0); + if (err) { + dev_err(dev, "regmap_update_bits of register 0x%x failed: %pe\n", + PCA9685_MODE1, ERR_PTR(err)); + return; + } + if (!enable) { /* Wait 500us for the oscillator to be back up */ udelay(500); @@ -351,7 +382,7 @@ static int __pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, return 0; } - regmap_read(pca->regmap, PCA9685_PRESCALE, &val); + pca9685_read_reg(pca, PCA9685_PRESCALE, &val); if (prescale != val) { if (!pca9685_prescaler_can_change(pca, pwm->hwpwm)) { dev_err(chip->dev, @@ -369,7 +400,7 @@ static int __pca9685_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, pca9685_set_sleep_mode(pca, true); /* Change the chip-wide output frequency */ - regmap_write(pca->regmap, PCA9685_PRESCALE, prescale); + pca9685_write_reg(pca, PCA9685_PRESCALE, prescale); /* Wake the chip up */ pca9685_set_sleep_mode(pca, false); @@ -408,7 +439,7 @@ static void pca9685_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, unsigned int val = 0; /* Calculate (chip-wide) period from prescale value */ - regmap_read(pca->regmap, PCA9685_PRESCALE, &val); + pca9685_read_reg(pca, PCA9685_PRESCALE, &val); /* * PCA9685_OSC_CLOCK_MHZ is 25, i.e. an integer divider of 1000. * The following calculation is therefore only a multiplication @@ -505,7 +536,9 @@ static int pca9685_pwm_probe(struct i2c_client *client, mutex_init(&pca->lock); - regmap_read(pca->regmap, PCA9685_MODE2, ®); + ret = pca9685_read_reg(pca, PCA9685_MODE2, ®); + if (ret) + return ret; if (device_property_read_bool(&client->dev, "invert")) reg |= MODE2_INVRT; @@ -517,18 +550,20 @@ static int pca9685_pwm_probe(struct i2c_client *client, else reg |= MODE2_OUTDRV; - regmap_write(pca->regmap, PCA9685_MODE2, reg); + ret = pca9685_write_reg(pca, PCA9685_MODE2, reg); + if (ret) + return ret; /* Disable all LED ALLCALL and SUBx addresses to avoid bus collisions */ - regmap_read(pca->regmap, PCA9685_MODE1, ®); + pca9685_read_reg(pca, PCA9685_MODE1, ®); reg &= ~(MODE1_ALLCALL | MODE1_SUB1 | MODE1_SUB2 | MODE1_SUB3); - regmap_write(pca->regmap, PCA9685_MODE1, reg); + pca9685_write_reg(pca, PCA9685_MODE1, reg); /* Reset OFF/ON registers to POR default */ - regmap_write(pca->regmap, PCA9685_ALL_LED_OFF_L, LED_FULL); - regmap_write(pca->regmap, PCA9685_ALL_LED_OFF_H, LED_FULL); - regmap_write(pca->regmap, PCA9685_ALL_LED_ON_L, 0); - regmap_write(pca->regmap, PCA9685_ALL_LED_ON_H, 0); + pca9685_write_reg(pca, PCA9685_ALL_LED_OFF_L, LED_FULL); + pca9685_write_reg(pca, PCA9685_ALL_LED_OFF_H, LED_FULL); + pca9685_write_reg(pca, PCA9685_ALL_LED_ON_L, 0); + pca9685_write_reg(pca, PCA9685_ALL_LED_ON_H, 0); pca->chip.ops = &pca9685_pwm_ops; /* Add an extra channel for ALL_LED */ -- cgit v1.2.3 From 2ba4597d932b45b25bc2e6604c1dbbb08e444cb7 Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Tue, 1 Jun 2021 11:14:01 +0530 Subject: dt-bindings: pwm: pwm-tiehrpwm: Convert to json schema Convert the tiehrpwm binding to DT schema format using json-schema. Along with this conversion the following changes are included: - 'clock' and 'clock-names' properties are marked as required as driver fails to probe without these properties - Dropped ti,am33xx-ehrpwm as it is no longer applicable. - 'power-domains' property is introduced and marked as optional. Signed-off-by: Lokesh Vutla Reviewed-by: Rob Herring Signed-off-by: Thierry Reding --- .../devicetree/bindings/pwm/pwm-tiehrpwm.txt | 50 ----------------- .../devicetree/bindings/pwm/pwm-tiehrpwm.yaml | 64 ++++++++++++++++++++++ 2 files changed, 64 insertions(+), 50 deletions(-) delete mode 100644 Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt create mode 100644 Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.yaml diff --git a/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt b/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt deleted file mode 100644 index c7e28f6d28be..000000000000 --- a/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt +++ /dev/null @@ -1,50 +0,0 @@ -TI SOC EHRPWM based PWM controller - -Required properties: -- compatible: Must be "ti,-ehrpwm". - for am33xx - compatible = "ti,am3352-ehrpwm", "ti,am33xx-ehrpwm"; - for am4372 - compatible = "ti,am4372-ehrpwm", "ti-am3352-ehrpwm", "ti,am33xx-ehrpwm"; - for am654 - compatible = "ti,am654-ehrpwm", "ti-am3352-ehrpwm"; - for da850 - compatible = "ti,da850-ehrpwm", "ti-am3352-ehrpwm", "ti,am33xx-ehrpwm"; - for dra746 - compatible = "ti,dra746-ehrpwm", "ti-am3352-ehrpwm"; -- #pwm-cells: should be 3. See pwm.yaml in this directory for a description of - the cells format. The only third cell flag supported by this binding is - PWM_POLARITY_INVERTED. -- reg: physical base address and size of the registers map. - -Optional properties: -- clocks: Handle to the PWM's time-base and functional clock. -- clock-names: Must be set to "tbclk" and "fck". - -Example: - -ehrpwm0: pwm@48300200 { /* EHRPWM on am33xx */ - compatible = "ti,am3352-ehrpwm", "ti,am33xx-ehrpwm"; - #pwm-cells = <3>; - reg = <0x48300200 0x100>; - clocks = <&ehrpwm0_tbclk>, <&l4ls_gclk>; - clock-names = "tbclk", "fck"; -}; - -ehrpwm0: pwm@48300200 { /* EHRPWM on am4372 */ - compatible = "ti,am4372-ehrpwm", "ti,am3352-ehrpwm", "ti,am33xx-ehrpwm"; - #pwm-cells = <3>; - reg = <0x48300200 0x80>; - clocks = <&ehrpwm0_tbclk>, <&l4ls_gclk>; - clock-names = "tbclk", "fck"; - ti,hwmods = "ehrpwm0"; -}; - -ehrpwm0: pwm@1f00000 { /* EHRPWM on da850 */ - compatible = "ti,da850-ehrpwm", "ti,am3352-ehrpwm", "ti,am33xx-ehrpwm"; - #pwm-cells = <3>; - reg = <0x1f00000 0x2000>; -}; - -ehrpwm0: pwm@4843e200 { /* EHRPWM on dra746 */ - compatible = "ti,dra746-ehrpwm", "ti,am3352-ehrpwm"; - #pwm-cells = <3>; - reg = <0x4843e200 0x80>; - clocks = <&ehrpwm0_tbclk>, <&l4_root_clk_div>; - clock-names = "tbclk", "fck"; -}; diff --git a/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.yaml b/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.yaml new file mode 100644 index 000000000000..84a8d6d38cee --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.yaml @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/pwm-tiehrpwm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI SOC EHRPWM based PWM controller + +maintainers: + - Vignesh R + +allOf: + - $ref: pwm.yaml# + +properties: + compatible: + oneOf: + - const: ti,am3352-ehrpwm + - items: + - enum: + - ti,da850-ehrpwm + - ti,am4372-ehrpwm + - ti,dra746-ehrpwm + - ti,am654-ehrpwm + - const: ti,am3352-ehrpwm + + reg: + maxItems: 1 + + "#pwm-cells": + const: 3 + description: | + See pwm.yaml in this directory for a description of the cells format. + The only third cell flag supported by this binding is PWM_POLARITY_INVERTED. + + clock-names: + items: + - const: tbclk + - const: fck + + clocks: + maxItems: 2 + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - "#pwm-cells" + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + ehrpwm0: pwm@48300200 { /* EHRPWM on am33xx */ + compatible = "ti,am3352-ehrpwm"; + #pwm-cells = <3>; + reg = <0x48300200 0x100>; + clocks = <&ehrpwm0_tbclk>, <&l4ls_gclk>; + clock-names = "tbclk", "fck"; + }; -- cgit v1.2.3 From d233504af7db9f4ddbbc4b04513d5ca657e7ae1f Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Tue, 1 Jun 2021 11:14:02 +0530 Subject: dt-bindings: pwm: pwm-tiehrpwm: Add compatible string for AM64 SoC Add compatible string for AM64 SoC in device tree binding. EPWM IP in AM64 does not support High resolution, so named epwm instead of ehrpwm in compatible. However IP is till compatible with features supported by driver with ti,am3352-ehrpwm compatible. Signed-off-by: Lokesh Vutla Reviewed-by: Rob Herring Signed-off-by: Thierry Reding --- Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.yaml b/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.yaml index 84a8d6d38cee..ee312cb210e6 100644 --- a/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.yaml +++ b/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.yaml @@ -22,6 +22,7 @@ properties: - ti,am4372-ehrpwm - ti,dra746-ehrpwm - ti,am654-ehrpwm + - ti,am64-epwm - const: ti,am3352-ehrpwm reg: -- cgit v1.2.3 From bcda91bf86c1ff7647df85029d69f2aed80f210e Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 7 Apr 2021 10:01:54 +0200 Subject: pwm: Add a device-managed function to add PWM chips MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This potentially simplifies low-level PWM drivers. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/core.c | 19 +++++++++++++++++++ include/linux/pwm.h | 3 +++ 2 files changed, 22 insertions(+) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index a42999f877d2..83db178f16bb 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -335,6 +335,25 @@ out: } EXPORT_SYMBOL_GPL(pwmchip_remove); +static void devm_pwmchip_remove(void *data) +{ + struct pwm_chip *chip = data; + + pwmchip_remove(chip); +} + +int devm_pwmchip_add(struct device *dev, struct pwm_chip *chip) +{ + int ret; + + ret = pwmchip_add(chip); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, devm_pwmchip_remove, chip); +} +EXPORT_SYMBOL_GPL(devm_pwmchip_add); + /** * pwm_request() - request a PWM device * @pwm: global PWM device index diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 5a73251d28e3..892ece4d4cfa 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -405,6 +405,9 @@ void *pwm_get_chip_data(struct pwm_device *pwm); int pwmchip_add(struct pwm_chip *chip); int pwmchip_remove(struct pwm_chip *chip); + +int devm_pwmchip_add(struct device *dev, struct pwm_chip *chip); + struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, unsigned int index, const char *label); -- cgit v1.2.3 From d1e487b7a3c5f8144156b37d45fc7e724e752a05 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 7 Apr 2021 10:01:55 +0200 Subject: pwm: lpss: Simplify using devm_pwmchip_add() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-lpss-pci.c | 4 ---- drivers/pwm/pwm-lpss-platform.c | 4 +--- drivers/pwm/pwm-lpss.c | 8 +------- drivers/pwm/pwm-lpss.h | 1 - 4 files changed, 2 insertions(+), 15 deletions(-) diff --git a/drivers/pwm/pwm-lpss-pci.c b/drivers/pwm/pwm-lpss-pci.c index cf749ea0de9f..c893ec3d2fb4 100644 --- a/drivers/pwm/pwm-lpss-pci.c +++ b/drivers/pwm/pwm-lpss-pci.c @@ -69,12 +69,8 @@ static int pwm_lpss_probe_pci(struct pci_dev *pdev, static void pwm_lpss_remove_pci(struct pci_dev *pdev) { - struct pwm_lpss_chip *lpwm = pci_get_drvdata(pdev); - pm_runtime_forbid(&pdev->dev); pm_runtime_get_sync(&pdev->dev); - - pwm_lpss_remove(lpwm); } #ifdef CONFIG_PM diff --git a/drivers/pwm/pwm-lpss-platform.c b/drivers/pwm/pwm-lpss-platform.c index 986786be1e49..928570430cef 100644 --- a/drivers/pwm/pwm-lpss-platform.c +++ b/drivers/pwm/pwm-lpss-platform.c @@ -85,10 +85,8 @@ static int pwm_lpss_probe_platform(struct platform_device *pdev) static int pwm_lpss_remove_platform(struct platform_device *pdev) { - struct pwm_lpss_chip *lpwm = platform_get_drvdata(pdev); - pm_runtime_disable(&pdev->dev); - return pwm_lpss_remove(lpwm); + return 0; } static const struct acpi_device_id pwm_lpss_acpi_match[] = { diff --git a/drivers/pwm/pwm-lpss.c b/drivers/pwm/pwm-lpss.c index 58b4031524af..36d4e83e6b79 100644 --- a/drivers/pwm/pwm-lpss.c +++ b/drivers/pwm/pwm-lpss.c @@ -236,7 +236,7 @@ struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, struct resource *r, lpwm->chip.ops = &pwm_lpss_ops; lpwm->chip.npwm = info->npwm; - ret = pwmchip_add(&lpwm->chip); + ret = devm_pwmchip_add(dev, &lpwm->chip); if (ret) { dev_err(dev, "failed to add PWM chip: %d\n", ret); return ERR_PTR(ret); @@ -252,12 +252,6 @@ struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, struct resource *r, } EXPORT_SYMBOL_GPL(pwm_lpss_probe); -int pwm_lpss_remove(struct pwm_lpss_chip *lpwm) -{ - return pwmchip_remove(&lpwm->chip); -} -EXPORT_SYMBOL_GPL(pwm_lpss_remove); - MODULE_DESCRIPTION("PWM driver for Intel LPSS"); MODULE_AUTHOR("Mika Westerberg "); MODULE_LICENSE("GPL v2"); diff --git a/drivers/pwm/pwm-lpss.h b/drivers/pwm/pwm-lpss.h index 70db7e389d66..8b3476f25e06 100644 --- a/drivers/pwm/pwm-lpss.h +++ b/drivers/pwm/pwm-lpss.h @@ -35,6 +35,5 @@ struct pwm_lpss_boardinfo { struct pwm_lpss_chip *pwm_lpss_probe(struct device *dev, struct resource *r, const struct pwm_lpss_boardinfo *info); -int pwm_lpss_remove(struct pwm_lpss_chip *lpwm); #endif /* __PWM_LPSS_H */ -- cgit v1.2.3 From f41227eb8fb79b2ebdd286c0febc1e6b313095ff Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 10 May 2021 21:09:25 +0200 Subject: pwm: meson: Simplify using devm_pwmchip_add() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Acked-by: Martin Blumenstingl Signed-off-by: Thierry Reding --- drivers/pwm/pwm-meson.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c index 595afec53a2d..3cf3bcf5ddfc 100644 --- a/drivers/pwm/pwm-meson.c +++ b/drivers/pwm/pwm-meson.c @@ -558,31 +558,21 @@ static int meson_pwm_probe(struct platform_device *pdev) if (err < 0) return err; - err = pwmchip_add(&meson->chip); + err = devm_pwmchip_add(&pdev->dev, &meson->chip); if (err < 0) { dev_err(&pdev->dev, "failed to register PWM chip: %d\n", err); return err; } - platform_set_drvdata(pdev, meson); - return 0; } -static int meson_pwm_remove(struct platform_device *pdev) -{ - struct meson_pwm *meson = platform_get_drvdata(pdev); - - return pwmchip_remove(&meson->chip); -} - static struct platform_driver meson_pwm_driver = { .driver = { .name = "meson-pwm", .of_match_table = meson_pwm_matches, }, .probe = meson_pwm_probe, - .remove = meson_pwm_remove, }; module_platform_driver(meson_pwm_driver); -- cgit v1.2.3 From 9c6a02e6d5c6b8432acf3c11667fc347c60d2e76 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sun, 23 May 2021 15:34:43 +0200 Subject: pwm: clps711x: Simplify using devm_pwmchip_add() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With devm_pwmchip_add() we can drop pwmchip_remove() from the device remove callback. The latter can then go away, too and as this is the only user of platform_get_drvdata(), the respective call to platform_set_drvdata() can go, too. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-clps711x.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/pwm/pwm-clps711x.c b/drivers/pwm/pwm-clps711x.c index f3d17a590305..d7ad88685830 100644 --- a/drivers/pwm/pwm-clps711x.c +++ b/drivers/pwm/pwm-clps711x.c @@ -134,16 +134,7 @@ static int clps711x_pwm_probe(struct platform_device *pdev) spin_lock_init(&priv->lock); - platform_set_drvdata(pdev, priv); - - return pwmchip_add(&priv->chip); -} - -static int clps711x_pwm_remove(struct platform_device *pdev) -{ - struct clps711x_chip *priv = platform_get_drvdata(pdev); - - return pwmchip_remove(&priv->chip); + return devm_pwmchip_add(&pdev->dev, &priv->chip); } static const struct of_device_id __maybe_unused clps711x_pwm_dt_ids[] = { @@ -158,7 +149,6 @@ static struct platform_driver clps711x_pwm_driver = { .of_match_table = of_match_ptr(clps711x_pwm_dt_ids), }, .probe = clps711x_pwm_probe, - .remove = clps711x_pwm_remove, }; module_platform_driver(clps711x_pwm_driver); -- cgit v1.2.3 From 1bc6ea31cb41d50302a3c9b401964cf0a88d41f9 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 25 May 2021 08:35:27 +0200 Subject: pwm: imx1: Don't disable clocks at device remove time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback disables clocks that were not enabled in .probe(). So just probing and then unbinding the driver results in a clk enable imbalance. So just drop the call to disable the clocks. (Which BTW was also in the wrong order because the call makes the PWM unfunctional and so should have come only after pwmchip_remove()). Fixes: 9f4c8f9607c3 ("pwm: imx: Add ipg clock operation") Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx1.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/pwm/pwm-imx1.c b/drivers/pwm/pwm-imx1.c index c957b365448e..e73858a8e464 100644 --- a/drivers/pwm/pwm-imx1.c +++ b/drivers/pwm/pwm-imx1.c @@ -168,8 +168,6 @@ static int pwm_imx1_remove(struct platform_device *pdev) { struct pwm_imx1_chip *imx = platform_get_drvdata(pdev); - pwm_imx1_clk_disable_unprepare(&imx->chip); - return pwmchip_remove(&imx->chip); } -- cgit v1.2.3 From f7edeb4023efcd6494176095560ddd34f3bab006 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 25 May 2021 08:35:28 +0200 Subject: pwm: imx1: Simplify using devm_pwmchip_add() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With devm_pwmchip_add() we can drop pwmchip_remove() from the device remove callback. The latter can then go away, too and as this is the only user of platform_get_drvdata(), the respective call to platform_set_drvdata() can go, too. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx1.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/pwm/pwm-imx1.c b/drivers/pwm/pwm-imx1.c index e73858a8e464..bcd849496f8d 100644 --- a/drivers/pwm/pwm-imx1.c +++ b/drivers/pwm/pwm-imx1.c @@ -141,8 +141,6 @@ static int pwm_imx1_probe(struct platform_device *pdev) if (!imx) return -ENOMEM; - platform_set_drvdata(pdev, imx); - imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(imx->clk_ipg)) return dev_err_probe(&pdev->dev, PTR_ERR(imx->clk_ipg), @@ -161,14 +159,7 @@ static int pwm_imx1_probe(struct platform_device *pdev) if (IS_ERR(imx->mmio_base)) return PTR_ERR(imx->mmio_base); - return pwmchip_add(&imx->chip); -} - -static int pwm_imx1_remove(struct platform_device *pdev) -{ - struct pwm_imx1_chip *imx = platform_get_drvdata(pdev); - - return pwmchip_remove(&imx->chip); + return devm_pwmchip_add(&pdev->dev, &imx->chip); } static struct platform_driver pwm_imx1_driver = { @@ -177,7 +168,6 @@ static struct platform_driver pwm_imx1_driver = { .of_match_table = pwm_imx1_dt_ids, }, .probe = pwm_imx1_probe, - .remove = pwm_imx1_remove, }; module_platform_driver(pwm_imx1_driver); -- cgit v1.2.3 From 66a03c4fd9e95e192c574811a1f4ea8f62992358 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 25 May 2021 22:31:56 +0200 Subject: pwm: crc: Simplify using devm_pwmchip_add() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With devm_pwmchip_add() we can drop pwmchip_remove() from the device remove callback. The latter can then go away, too and as this is the only user of platform_get_drvdata(), the respective call to platform_set_drvdata() can go, too. Signed-off-by: Uwe Kleine-König Reviewed-by: Hans de Goede Signed-off-by: Thierry Reding --- drivers/pwm/pwm-crc.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/pwm/pwm-crc.c b/drivers/pwm/pwm-crc.c index 02522a9a3073..7b357d1cf642 100644 --- a/drivers/pwm/pwm-crc.c +++ b/drivers/pwm/pwm-crc.c @@ -173,21 +173,11 @@ static int crystalcove_pwm_probe(struct platform_device *pdev) /* get the PMIC regmap */ pwm->regmap = pmic->regmap; - platform_set_drvdata(pdev, pwm); - - return pwmchip_add(&pwm->chip); -} - -static int crystalcove_pwm_remove(struct platform_device *pdev) -{ - struct crystalcove_pwm *pwm = platform_get_drvdata(pdev); - - return pwmchip_remove(&pwm->chip); + return devm_pwmchip_add(&pdev->dev, &pwm->chip); } static struct platform_driver crystalcove_pwm_driver = { .probe = crystalcove_pwm_probe, - .remove = crystalcove_pwm_remove, .driver = { .name = "crystal_cove_pwm", }, -- cgit v1.2.3 From ad5e085c63f59391f5cfbde64fbff192872dfe8f Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 23 Apr 2021 18:59:02 +0200 Subject: pwm: Drop irrelevant error path from pwmchip_remove() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the PWM core uses device links (commit b2c200e3f2fd ("pwm: Add consumer device link")) each consumer driver that requested the PWMs is already gone. If they called pwm_put() (as they should) the PWMF_REQUESTED bit is not set. If they failed (which is a bug) the PWMF_REQUESTED bit might still be set, but the driver that cared is gone, so nothing bad happens if the PWM chip goes away even if the PWMF_REQUESTED is still present. So the check can be dropped. With this change pwmchip_remove() returns always 0, so lowlevel drivers don't need to check the return code any more. Once all drivers dropped this check this function can be changed to return void. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/core.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 83db178f16bb..a88e13c8a53c 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -306,22 +306,10 @@ EXPORT_SYMBOL_GPL(pwmchip_add); */ int pwmchip_remove(struct pwm_chip *chip) { - unsigned int i; - int ret = 0; - pwmchip_sysfs_unexport(chip); mutex_lock(&pwm_lock); - for (i = 0; i < chip->npwm; i++) { - struct pwm_device *pwm = &chip->pwms[i]; - - if (test_bit(PWMF_REQUESTED, &pwm->flags)) { - ret = -EBUSY; - goto out; - } - } - list_del_init(&chip->list); if (IS_ENABLED(CONFIG_OF)) @@ -329,9 +317,9 @@ int pwmchip_remove(struct pwm_chip *chip) free_pwms(chip); -out: mutex_unlock(&pwm_lock); - return ret; + + return 0; } EXPORT_SYMBOL_GPL(pwmchip_remove); -- cgit v1.2.3 From ec67fba92ebf6249b8155613063e403c695696c6 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 29 Apr 2021 15:32:18 +0200 Subject: pwm: tiecap: Drop .free() callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ecap_pwm_free is only called when a consumer releases the PWM (using pwm_put() or pwm_free()). The consumer is expected to disable the PWM before doing that. It's not clear if a warning about that is justified, but if it is this is independent of the actual driver and can better be done in the core. Also if there is a good reason it's wrong to disable the hardware and so the call to pm_runtime_put_sync() should be dropped. Moreover there is no matching pwm_runtime_get call and so the runtime usage counter might become negative. Fixes: 8e0cb05b3b75 ("pwm: pwm-tiecap: PWM driver support for ECAP APWM") Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tiecap.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c index f40975fcb195..027dd0142558 100644 --- a/drivers/pwm/pwm-tiecap.c +++ b/drivers/pwm/pwm-tiecap.c @@ -168,16 +168,7 @@ static void ecap_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) pm_runtime_put_sync(pc->chip.dev); } -static void ecap_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) -{ - if (pwm_is_enabled(pwm)) { - dev_warn(chip->dev, "Removing PWM device without disabling\n"); - pm_runtime_put_sync(chip->dev); - } -} - static const struct pwm_ops ecap_pwm_ops = { - .free = ecap_pwm_free, .config = ecap_pwm_config, .set_polarity = ecap_pwm_set_polarity, .enable = ecap_pwm_enable, -- cgit v1.2.3 From 0ca7acd847665f4554ef133c532b3bd855b7bb7f Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sat, 1 May 2021 18:01:39 +0200 Subject: pwm: tiecap: Implement .apply() callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To eventually get rid of all legacy drivers convert this driver to the modern world implementing .apply(). This just pushes down a slightly optimized variant of how legacy drivers are handled in the core. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tiecap.c | 55 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c index 027dd0142558..dec3f1fb150c 100644 --- a/drivers/pwm/pwm-tiecap.c +++ b/drivers/pwm/pwm-tiecap.c @@ -48,16 +48,13 @@ static inline struct ecap_pwm_chip *to_ecap_pwm_chip(struct pwm_chip *chip) * duty_ns = 10^9 * duty_cycles / PWM_CLK_RATE */ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) + int duty_ns, int period_ns, int enabled) { struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip); u32 period_cycles, duty_cycles; unsigned long long c; u16 value; - if (period_ns > NSEC_PER_SEC) - return -ERANGE; - c = pc->clk_rate; c = c * period_ns; do_div(c, NSEC_PER_SEC); @@ -82,7 +79,7 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, writew(value, pc->mmio_base + ECCTL2); - if (!pwm_is_enabled(pwm)) { + if (!enabled) { /* Update active registers if not running */ writel(duty_cycles, pc->mmio_base + CAP2); writel(period_cycles, pc->mmio_base + CAP1); @@ -96,7 +93,7 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, writel(period_cycles, pc->mmio_base + CAP3); } - if (!pwm_is_enabled(pwm)) { + if (!enabled) { value = readw(pc->mmio_base + ECCTL2); /* Disable APWM mode to put APWM output Low */ value &= ~ECCTL2_APWM_MODE; @@ -168,11 +165,49 @@ static void ecap_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) pm_runtime_put_sync(pc->chip.dev); } +static int ecap_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + int err; + int enabled = pwm->state.enabled; + + if (state->polarity != pwm->state.polarity) { + + if (enabled) { + ecap_pwm_disable(chip, pwm); + enabled = false; + } + + err = ecap_pwm_set_polarity(chip, pwm, state->polarity); + if (err) + return err; + } + + if (!state->enabled) { + if (enabled) + ecap_pwm_disable(chip, pwm); + return 0; + } + + if (state->period != pwm->state.period || + state->duty_cycle != pwm->state.duty_cycle) { + if (state->period > NSEC_PER_SEC) + return -ERANGE; + + err = ecap_pwm_config(chip, pwm, state->duty_cycle, + state->period, enabled); + if (err) + return err; + } + + if (!enabled) + return ecap_pwm_enable(chip, pwm); + + return 0; +} + static const struct pwm_ops ecap_pwm_ops = { - .config = ecap_pwm_config, - .set_polarity = ecap_pwm_set_polarity, - .enable = ecap_pwm_enable, - .disable = ecap_pwm_disable, + .apply = ecap_pwm_apply, .owner = THIS_MODULE, }; -- cgit v1.2.3 From 3f3e805177afc04dccf43cc58856a14a000a593f Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 4 May 2021 15:25:34 +0200 Subject: pwm: berlin: use consistent naming for variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A struct berlin_pwm_chip * is now always called "bpc" (instead of "pwm" which is usually used for struct pwm_device * or "chip" which is usually used for struct pwm_chip *). The struct pwm_device * variables were named "pwm_dev" or "pwm"; they are now always called "pwm". Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-berlin.c | 116 +++++++++++++++++++++++------------------------ 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/drivers/pwm/pwm-berlin.c b/drivers/pwm/pwm-berlin.c index a7fa6a4bf1c9..7eddc21ff77b 100644 --- a/drivers/pwm/pwm-berlin.c +++ b/drivers/pwm/pwm-berlin.c @@ -56,17 +56,17 @@ static inline struct berlin_pwm_chip *to_berlin_pwm_chip(struct pwm_chip *chip) return container_of(chip, struct berlin_pwm_chip, chip); } -static inline u32 berlin_pwm_readl(struct berlin_pwm_chip *chip, +static inline u32 berlin_pwm_readl(struct berlin_pwm_chip *bpc, unsigned int channel, unsigned long offset) { - return readl_relaxed(chip->base + channel * 0x10 + offset); + return readl_relaxed(bpc->base + channel * 0x10 + offset); } -static inline void berlin_pwm_writel(struct berlin_pwm_chip *chip, +static inline void berlin_pwm_writel(struct berlin_pwm_chip *bpc, unsigned int channel, u32 value, unsigned long offset) { - writel_relaxed(value, chip->base + channel * 0x10 + offset); + writel_relaxed(value, bpc->base + channel * 0x10 + offset); } static int berlin_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) @@ -87,15 +87,15 @@ static void berlin_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) kfree(channel); } -static int berlin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm_dev, +static int berlin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { - struct berlin_pwm_chip *pwm = to_berlin_pwm_chip(chip); + struct berlin_pwm_chip *bpc = to_berlin_pwm_chip(chip); bool prescale_4096 = false; u32 value, duty, period; u64 cycles; - cycles = clk_get_rate(pwm->clk); + cycles = clk_get_rate(bpc->clk); cycles *= period_ns; do_div(cycles, NSEC_PER_SEC); @@ -112,59 +112,59 @@ static int berlin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm_dev, do_div(cycles, period_ns); duty = cycles; - value = berlin_pwm_readl(pwm, pwm_dev->hwpwm, BERLIN_PWM_CONTROL); + value = berlin_pwm_readl(bpc, pwm->hwpwm, BERLIN_PWM_CONTROL); if (prescale_4096) value |= BERLIN_PWM_PRESCALE_4096; else value &= ~BERLIN_PWM_PRESCALE_4096; - berlin_pwm_writel(pwm, pwm_dev->hwpwm, value, BERLIN_PWM_CONTROL); + berlin_pwm_writel(bpc, pwm->hwpwm, value, BERLIN_PWM_CONTROL); - berlin_pwm_writel(pwm, pwm_dev->hwpwm, duty, BERLIN_PWM_DUTY); - berlin_pwm_writel(pwm, pwm_dev->hwpwm, period, BERLIN_PWM_TCNT); + berlin_pwm_writel(bpc, pwm->hwpwm, duty, BERLIN_PWM_DUTY); + berlin_pwm_writel(bpc, pwm->hwpwm, period, BERLIN_PWM_TCNT); return 0; } static int berlin_pwm_set_polarity(struct pwm_chip *chip, - struct pwm_device *pwm_dev, + struct pwm_device *pwm, enum pwm_polarity polarity) { - struct berlin_pwm_chip *pwm = to_berlin_pwm_chip(chip); + struct berlin_pwm_chip *bpc = to_berlin_pwm_chip(chip); u32 value; - value = berlin_pwm_readl(pwm, pwm_dev->hwpwm, BERLIN_PWM_CONTROL); + value = berlin_pwm_readl(bpc, pwm->hwpwm, BERLIN_PWM_CONTROL); if (polarity == PWM_POLARITY_NORMAL) value &= ~BERLIN_PWM_INVERT_POLARITY; else value |= BERLIN_PWM_INVERT_POLARITY; - berlin_pwm_writel(pwm, pwm_dev->hwpwm, value, BERLIN_PWM_CONTROL); + berlin_pwm_writel(bpc, pwm->hwpwm, value, BERLIN_PWM_CONTROL); return 0; } -static int berlin_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm_dev) +static int berlin_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { - struct berlin_pwm_chip *pwm = to_berlin_pwm_chip(chip); + struct berlin_pwm_chip *bpc = to_berlin_pwm_chip(chip); u32 value; - value = berlin_pwm_readl(pwm, pwm_dev->hwpwm, BERLIN_PWM_EN); + value = berlin_pwm_readl(bpc, pwm->hwpwm, BERLIN_PWM_EN); value |= BERLIN_PWM_ENABLE; - berlin_pwm_writel(pwm, pwm_dev->hwpwm, value, BERLIN_PWM_EN); + berlin_pwm_writel(bpc, pwm->hwpwm, value, BERLIN_PWM_EN); return 0; } static void berlin_pwm_disable(struct pwm_chip *chip, - struct pwm_device *pwm_dev) + struct pwm_device *pwm) { - struct berlin_pwm_chip *pwm = to_berlin_pwm_chip(chip); + struct berlin_pwm_chip *bpc = to_berlin_pwm_chip(chip); u32 value; - value = berlin_pwm_readl(pwm, pwm_dev->hwpwm, BERLIN_PWM_EN); + value = berlin_pwm_readl(bpc, pwm->hwpwm, BERLIN_PWM_EN); value &= ~BERLIN_PWM_ENABLE; - berlin_pwm_writel(pwm, pwm_dev->hwpwm, value, BERLIN_PWM_EN); + berlin_pwm_writel(bpc, pwm->hwpwm, value, BERLIN_PWM_EN); } static const struct pwm_ops berlin_pwm_ops = { @@ -185,48 +185,48 @@ MODULE_DEVICE_TABLE(of, berlin_pwm_match); static int berlin_pwm_probe(struct platform_device *pdev) { - struct berlin_pwm_chip *pwm; + struct berlin_pwm_chip *bpc; int ret; - pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); - if (!pwm) + bpc = devm_kzalloc(&pdev->dev, sizeof(*bpc), GFP_KERNEL); + if (!bpc) return -ENOMEM; - pwm->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(pwm->base)) - return PTR_ERR(pwm->base); + bpc->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(bpc->base)) + return PTR_ERR(bpc->base); - pwm->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(pwm->clk)) - return PTR_ERR(pwm->clk); + bpc->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(bpc->clk)) + return PTR_ERR(bpc->clk); - ret = clk_prepare_enable(pwm->clk); + ret = clk_prepare_enable(bpc->clk); if (ret) return ret; - pwm->chip.dev = &pdev->dev; - pwm->chip.ops = &berlin_pwm_ops; - pwm->chip.npwm = 4; + bpc->chip.dev = &pdev->dev; + bpc->chip.ops = &berlin_pwm_ops; + bpc->chip.npwm = 4; - ret = pwmchip_add(&pwm->chip); + ret = pwmchip_add(&bpc->chip); if (ret < 0) { dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret); - clk_disable_unprepare(pwm->clk); + clk_disable_unprepare(bpc->clk); return ret; } - platform_set_drvdata(pdev, pwm); + platform_set_drvdata(pdev, bpc); return 0; } static int berlin_pwm_remove(struct platform_device *pdev) { - struct berlin_pwm_chip *pwm = platform_get_drvdata(pdev); + struct berlin_pwm_chip *bpc = platform_get_drvdata(pdev); int ret; - ret = pwmchip_remove(&pwm->chip); - clk_disable_unprepare(pwm->clk); + ret = pwmchip_remove(&bpc->chip); + clk_disable_unprepare(bpc->clk); return ret; } @@ -234,48 +234,48 @@ static int berlin_pwm_remove(struct platform_device *pdev) #ifdef CONFIG_PM_SLEEP static int berlin_pwm_suspend(struct device *dev) { - struct berlin_pwm_chip *pwm = dev_get_drvdata(dev); + struct berlin_pwm_chip *bpc = dev_get_drvdata(dev); unsigned int i; - for (i = 0; i < pwm->chip.npwm; i++) { + for (i = 0; i < bpc->chip.npwm; i++) { struct berlin_pwm_channel *channel; - channel = pwm_get_chip_data(&pwm->chip.pwms[i]); + channel = pwm_get_chip_data(&bpc->chip.pwms[i]); if (!channel) continue; - channel->enable = berlin_pwm_readl(pwm, i, BERLIN_PWM_ENABLE); - channel->ctrl = berlin_pwm_readl(pwm, i, BERLIN_PWM_CONTROL); - channel->duty = berlin_pwm_readl(pwm, i, BERLIN_PWM_DUTY); - channel->tcnt = berlin_pwm_readl(pwm, i, BERLIN_PWM_TCNT); + channel->enable = berlin_pwm_readl(bpc, i, BERLIN_PWM_ENABLE); + channel->ctrl = berlin_pwm_readl(bpc, i, BERLIN_PWM_CONTROL); + channel->duty = berlin_pwm_readl(bpc, i, BERLIN_PWM_DUTY); + channel->tcnt = berlin_pwm_readl(bpc, i, BERLIN_PWM_TCNT); } - clk_disable_unprepare(pwm->clk); + clk_disable_unprepare(bpc->clk); return 0; } static int berlin_pwm_resume(struct device *dev) { - struct berlin_pwm_chip *pwm = dev_get_drvdata(dev); + struct berlin_pwm_chip *bpc = dev_get_drvdata(dev); unsigned int i; int ret; - ret = clk_prepare_enable(pwm->clk); + ret = clk_prepare_enable(bpc->clk); if (ret) return ret; - for (i = 0; i < pwm->chip.npwm; i++) { + for (i = 0; i < bpc->chip.npwm; i++) { struct berlin_pwm_channel *channel; - channel = pwm_get_chip_data(&pwm->chip.pwms[i]); + channel = pwm_get_chip_data(&bpc->chip.pwms[i]); if (!channel) continue; - berlin_pwm_writel(pwm, i, channel->ctrl, BERLIN_PWM_CONTROL); - berlin_pwm_writel(pwm, i, channel->duty, BERLIN_PWM_DUTY); - berlin_pwm_writel(pwm, i, channel->tcnt, BERLIN_PWM_TCNT); - berlin_pwm_writel(pwm, i, channel->enable, BERLIN_PWM_ENABLE); + berlin_pwm_writel(bpc, i, channel->ctrl, BERLIN_PWM_CONTROL); + berlin_pwm_writel(bpc, i, channel->duty, BERLIN_PWM_DUTY); + berlin_pwm_writel(bpc, i, channel->tcnt, BERLIN_PWM_TCNT); + berlin_pwm_writel(bpc, i, channel->enable, BERLIN_PWM_ENABLE); } return 0; -- cgit v1.2.3 From 30dffb42fcd4b127474f089e9c03fbc0dfc649a8 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 4 May 2021 15:25:36 +0200 Subject: pwm: berlin: Implement .apply() callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To eventually get rid of all legacy drivers convert this driver to the modern world implementing .apply(). This just pushes down a slightly optimized variant of how legacy drivers are handled in the core. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-berlin.c | 43 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/drivers/pwm/pwm-berlin.c b/drivers/pwm/pwm-berlin.c index 7eddc21ff77b..c17d7e8f0470 100644 --- a/drivers/pwm/pwm-berlin.c +++ b/drivers/pwm/pwm-berlin.c @@ -88,7 +88,7 @@ static void berlin_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) } static int berlin_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) + u64 duty_ns, u64 period_ns) { struct berlin_pwm_chip *bpc = to_berlin_pwm_chip(chip); bool prescale_4096 = false; @@ -167,13 +167,46 @@ static void berlin_pwm_disable(struct pwm_chip *chip, berlin_pwm_writel(bpc, pwm->hwpwm, value, BERLIN_PWM_EN); } +static int berlin_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + int err; + bool enabled = pwm->state.enabled; + + if (state->polarity != pwm->state.polarity) { + if (enabled) { + berlin_pwm_disable(chip, pwm); + enabled = false; + } + + err = berlin_pwm_set_polarity(chip, pwm, state->polarity); + if (err) + return err; + } + + if (!state->enabled) { + if (enabled) + berlin_pwm_disable(chip, pwm); + return 0; + } + + if (state->period != pwm->state.period || + state->duty_cycle != pwm->state.duty_cycle) { + err = berlin_pwm_config(chip, pwm, state->duty_cycle, state->period); + if (err) + return err; + } + + if (!enabled) + return berlin_pwm_enable(chip, pwm); + + return 0; +} + static const struct pwm_ops berlin_pwm_ops = { .request = berlin_pwm_request, .free = berlin_pwm_free, - .config = berlin_pwm_config, - .set_polarity = berlin_pwm_set_polarity, - .enable = berlin_pwm_enable, - .disable = berlin_pwm_disable, + .apply = berlin_pwm_apply, .owner = THIS_MODULE, }; -- cgit v1.2.3 From 0512f0503b051a98608a9b0d33b75482b960a313 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 4 May 2021 15:25:37 +0200 Subject: pwm: berlin: Don't check the return code of pwmchip_remove() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pwmchip_remove() always returns 0. Don't use the value to make it possible to eventually change the function to return void. This is a good thing as pwmchip_remove() is usually called from a remove function (mostly for platform devices) and their return value is ignored by the device core anyhow. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-berlin.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pwm/pwm-berlin.c b/drivers/pwm/pwm-berlin.c index c17d7e8f0470..5537b5f6dd5d 100644 --- a/drivers/pwm/pwm-berlin.c +++ b/drivers/pwm/pwm-berlin.c @@ -256,12 +256,12 @@ static int berlin_pwm_probe(struct platform_device *pdev) static int berlin_pwm_remove(struct platform_device *pdev) { struct berlin_pwm_chip *bpc = platform_get_drvdata(pdev); - int ret; - ret = pwmchip_remove(&bpc->chip); + pwmchip_remove(&bpc->chip); + clk_disable_unprepare(bpc->clk); - return ret; + return 0; } #ifdef CONFIG_PM_SLEEP -- cgit v1.2.3 From 81b7c173e3609ed77a9f9909406aefa122801a38 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 5 May 2021 18:19:08 +0200 Subject: pwm: pxa: Drop if with an always false condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() function is only called after .probe() returned successfully. In this case platform_set_drvdata() was called with a non-NULL argument and so platfrom_get_drvdata() returns the same non-NULL value. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-pxa.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c index cfb683827d32..31752640dcf7 100644 --- a/drivers/pwm/pwm-pxa.c +++ b/drivers/pwm/pwm-pxa.c @@ -210,8 +210,6 @@ static int pwm_remove(struct platform_device *pdev) struct pxa_pwm_chip *chip; chip = platform_get_drvdata(pdev); - if (chip == NULL) - return -ENODEV; return pwmchip_remove(&chip->chip); } -- cgit v1.2.3 From b63d60b2eaf3a20a60cbd3b1f252584604e828c7 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 5 May 2021 18:19:09 +0200 Subject: pwm: pxa: Always use the same variable name for driver data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In most functions the driver data variable is called pc. Do the same in the two remaining functions. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-pxa.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/pwm/pwm-pxa.c b/drivers/pwm/pwm-pxa.c index 31752640dcf7..e091a528e33c 100644 --- a/drivers/pwm/pwm-pxa.c +++ b/drivers/pwm/pwm-pxa.c @@ -165,7 +165,7 @@ pxa_pwm_of_xlate(struct pwm_chip *pc, const struct of_phandle_args *args) static int pwm_probe(struct platform_device *pdev) { const struct platform_device_id *id = platform_get_device_id(pdev); - struct pxa_pwm_chip *pwm; + struct pxa_pwm_chip *pc; int ret = 0; if (IS_ENABLED(CONFIG_OF) && id == NULL) @@ -174,44 +174,44 @@ static int pwm_probe(struct platform_device *pdev) if (id == NULL) return -EINVAL; - pwm = devm_kzalloc(&pdev->dev, sizeof(*pwm), GFP_KERNEL); - if (pwm == NULL) + pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); + if (pc == NULL) return -ENOMEM; - pwm->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(pwm->clk)) - return PTR_ERR(pwm->clk); + pc->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pc->clk)) + return PTR_ERR(pc->clk); - pwm->chip.dev = &pdev->dev; - pwm->chip.ops = &pxa_pwm_ops; - pwm->chip.npwm = (id->driver_data & HAS_SECONDARY_PWM) ? 2 : 1; + pc->chip.dev = &pdev->dev; + pc->chip.ops = &pxa_pwm_ops; + pc->chip.npwm = (id->driver_data & HAS_SECONDARY_PWM) ? 2 : 1; if (IS_ENABLED(CONFIG_OF)) { - pwm->chip.of_xlate = pxa_pwm_of_xlate; - pwm->chip.of_pwm_n_cells = 1; + pc->chip.of_xlate = pxa_pwm_of_xlate; + pc->chip.of_pwm_n_cells = 1; } - pwm->mmio_base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(pwm->mmio_base)) - return PTR_ERR(pwm->mmio_base); + pc->mmio_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pc->mmio_base)) + return PTR_ERR(pc->mmio_base); - ret = pwmchip_add(&pwm->chip); + ret = pwmchip_add(&pc->chip); if (ret < 0) { dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); return ret; } - platform_set_drvdata(pdev, pwm); + platform_set_drvdata(pdev, pc); return 0; } static int pwm_remove(struct platform_device *pdev) { - struct pxa_pwm_chip *chip; + struct pxa_pwm_chip *pc; - chip = platform_get_drvdata(pdev); + pc = platform_get_drvdata(pdev); - return pwmchip_remove(&chip->chip); + return pwmchip_remove(&pc->chip); } static struct platform_driver pwm_driver = { -- cgit v1.2.3 From fde25294dfd8e36e4e30b693c27a86232864002a Mon Sep 17 00:00:00 2001 From: Zou Wei Date: Wed, 12 May 2021 11:57:17 +0800 Subject: pwm: img: Fix PM reference leak in img_pwm_enable() pm_runtime_get_sync will increment pm usage counter even it failed. Forgetting to putting operation will result in reference leak here. Fix it by replacing it with pm_runtime_resume_and_get to keep usage counter balanced. Reported-by: Hulk Robot Signed-off-by: Zou Wei Signed-off-by: Thierry Reding --- drivers/pwm/pwm-img.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-img.c b/drivers/pwm/pwm-img.c index cc37054589cc..11b16ecc4f96 100644 --- a/drivers/pwm/pwm-img.c +++ b/drivers/pwm/pwm-img.c @@ -156,7 +156,7 @@ static int img_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) struct img_pwm_chip *pwm_chip = to_img_pwm_chip(chip); int ret; - ret = pm_runtime_get_sync(chip->dev); + ret = pm_runtime_resume_and_get(chip->dev); if (ret < 0) return ret; -- cgit v1.2.3 From 76982e478de4d682cb5fca26d6d3efe2d2545722 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sat, 22 May 2021 17:04:32 +0200 Subject: pwm: sprd: Don't check the return code of pwmchip_remove() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pwmchip_remove() returns always 0. Don't use the value to make it possible to eventually change the function to return void. This is a good thing as pwmchip_remove() is usually called from a remove function (mostly for platform devices) and their return value is ignored by the device core anyhow. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-sprd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-sprd.c b/drivers/pwm/pwm-sprd.c index 98c479dfae31..f2a85e8dd941 100644 --- a/drivers/pwm/pwm-sprd.c +++ b/drivers/pwm/pwm-sprd.c @@ -284,7 +284,9 @@ static int sprd_pwm_remove(struct platform_device *pdev) { struct sprd_pwm_chip *spc = platform_get_drvdata(pdev); - return pwmchip_remove(&spc->chip); + pwmchip_remove(&spc->chip); + + return 0; } static const struct of_device_id sprd_pwm_of_match[] = { -- cgit v1.2.3 From b0b8d558efb5b607fc35fbccb9edb5230c41c0ba Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Tue, 1 Jun 2021 15:58:03 +0530 Subject: dt-bindings: pwm: pwm-tiecap: Convert to json schema Convert the tiecap binding to DT schema format using json-schema. Along with this conversion the following changes are included: - 'clock' and 'clock-names' properties are marked required as driver fails to probe without these properties - Dropped ti,am33xx-ecap as it is no longer applicable. - 'power-domains' property is introduced and marked as optional. Signed-off-by: Lokesh Vutla Reviewed-by: Rob Herring Signed-off-by: Thierry Reding --- .../devicetree/bindings/pwm/pwm-tiecap.txt | 51 ------------------ .../devicetree/bindings/pwm/pwm-tiecap.yaml | 63 ++++++++++++++++++++++ 2 files changed, 63 insertions(+), 51 deletions(-) delete mode 100644 Documentation/devicetree/bindings/pwm/pwm-tiecap.txt create mode 100644 Documentation/devicetree/bindings/pwm/pwm-tiecap.yaml diff --git a/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt b/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt deleted file mode 100644 index c7c4347a769a..000000000000 --- a/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt +++ /dev/null @@ -1,51 +0,0 @@ -TI SOC ECAP based APWM controller - -Required properties: -- compatible: Must be "ti,-ecap". - for am33xx - compatible = "ti,am3352-ecap", "ti,am33xx-ecap"; - for am4372 - compatible = "ti,am4372-ecap", "ti,am3352-ecap", "ti,am33xx-ecap"; - for da850 - compatible = "ti,da850-ecap", "ti,am3352-ecap", "ti,am33xx-ecap"; - for dra746 - compatible = "ti,dra746-ecap", "ti,am3352-ecap"; - for 66ak2g - compatible = "ti,k2g-ecap", "ti,am3352-ecap"; - for am654 - compatible = "ti,am654-ecap", "ti,am3352-ecap"; -- #pwm-cells: should be 3. See pwm.yaml in this directory for a description of - the cells format. The PWM channel index ranges from 0 to 4. The only third - cell flag supported by this binding is PWM_POLARITY_INVERTED. -- reg: physical base address and size of the registers map. - -Optional properties: -- clocks: Handle to the ECAP's functional clock. -- clock-names: Must be set to "fck". - -Example: - -ecap0: ecap@48300100 { /* ECAP on am33xx */ - compatible = "ti,am3352-ecap", "ti,am33xx-ecap"; - #pwm-cells = <3>; - reg = <0x48300100 0x80>; - clocks = <&l4ls_gclk>; - clock-names = "fck"; -}; - -ecap0: ecap@48300100 { /* ECAP on am4372 */ - compatible = "ti,am4372-ecap", "ti,am3352-ecap", "ti,am33xx-ecap"; - #pwm-cells = <3>; - reg = <0x48300100 0x80>; - ti,hwmods = "ecap0"; - clocks = <&l4ls_gclk>; - clock-names = "fck"; -}; - -ecap0: ecap@1f06000 { /* ECAP on da850 */ - compatible = "ti,da850-ecap", "ti,am3352-ecap", "ti,am33xx-ecap"; - #pwm-cells = <3>; - reg = <0x1f06000 0x80>; -}; - -ecap0: ecap@4843e100 { - compatible = "ti,dra746-ecap", "ti,am3352-ecap"; - #pwm-cells = <3>; - reg = <0x4843e100 0x80>; - clocks = <&l4_root_clk_div>; - clock-names = "fck"; -}; diff --git a/Documentation/devicetree/bindings/pwm/pwm-tiecap.yaml b/Documentation/devicetree/bindings/pwm/pwm-tiecap.yaml new file mode 100644 index 000000000000..8717166dd05f --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-tiecap.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/pwm-tiecap.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI SOC ECAP based APWM controller + +maintainers: + - Vignesh R + +allOf: + - $ref: pwm.yaml# + +properties: + compatible: + oneOf: + - const: ti,am3352-ecap + - items: + - enum: + - ti,da850-ecap + - ti,am4372-ecap + - ti,dra746-ecap + - ti,k2g-ecap + - ti,am654-ecap + - const: ti,am3352-ecap + + reg: + maxItems: 1 + + "#pwm-cells": + const: 3 + description: | + See pwm.yaml in this directory for a description of the cells format. + The only third cell flag supported by this binding is PWM_POLARITY_INVERTED. + + clock-names: + const: fck + + clocks: + maxItems: 1 + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - "#pwm-cells" + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + ecap0: pwm@48300100 { /* ECAP on am33xx */ + compatible = "ti,am3352-ecap"; + #pwm-cells = <3>; + reg = <0x48300100 0x80>; + clocks = <&l4ls_gclk>; + clock-names = "fck"; + }; -- cgit v1.2.3 From 9939648a53c4c795bdac0d54df22423f81ccf5f0 Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Tue, 1 Jun 2021 15:58:04 +0530 Subject: dt-bindings: pwm: pwm-tiecap: Add compatible string for AM64 SoC Add compatible string for AM64 SoC in device tree binding. IP is compatible with ti,am3352-ecap, so adding the AM64 compatible under enum of one of the compatible list entry. Signed-off-by: Lokesh Vutla Reviewed-by: Rob Herring Signed-off-by: Thierry Reding --- Documentation/devicetree/bindings/pwm/pwm-tiecap.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/pwm/pwm-tiecap.yaml b/Documentation/devicetree/bindings/pwm/pwm-tiecap.yaml index 8717166dd05f..ed35b6cc48d5 100644 --- a/Documentation/devicetree/bindings/pwm/pwm-tiecap.yaml +++ b/Documentation/devicetree/bindings/pwm/pwm-tiecap.yaml @@ -23,6 +23,7 @@ properties: - ti,dra746-ecap - ti,k2g-ecap - ti,am654-ecap + - ti,am64-ecap - const: ti,am3352-ecap reg: -- cgit v1.2.3 From ef3d13b867637f9a791b524100362ba7e2b69810 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 7 Jun 2021 15:24:53 +0300 Subject: docs: firmware-guide: ACPI: Add a PWM example When PWM support for ACPI has been added into the kernel, it missed the documentation update. Hence update documentation here. Fixes: 4a6ef8e37c4d ("pwm: Add support referencing PWMs from ACPI") Signed-off-by: Andy Shevchenko Acked-by: Rafael J. Wysocki Signed-off-by: Thierry Reding --- Documentation/firmware-guide/acpi/enumeration.rst | 32 +++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Documentation/firmware-guide/acpi/enumeration.rst b/Documentation/firmware-guide/acpi/enumeration.rst index 9f0d5c854fa4..f588663ba906 100644 --- a/Documentation/firmware-guide/acpi/enumeration.rst +++ b/Documentation/firmware-guide/acpi/enumeration.rst @@ -258,6 +258,38 @@ input driver:: .id_table = mpu3050_ids, }; +Reference to PWM device +======================= + +Sometimes a device can be a consumer of PWM channel. Obviously OS would like +to know which one. To provide this mapping the special property has been +introduced, i.e.:: + + Device (DEV) + { + Name (_DSD, Package () + { + ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"), + Package () { + Package () { "compatible", Package () { "pwm-leds" } }, + Package () { "label", "alarm-led" }, + Package () { "pwms", + Package () { + "\\_SB.PCI0.PWM", // + 0, // + 600000000, // + 0, // + } + } + } + + }) + ... + +In the above example the PWM-based LED driver references to the PWM channel 0 +of \_SB.PCI0.PWM device with initial period setting equal to 600 ms (note that +value is given in nanoseconds). + GPIO support ============ -- cgit v1.2.3 From ca06616b1eed31126138aaf1a6b5bdd149b61da8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 7 Jun 2021 15:24:54 +0300 Subject: pwm: core: Convert to use fwnode for matching When we traverse the list of the registered PWM controllers, use fwnode to match. This will help for further cleanup. Signed-off-by: Andy Shevchenko Signed-off-by: Thierry Reding --- drivers/pwm/core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index a88e13c8a53c..26765d4f29bd 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -699,14 +699,14 @@ int pwm_adjust_config(struct pwm_device *pwm) } EXPORT_SYMBOL_GPL(pwm_adjust_config); -static struct pwm_chip *of_node_to_pwmchip(struct device_node *np) +static struct pwm_chip *fwnode_to_pwmchip(struct fwnode_handle *fwnode) { struct pwm_chip *chip; mutex_lock(&pwm_lock); list_for_each_entry(chip, &pwm_chips, list) - if (chip->dev && chip->dev->of_node == np) { + if (chip->dev && dev_fwnode(chip->dev) == fwnode) { mutex_unlock(&pwm_lock); return chip; } @@ -785,7 +785,7 @@ struct pwm_device *of_pwm_get(struct device *dev, struct device_node *np, return ERR_PTR(err); } - pc = of_node_to_pwmchip(args.np); + pc = fwnode_to_pwmchip(of_fwnode_handle(args.np)); if (IS_ERR(pc)) { if (PTR_ERR(pc) != -EPROBE_DEFER) pr_err("%s(): PWM chip not found\n", __func__); -- cgit v1.2.3 From e5c38ba9f2813beb8cb80ef3f5065bfe98a9a450 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 7 Jun 2021 15:24:55 +0300 Subject: pwm: core: Reuse fwnode_to_pwmchip() in ACPI case In ACPI case we may use matching by fwnode as provided via fwnode_to_pwmchip(). This makes device_to_pwmchip() not needed anymore. Signed-off-by: Andy Shevchenko Acked-by: Rafael J. Wysocki Signed-off-by: Thierry Reding --- drivers/pwm/core.c | 31 +------------------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 26765d4f29bd..b72fb77aea60 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -827,28 +827,6 @@ put: } EXPORT_SYMBOL_GPL(of_pwm_get); -#if IS_ENABLED(CONFIG_ACPI) -static struct pwm_chip *device_to_pwmchip(struct device *dev) -{ - struct pwm_chip *chip; - - mutex_lock(&pwm_lock); - - list_for_each_entry(chip, &pwm_chips, list) { - struct acpi_device *adev = ACPI_COMPANION(chip->dev); - - if ((chip->dev == dev) || (adev && &adev->dev == dev)) { - mutex_unlock(&pwm_lock); - return chip; - } - } - - mutex_unlock(&pwm_lock); - - return ERR_PTR(-EPROBE_DEFER); -} -#endif - /** * acpi_pwm_get() - request a PWM via parsing "pwms" property in ACPI * @fwnode: firmware node to get the "pwm" property from @@ -869,9 +847,7 @@ static struct pwm_chip *device_to_pwmchip(struct device *dev) static struct pwm_device *acpi_pwm_get(struct fwnode_handle *fwnode) { struct pwm_device *pwm = ERR_PTR(-ENODEV); -#if IS_ENABLED(CONFIG_ACPI) struct fwnode_reference_args args; - struct acpi_device *acpi; struct pwm_chip *chip; int ret; @@ -881,14 +857,10 @@ static struct pwm_device *acpi_pwm_get(struct fwnode_handle *fwnode) if (ret < 0) return ERR_PTR(ret); - acpi = to_acpi_device_node(args.fwnode); - if (!acpi) - return ERR_PTR(-EINVAL); - if (args.nargs < 2) return ERR_PTR(-EPROTO); - chip = device_to_pwmchip(&acpi->dev); + chip = fwnode_to_pwmchip(args.fwnode); if (IS_ERR(chip)) return ERR_CAST(chip); @@ -901,7 +873,6 @@ static struct pwm_device *acpi_pwm_get(struct fwnode_handle *fwnode) if (args.nargs > 2 && args.args[2] & PWM_POLARITY_INVERTED) pwm->args.polarity = PWM_POLARITY_INVERSED; -#endif return pwm; } -- cgit v1.2.3 From e625fb70a6d21e4d9ca6d91924d4711a66fd634f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 7 Jun 2021 15:24:56 +0300 Subject: pwm: core: Unify fwnode checks in the module Historically we have two different approaches on how to check type of fwnode. Unify them using the latest and greatest fwnode related APIs. Signed-off-by: Andy Shevchenko Signed-off-by: Thierry Reding --- drivers/pwm/core.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index b72fb77aea60..e823a4eba79e 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -829,7 +829,7 @@ EXPORT_SYMBOL_GPL(of_pwm_get); /** * acpi_pwm_get() - request a PWM via parsing "pwms" property in ACPI - * @fwnode: firmware node to get the "pwm" property from + * @fwnode: firmware node to get the "pwms" property from * * Returns the PWM device parsed from the fwnode and index specified in the * "pwms" property or a negative error-code on failure. @@ -844,7 +844,7 @@ EXPORT_SYMBOL_GPL(of_pwm_get); * Returns: A pointer to the requested PWM device or an ERR_PTR()-encoded * error code on failure. */ -static struct pwm_device *acpi_pwm_get(struct fwnode_handle *fwnode) +static struct pwm_device *acpi_pwm_get(const struct fwnode_handle *fwnode) { struct pwm_device *pwm = ERR_PTR(-ENODEV); struct fwnode_reference_args args; @@ -928,6 +928,7 @@ void pwm_remove_table(struct pwm_lookup *table, size_t num) */ struct pwm_device *pwm_get(struct device *dev, const char *con_id) { + const struct fwnode_handle *fwnode = dev ? dev_fwnode(dev) : NULL; const char *dev_id = dev ? dev_name(dev) : NULL; struct pwm_device *pwm; struct pwm_chip *chip; @@ -938,12 +939,12 @@ struct pwm_device *pwm_get(struct device *dev, const char *con_id) int err; /* look up via DT first */ - if (IS_ENABLED(CONFIG_OF) && dev && dev->of_node) - return of_pwm_get(dev, dev->of_node, con_id); + if (is_of_node(fwnode)) + return of_pwm_get(dev, to_of_node(fwnode), con_id); /* then lookup via ACPI */ - if (dev && is_acpi_node(dev->fwnode)) { - pwm = acpi_pwm_get(dev->fwnode); + if (is_acpi_node(fwnode)) { + pwm = acpi_pwm_get(fwnode); if (!IS_ERR(pwm) || PTR_ERR(pwm) != -ENOENT) return pwm; } -- cgit v1.2.3 From c333b936c1530e76eba4e81091874d1217046131 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 7 Jun 2021 15:24:57 +0300 Subject: pwm: core: Remove unused devm_pwm_put() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are no users and seems no will come of the devm_pwm_put(). Remove the function. While at it, slightly update documentation. Signed-off-by: Andy Shevchenko Reviewed-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- Documentation/driver-api/driver-model/devres.rst | 3 ++- Documentation/driver-api/pwm.rst | 3 ++- drivers/pwm/core.c | 25 ------------------------ include/linux/pwm.h | 5 ----- 4 files changed, 4 insertions(+), 32 deletions(-) diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst index e0814d214048..772216f7c6ff 100644 --- a/Documentation/driver-api/driver-model/devres.rst +++ b/Documentation/driver-api/driver-model/devres.rst @@ -401,7 +401,8 @@ POWER PWM devm_pwm_get() - devm_pwm_put() + devm_of_pwm_get() + devm_fwnode_pwm_get() REGULATOR devm_regulator_bulk_get() diff --git a/Documentation/driver-api/pwm.rst b/Documentation/driver-api/pwm.rst index 750734a7f874..ccb06e485756 100644 --- a/Documentation/driver-api/pwm.rst +++ b/Documentation/driver-api/pwm.rst @@ -40,7 +40,8 @@ after usage with pwm_free(). New users should use the pwm_get() function and pass to it the consumer device or a consumer name. pwm_put() is used to free the PWM device. Managed -variants of these functions, devm_pwm_get() and devm_pwm_put(), also exist. +variants of the getter, devm_pwm_get(), devm_of_pwm_get(), +devm_fwnode_pwm_get(), also exist. After being requested, a PWM has to be configured using:: diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index e823a4eba79e..4a173a00014e 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -1172,31 +1172,6 @@ struct pwm_device *devm_fwnode_pwm_get(struct device *dev, } EXPORT_SYMBOL_GPL(devm_fwnode_pwm_get); -static int devm_pwm_match(struct device *dev, void *res, void *data) -{ - struct pwm_device **p = res; - - if (WARN_ON(!p || !*p)) - return 0; - - return *p == data; -} - -/** - * devm_pwm_put() - resource managed pwm_put() - * @dev: device for PWM consumer - * @pwm: PWM device - * - * Release a PWM previously allocated using devm_pwm_get(). Calling this - * function is usually not needed because devm-allocated resources are - * automatically released on driver detach. - */ -void devm_pwm_put(struct device *dev, struct pwm_device *pwm) -{ - WARN_ON(devres_release(dev, devm_pwm_release, devm_pwm_match, pwm)); -} -EXPORT_SYMBOL_GPL(devm_pwm_put); - #ifdef CONFIG_DEBUG_FS static void pwm_dbg_show(struct pwm_chip *chip, struct seq_file *s) { diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 892ece4d4cfa..a0b7e43049d5 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -426,7 +426,6 @@ struct pwm_device *devm_of_pwm_get(struct device *dev, struct device_node *np, struct pwm_device *devm_fwnode_pwm_get(struct device *dev, struct fwnode_handle *fwnode, const char *con_id); -void devm_pwm_put(struct device *dev, struct pwm_device *pwm); #else static inline struct pwm_device *pwm_request(int pwm_id, const char *label) { @@ -533,10 +532,6 @@ devm_fwnode_pwm_get(struct device *dev, struct fwnode_handle *fwnode, { return ERR_PTR(-ENODEV); } - -static inline void devm_pwm_put(struct device *dev, struct pwm_device *pwm) -{ -} #endif static inline void pwm_apply_args(struct pwm_device *pwm) -- cgit v1.2.3 From 9ae241d06ef7aca8ebe8b1df91e0621ba7de5195 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 7 Jun 2021 15:24:58 +0300 Subject: pwm: core: Simplify some devm_*pwm*() functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use devm_add_action_or_reset() instead of devres_alloc() and devres_add(), which works the same. This will simplify the code. There is no functional changes. Signed-off-by: Andy Shevchenko Reviewed-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/core.c | 60 +++++++++++++++++++++++------------------------------- 1 file changed, 25 insertions(+), 35 deletions(-) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 4a173a00014e..a28c8639af5b 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -1065,9 +1065,9 @@ out: } EXPORT_SYMBOL_GPL(pwm_put); -static void devm_pwm_release(struct device *dev, void *res) +static void devm_pwm_release(void *pwm) { - pwm_put(*(struct pwm_device **)res); + pwm_put(pwm); } /** @@ -1083,19 +1083,16 @@ static void devm_pwm_release(struct device *dev, void *res) */ struct pwm_device *devm_pwm_get(struct device *dev, const char *con_id) { - struct pwm_device **ptr, *pwm; - - ptr = devres_alloc(devm_pwm_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); + struct pwm_device *pwm; + int ret; pwm = pwm_get(dev, con_id); - if (!IS_ERR(pwm)) { - *ptr = pwm; - devres_add(dev, ptr); - } else { - devres_free(ptr); - } + if (IS_ERR(pwm)) + return pwm; + + ret = devm_add_action_or_reset(dev, devm_pwm_release, pwm); + if (ret) + return ERR_PTR(ret); return pwm; } @@ -1116,19 +1113,16 @@ EXPORT_SYMBOL_GPL(devm_pwm_get); struct pwm_device *devm_of_pwm_get(struct device *dev, struct device_node *np, const char *con_id) { - struct pwm_device **ptr, *pwm; - - ptr = devres_alloc(devm_pwm_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); + struct pwm_device *pwm; + int ret; pwm = of_pwm_get(dev, np, con_id); - if (!IS_ERR(pwm)) { - *ptr = pwm; - devres_add(dev, ptr); - } else { - devres_free(ptr); - } + if (IS_ERR(pwm)) + return pwm; + + ret = devm_add_action_or_reset(dev, devm_pwm_release, pwm); + if (ret) + return ERR_PTR(ret); return pwm; } @@ -1150,23 +1144,19 @@ struct pwm_device *devm_fwnode_pwm_get(struct device *dev, struct fwnode_handle *fwnode, const char *con_id) { - struct pwm_device **ptr, *pwm = ERR_PTR(-ENODEV); - - ptr = devres_alloc(devm_pwm_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); + struct pwm_device *pwm = ERR_PTR(-ENODEV); + int ret; if (is_of_node(fwnode)) pwm = of_pwm_get(dev, to_of_node(fwnode), con_id); else if (is_acpi_node(fwnode)) pwm = acpi_pwm_get(fwnode); + if (IS_ERR(pwm)) + return pwm; - if (!IS_ERR(pwm)) { - *ptr = pwm; - devres_add(dev, ptr); - } else { - devres_free(ptr); - } + ret = devm_add_action_or_reset(dev, devm_pwm_release, pwm); + if (ret) + return ERR_PTR(ret); return pwm; } -- cgit v1.2.3 From c1b8ac969febc8f413c4d71f0eefe2e107610449 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 17 Jun 2021 11:51:40 +0200 Subject: pwm: tegra: Drop an if block with an always false condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tegra_pwm_remove() is only called after tegra_pwm_probe() successfully completed. In this case platform_set_drvdata() was called with a non-NULL value and so platform_get_drvdata(pdev) cannot return NULL. Returning an error code from a platform_driver's remove function is ignored anyway, so it's a good thing this exit path is gone. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tegra.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c index c529a170bcdd..fa025795198b 100644 --- a/drivers/pwm/pwm-tegra.c +++ b/drivers/pwm/pwm-tegra.c @@ -303,9 +303,6 @@ static int tegra_pwm_remove(struct platform_device *pdev) unsigned int i; int err; - if (WARN_ON(!pc)) - return -ENODEV; - err = clk_prepare_enable(pc->clk); if (err < 0) return err; -- cgit v1.2.3 From 86f7fa71cd830d18d7ebcaf719dffd5ddfe1acdd Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 17 Jun 2021 11:51:41 +0200 Subject: pwm: tegra: Don't modify HW state in .remove callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A consumer is expected to disable a PWM before calling pwm_put(). And if they didn't there is hopefully a good reason (or the consumer needs fixing). Also if disabling an enabled PWM was the right thing to do, this should better be done in the framework instead of in each low level driver. So drop the hardware modification from the .remove() callback. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tegra.c | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c index fa025795198b..a051cf43e7d7 100644 --- a/drivers/pwm/pwm-tegra.c +++ b/drivers/pwm/pwm-tegra.c @@ -300,25 +300,12 @@ static int tegra_pwm_probe(struct platform_device *pdev) static int tegra_pwm_remove(struct platform_device *pdev) { struct tegra_pwm_chip *pc = platform_get_drvdata(pdev); - unsigned int i; int err; err = clk_prepare_enable(pc->clk); if (err < 0) return err; - for (i = 0; i < pc->chip.npwm; i++) { - struct pwm_device *pwm = &pc->chip.pwms[i]; - - if (!pwm_is_enabled(pwm)) - if (clk_prepare_enable(pc->clk) < 0) - continue; - - pwm_writel(pc, i, 0); - - clk_disable_unprepare(pc->clk); - } - reset_control_assert(pc->rst); clk_disable_unprepare(pc->clk); -- cgit v1.2.3 From 3b8b571fcc49e794703eb01a97e9ed8b85e88304 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 17 Jun 2021 11:51:42 +0200 Subject: pwm: tegra: Don't needlessly enable and disable the clock in .remove() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no reason to enable the PWM clock just to assert the reset control. (If the reset control depends on the clock this is a bug and probably it doesn't because in .probe() the reset is deasserted without the clock being enabled.) Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tegra.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c index a051cf43e7d7..e865743e5989 100644 --- a/drivers/pwm/pwm-tegra.c +++ b/drivers/pwm/pwm-tegra.c @@ -300,14 +300,8 @@ static int tegra_pwm_probe(struct platform_device *pdev) static int tegra_pwm_remove(struct platform_device *pdev) { struct tegra_pwm_chip *pc = platform_get_drvdata(pdev); - int err; - - err = clk_prepare_enable(pc->clk); - if (err < 0) - return err; reset_control_assert(pc->rst); - clk_disable_unprepare(pc->clk); return pwmchip_remove(&pc->chip); } -- cgit v1.2.3 From 2f1a3bd4510afd8cf5ab5a6169ad64981a62dccf Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 17 Jun 2021 11:51:43 +0200 Subject: pwm: tegra: Assert reset only after the PWM was unregistered MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver is supposed to stay functional until pwmchip_remove() returns. So the reset must be asserted only after that. pwmchip_remove() always returns 0, so the return code can be ignored which keeps the tegra_pwm_remove() a bit simpler. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tegra.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c index e865743e5989..11a10b575ace 100644 --- a/drivers/pwm/pwm-tegra.c +++ b/drivers/pwm/pwm-tegra.c @@ -301,9 +301,11 @@ static int tegra_pwm_remove(struct platform_device *pdev) { struct tegra_pwm_chip *pc = platform_get_drvdata(pdev); + pwmchip_remove(&pc->chip); + reset_control_assert(pc->rst); - return pwmchip_remove(&pc->chip); + return 0; } #ifdef CONFIG_PM_SLEEP -- cgit v1.2.3 From fb2cb3bff9e0ede1709620809374dd14c07b1e16 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 21 Jun 2021 15:04:57 +0200 Subject: pwm: vt8500: Drop if with an always false condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vt8500_pwm_remove() is only called after vt8500_pwm_probe() returned successfully. In this case driver data was set to a non-NULL value and so chip can never be NULL. While touching this code also put declaration and assignment in a single line. Signed-off-by: Uwe Kleine-König Acked-by: Arnd Bergmann Signed-off-by: Thierry Reding --- drivers/pwm/pwm-vt8500.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/pwm/pwm-vt8500.c b/drivers/pwm/pwm-vt8500.c index f9eb36be9088..7164df2fbacf 100644 --- a/drivers/pwm/pwm-vt8500.c +++ b/drivers/pwm/pwm-vt8500.c @@ -238,11 +238,8 @@ static int vt8500_pwm_probe(struct platform_device *pdev) static int vt8500_pwm_remove(struct platform_device *pdev) { - struct vt8500_chip *chip; + struct vt8500_chip *chip = platform_get_drvdata(pdev); - chip = platform_get_drvdata(pdev); - if (chip == NULL) - return -ENODEV; clk_unprepare(chip->clk); -- cgit v1.2.3 From 868f13bdea3304362dd882f216ba30a8bf4c10c8 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 21 Jun 2021 15:04:58 +0200 Subject: pwm: vt8500: Only unprepare the clock after the pwmchip was removed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Until pwmchip_remove() returns the PWM is supposed to work, so pwmchip_remove() must be called before the clock is stopped. The return value of pwmchip_remove doesn't need to be checked because it returns zero anyhow and I plan to make it return void soon. Signed-off-by: Uwe Kleine-König Acked-by: Arnd Bergmann Signed-off-by: Thierry Reding --- drivers/pwm/pwm-vt8500.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-vt8500.c b/drivers/pwm/pwm-vt8500.c index 7164df2fbacf..ea2aa151080a 100644 --- a/drivers/pwm/pwm-vt8500.c +++ b/drivers/pwm/pwm-vt8500.c @@ -240,10 +240,11 @@ static int vt8500_pwm_remove(struct platform_device *pdev) { struct vt8500_chip *chip = platform_get_drvdata(pdev); + pwmchip_remove(&chip->chip); clk_unprepare(chip->clk); - return pwmchip_remove(&chip->chip); + return 0; } static struct platform_driver vt8500_pwm_driver = { -- cgit v1.2.3 From 6d45374af539c84d17cfcf5a4e96bc4b2ca421e6 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 23 Jun 2021 16:02:39 +0200 Subject: pwm: ep93xx: Implement .apply callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To ease review this reuses the formerly implemented callbacks. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-ep93xx.c | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/drivers/pwm/pwm-ep93xx.c b/drivers/pwm/pwm-ep93xx.c index 4ca70794ad96..3ef4b41bfd66 100644 --- a/drivers/pwm/pwm-ep93xx.c +++ b/drivers/pwm/pwm-ep93xx.c @@ -156,13 +156,48 @@ static void ep93xx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) clk_disable(ep93xx_pwm->clk); } +static int ep93xx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + int ret; + bool enabled = state->enabled; + + if (state->polarity != pwm->state.polarity) { + if (enabled) { + ep93xx_pwm_disable(chip, pwm); + enabled = false; + } + + ret = ep93xx_pwm_polarity(chip, pwm, state->polarity); + if (ret) + return ret; + } + + if (!state->enabled) { + if (enabled) + ep93xx_pwm_disable(chip, pwm); + + return 0; + } + + if (state->period != pwm->state.period || + state->duty_cycle != pwm->state.duty_cycle) { + ret = ep93xx_pwm_config(chip, pwm, (int)state->duty_cycle, + (int)state->period); + if (ret) + return ret; + } + + if (!enabled) + return ep93xx_pwm_enable(chip, pwm); + + return 0; +} + static const struct pwm_ops ep93xx_pwm_ops = { .request = ep93xx_pwm_request, .free = ep93xx_pwm_free, - .config = ep93xx_pwm_config, - .set_polarity = ep93xx_pwm_polarity, - .enable = ep93xx_pwm_enable, - .disable = ep93xx_pwm_disable, + .apply = ep93xx_pwm_apply, .owner = THIS_MODULE, }; -- cgit v1.2.3 From 72cce47fe8f8dec55de8cd4574c574dec5195252 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 23 Jun 2021 16:02:40 +0200 Subject: pwm: ep93xx: Unfold legacy callbacks into ep93xx_pwm_apply() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This just puts the implementation of ep93xx_pwm_disable(), ep93xx_pwm_enable() and ep93xx_pwm_config() into their only caller. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-ep93xx.c | 177 +++++++++++++++++++---------------------------- 1 file changed, 71 insertions(+), 106 deletions(-) diff --git a/drivers/pwm/pwm-ep93xx.c b/drivers/pwm/pwm-ep93xx.c index 3ef4b41bfd66..3354b734f28f 100644 --- a/drivers/pwm/pwm-ep93xx.c +++ b/drivers/pwm/pwm-ep93xx.c @@ -58,138 +58,103 @@ static void ep93xx_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) ep93xx_pwm_release_gpio(pdev); } -static int ep93xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) -{ - struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip); - void __iomem *base = ep93xx_pwm->base; - unsigned long long c; - unsigned long period_cycles; - unsigned long duty_cycles; - unsigned long term; - int ret = 0; - - /* - * The clock needs to be enabled to access the PWM registers. - * Configuration can be changed at any time. - */ - if (!pwm_is_enabled(pwm)) { - ret = clk_enable(ep93xx_pwm->clk); - if (ret) - return ret; - } - - c = clk_get_rate(ep93xx_pwm->clk); - c *= period_ns; - do_div(c, 1000000000); - period_cycles = c; - - c = period_cycles; - c *= duty_ns; - do_div(c, period_ns); - duty_cycles = c; - - if (period_cycles < 0x10000 && duty_cycles < 0x10000) { - term = readw(base + EP93XX_PWMx_TERM_COUNT); - - /* Order is important if PWM is running */ - if (period_cycles > term) { - writew(period_cycles, base + EP93XX_PWMx_TERM_COUNT); - writew(duty_cycles, base + EP93XX_PWMx_DUTY_CYCLE); - } else { - writew(duty_cycles, base + EP93XX_PWMx_DUTY_CYCLE); - writew(period_cycles, base + EP93XX_PWMx_TERM_COUNT); - } - } else { - ret = -EINVAL; - } - - if (!pwm_is_enabled(pwm)) - clk_disable(ep93xx_pwm->clk); - - return ret; -} - -static int ep93xx_pwm_polarity(struct pwm_chip *chip, struct pwm_device *pwm, - enum pwm_polarity polarity) -{ - struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip); - int ret; - - /* - * The clock needs to be enabled to access the PWM registers. - * Polarity can only be changed when the PWM is disabled. - */ - ret = clk_enable(ep93xx_pwm->clk); - if (ret) - return ret; - - if (polarity == PWM_POLARITY_INVERSED) - writew(0x1, ep93xx_pwm->base + EP93XX_PWMx_INVERT); - else - writew(0x0, ep93xx_pwm->base + EP93XX_PWMx_INVERT); - - clk_disable(ep93xx_pwm->clk); - - return 0; -} - -static int ep93xx_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip); - int ret; - - ret = clk_enable(ep93xx_pwm->clk); - if (ret) - return ret; - - writew(0x1, ep93xx_pwm->base + EP93XX_PWMx_ENABLE); - - return 0; -} - -static void ep93xx_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip); - - writew(0x0, ep93xx_pwm->base + EP93XX_PWMx_ENABLE); - clk_disable(ep93xx_pwm->clk); -} - static int ep93xx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state) { int ret; + struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip); bool enabled = state->enabled; if (state->polarity != pwm->state.polarity) { if (enabled) { - ep93xx_pwm_disable(chip, pwm); + writew(0x0, ep93xx_pwm->base + EP93XX_PWMx_ENABLE); + clk_disable(ep93xx_pwm->clk); enabled = false; } - ret = ep93xx_pwm_polarity(chip, pwm, state->polarity); + /* + * The clock needs to be enabled to access the PWM registers. + * Polarity can only be changed when the PWM is disabled. + */ + ret = clk_enable(ep93xx_pwm->clk); if (ret) return ret; + + if (state->polarity == PWM_POLARITY_INVERSED) + writew(0x1, ep93xx_pwm->base + EP93XX_PWMx_INVERT); + else + writew(0x0, ep93xx_pwm->base + EP93XX_PWMx_INVERT); + + clk_disable(ep93xx_pwm->clk); } if (!state->enabled) { - if (enabled) - ep93xx_pwm_disable(chip, pwm); + if (enabled) { + writew(0x0, ep93xx_pwm->base + EP93XX_PWMx_ENABLE); + clk_disable(ep93xx_pwm->clk); + } return 0; } if (state->period != pwm->state.period || state->duty_cycle != pwm->state.duty_cycle) { - ret = ep93xx_pwm_config(chip, pwm, (int)state->duty_cycle, - (int)state->period); + struct ep93xx_pwm *ep93xx_pwm = to_ep93xx_pwm(chip); + void __iomem *base = ep93xx_pwm->base; + unsigned long long c; + unsigned long period_cycles; + unsigned long duty_cycles; + unsigned long term; + + /* + * The clock needs to be enabled to access the PWM registers. + * Configuration can be changed at any time. + */ + if (!pwm_is_enabled(pwm)) { + ret = clk_enable(ep93xx_pwm->clk); + if (ret) + return ret; + } + + c = clk_get_rate(ep93xx_pwm->clk); + c *= state->period; + do_div(c, 1000000000); + period_cycles = c; + + c = period_cycles; + c *= state->duty_cycle; + do_div(c, state->period); + duty_cycles = c; + + if (period_cycles < 0x10000 && duty_cycles < 0x10000) { + term = readw(base + EP93XX_PWMx_TERM_COUNT); + + /* Order is important if PWM is running */ + if (period_cycles > term) { + writew(period_cycles, base + EP93XX_PWMx_TERM_COUNT); + writew(duty_cycles, base + EP93XX_PWMx_DUTY_CYCLE); + } else { + writew(duty_cycles, base + EP93XX_PWMx_DUTY_CYCLE); + writew(period_cycles, base + EP93XX_PWMx_TERM_COUNT); + } + } else { + ret = -EINVAL; + } + + if (!pwm_is_enabled(pwm)) + clk_disable(ep93xx_pwm->clk); + if (ret) return ret; } - if (!enabled) - return ep93xx_pwm_enable(chip, pwm); + if (!enabled) { + ret = clk_enable(ep93xx_pwm->clk); + if (ret) + return ret; + + writew(0x1, ep93xx_pwm->base + EP93XX_PWMx_ENABLE); + } return 0; } -- cgit v1.2.3 From b235f8a39fc3ee09c804bf711c3039f92d867549 Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Mon, 14 Jun 2021 01:30:41 +0200 Subject: pwm: ep93xx: Prepare clock before using it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use clk_prepare_enable()/clk_disable_unprepare() in preparation for switch to Common Clock Framework. Signed-off-by: Alexander Sverdlin Reviewed-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-ep93xx.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/pwm/pwm-ep93xx.c b/drivers/pwm/pwm-ep93xx.c index 3354b734f28f..70fa2957f9d3 100644 --- a/drivers/pwm/pwm-ep93xx.c +++ b/drivers/pwm/pwm-ep93xx.c @@ -68,7 +68,7 @@ static int ep93xx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, if (state->polarity != pwm->state.polarity) { if (enabled) { writew(0x0, ep93xx_pwm->base + EP93XX_PWMx_ENABLE); - clk_disable(ep93xx_pwm->clk); + clk_disable_unprepare(ep93xx_pwm->clk); enabled = false; } @@ -76,7 +76,7 @@ static int ep93xx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, * The clock needs to be enabled to access the PWM registers. * Polarity can only be changed when the PWM is disabled. */ - ret = clk_enable(ep93xx_pwm->clk); + ret = clk_prepare_enable(ep93xx_pwm->clk); if (ret) return ret; @@ -85,13 +85,13 @@ static int ep93xx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, else writew(0x0, ep93xx_pwm->base + EP93XX_PWMx_INVERT); - clk_disable(ep93xx_pwm->clk); + clk_disable_unprepare(ep93xx_pwm->clk); } if (!state->enabled) { if (enabled) { writew(0x0, ep93xx_pwm->base + EP93XX_PWMx_ENABLE); - clk_disable(ep93xx_pwm->clk); + clk_disable_unprepare(ep93xx_pwm->clk); } return 0; @@ -111,7 +111,7 @@ static int ep93xx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, * Configuration can be changed at any time. */ if (!pwm_is_enabled(pwm)) { - ret = clk_enable(ep93xx_pwm->clk); + ret = clk_prepare_enable(ep93xx_pwm->clk); if (ret) return ret; } @@ -142,14 +142,14 @@ static int ep93xx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, } if (!pwm_is_enabled(pwm)) - clk_disable(ep93xx_pwm->clk); + clk_disable_unprepare(ep93xx_pwm->clk); if (ret) return ret; } if (!enabled) { - ret = clk_enable(ep93xx_pwm->clk); + ret = clk_prepare_enable(ep93xx_pwm->clk); if (ret) return ret; -- cgit v1.2.3 From 96e45e5202377da39d086ec19f8934ebcc5b0fd6 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 29 Jun 2021 18:22:53 +0100 Subject: pwm: ep93xx: Fix read of uninitialized variable ret There is a potential path in function ep93xx_pwm_apply where ret is never assigned a value and it is checked for an error code. Fix this by ensuring ret is zero'd in the success path to avoid this issue. Addresses-Coverity: ("Uninitialized scalar variable") Fixes: f6ef94edf0f6 ("pwm: ep93xx: Unfold legacy callbacks into ep93xx_pwm_apply()") Signed-off-by: Colin Ian King Signed-off-by: Thierry Reding --- drivers/pwm/pwm-ep93xx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pwm/pwm-ep93xx.c b/drivers/pwm/pwm-ep93xx.c index 70fa2957f9d3..8a3d781e6514 100644 --- a/drivers/pwm/pwm-ep93xx.c +++ b/drivers/pwm/pwm-ep93xx.c @@ -137,6 +137,7 @@ static int ep93xx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, writew(duty_cycles, base + EP93XX_PWMx_DUTY_CYCLE); writew(period_cycles, base + EP93XX_PWMx_TERM_COUNT); } + ret = 0; } else { ret = -EINVAL; } -- cgit v1.2.3 From bebedf2bb4a9e0cb4ffa72cbc960728051b338a4 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 6 Jul 2021 16:11:32 +0100 Subject: pwm: Remove redundant assignment to pointer pwm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pointer pwm is being initialized with a value that is never read and it is being updated later with a new value. The initialization is redundant and can be removed. Addresses-Coverity: ("Unused value") Signed-off-by: Colin Ian King Acked-by: Uwe Kleine-König Reviewed-by: Andy Shevchenko Signed-off-by: Thierry Reding --- drivers/pwm/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index a28c8639af5b..35e894f4a379 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -846,7 +846,7 @@ EXPORT_SYMBOL_GPL(of_pwm_get); */ static struct pwm_device *acpi_pwm_get(const struct fwnode_handle *fwnode) { - struct pwm_device *pwm = ERR_PTR(-ENODEV); + struct pwm_device *pwm; struct fwnode_reference_args args; struct pwm_chip *chip; int ret; -- cgit v1.2.3