From 36af66a7905699a5c8e384e8777ead7be68a3ab9 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 18 Jul 2017 16:43:25 -0500 Subject: [PATCH 01/32] pwm: Convert to using %pOF instead of full_name Now that we have a custom printf format specifier, convert users of full_name to use %pOF instead. This is preparation to remove storing of the full path string for each node. Signed-off-by: Rob Herring Cc: Thierry Reding Cc: Carlo Caione Cc: Kevin Hilman Cc: linux-pwm@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org Cc: linux-amlogic@lists.infradead.org Acked-by: Neil Armstrong Signed-off-by: Thierry Reding --- drivers/pwm/pwm-meson.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-meson.c b/drivers/pwm/pwm-meson.c index cb845edfe2b4..d589331d1884 100644 --- a/drivers/pwm/pwm-meson.c +++ b/drivers/pwm/pwm-meson.c @@ -441,7 +441,7 @@ static int meson_pwm_init_channels(struct meson_pwm *meson, for (i = 0; i < meson->chip.npwm; i++) { struct meson_pwm_channel *channel = &channels[i]; - snprintf(name, sizeof(name), "%s#mux%u", np->full_name, i); + snprintf(name, sizeof(name), "%pOF#mux%u", np, i); init.name = name; init.ops = &clk_mux_ops; From 0829326ab26ecc598985b6e456c045a15bd253e4 Mon Sep 17 00:00:00 2001 From: Sven Van Asbroeck Date: Fri, 21 Apr 2017 09:19:02 -0400 Subject: [PATCH 02/32] pwm: pca9685: clarify pca9685_set_sleep_mode() interface. The function static void pca9685_set_sleep_mode(struct pca9685 *pca, int sleep) takes the chip in and out of sleep mode, depending on the value of sleep, which is interpreted as a boolean. To clarify that 'int sleep' is a boolean and not a sleep delay, change the function interface to: static void pca9685_set_sleep_mode(struct pca9685 *pca, bool enable) Suggested-by: Andy Shevchenko Signed-off-by: Sven Van Asbroeck Reviewed-by: Andy Shevchenko Signed-off-by: Thierry Reding --- drivers/pwm/pwm-pca9685.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c index 5f55cfab9b1c..a7eaf962a95b 100644 --- a/drivers/pwm/pwm-pca9685.c +++ b/drivers/pwm/pwm-pca9685.c @@ -241,11 +241,11 @@ static inline int pca9685_pwm_gpio_probe(struct pca9685 *pca) } #endif -static void pca9685_set_sleep_mode(struct pca9685 *pca, int sleep) +static void pca9685_set_sleep_mode(struct pca9685 *pca, bool enable) { regmap_update_bits(pca->regmap, PCA9685_MODE1, - MODE1_SLEEP, sleep ? MODE1_SLEEP : 0); - if (!sleep) { + MODE1_SLEEP, enable ? MODE1_SLEEP : 0); + if (!enable) { /* Wait 500us for the oscillator to be back up */ udelay(500); } @@ -272,13 +272,13 @@ static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, * state is guaranteed active here. */ /* Put chip into sleep mode */ - pca9685_set_sleep_mode(pca, 1); + pca9685_set_sleep_mode(pca, true); /* Change the chip-wide output frequency */ regmap_write(pca->regmap, PCA9685_PRESCALE, prescale); /* Wake the chip up */ - pca9685_set_sleep_mode(pca, 0); + pca9685_set_sleep_mode(pca, false); pca->period_ns = period_ns; } else { @@ -534,7 +534,7 @@ static int pca9685_pwm_runtime_suspend(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct pca9685 *pca = i2c_get_clientdata(client); - pca9685_set_sleep_mode(pca, 1); + pca9685_set_sleep_mode(pca, true); return 0; } @@ -543,7 +543,7 @@ static int pca9685_pwm_runtime_resume(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct pca9685 *pca = i2c_get_clientdata(client); - pca9685_set_sleep_mode(pca, 0); + pca9685_set_sleep_mode(pca, false); return 0; } #endif From 0bd24f9b5b289aeb439bc4d29b54e3f3a6ea807c Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Fri, 5 May 2017 13:30:14 +0530 Subject: [PATCH 03/32] pwm: vt8500: Undo preparation of a clock source. Undo preparation of a clock source if vt8500_pwm_probe() is not successful. Signed-off-by: Arvind Yadav Signed-off-by: Thierry Reding --- drivers/pwm/pwm-vt8500.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pwm/pwm-vt8500.c b/drivers/pwm/pwm-vt8500.c index 8141a4984126..3a78dd09ac81 100644 --- a/drivers/pwm/pwm-vt8500.c +++ b/drivers/pwm/pwm-vt8500.c @@ -241,6 +241,7 @@ static int vt8500_pwm_probe(struct platform_device *pdev) ret = pwmchip_add(&chip->chip); if (ret < 0) { dev_err(&pdev->dev, "failed to add PWM chip\n"); + clk_unprepare(chip->clk); return ret; } From 1f8736c4e1ee1fd3d933208683a5315f9a9d6b5c Mon Sep 17 00:00:00 2001 From: Simon Horman Date: Fri, 18 Aug 2017 12:09:01 +0200 Subject: [PATCH 04/32] pwm: renesas-tpu: Remove support for SH7372 Remove support for the SH7372 (SH-Mobile AP4) from the renesas-tpu driver. Commit edf4100906044225 ("ARM: shmobile: sh7372 dtsi: Remove Legacy file") removed this SoC from the kernel in v4.1. Acked-by: Geert Uytterhoeven Signed-off-by: Simon Horman Signed-off-by: Thierry Reding --- Documentation/devicetree/bindings/pwm/renesas,tpu-pwm.txt | 1 - drivers/pwm/pwm-renesas-tpu.c | 1 - 2 files changed, 2 deletions(-) diff --git a/Documentation/devicetree/bindings/pwm/renesas,tpu-pwm.txt b/Documentation/devicetree/bindings/pwm/renesas,tpu-pwm.txt index b067e84a94b5..1aadc804dae4 100644 --- a/Documentation/devicetree/bindings/pwm/renesas,tpu-pwm.txt +++ b/Documentation/devicetree/bindings/pwm/renesas,tpu-pwm.txt @@ -6,7 +6,6 @@ Required Properties: - "renesas,tpu-r8a73a4": for R8A77A4 (R-Mobile APE6) compatible PWM controller. - "renesas,tpu-r8a7740": for R8A7740 (R-Mobile A1) compatible PWM controller. - "renesas,tpu-r8a7790": for R8A7790 (R-Car H2) compatible PWM controller. - - "renesas,tpu-sh7372": for SH7372 (SH-Mobile AP4) compatible PWM controller. - "renesas,tpu": for generic R-Car TPU PWM controller. - reg: Base address and length of each memory resource used by the PWM diff --git a/drivers/pwm/pwm-renesas-tpu.c b/drivers/pwm/pwm-renesas-tpu.c index 075c1a764ba2..29267d12fb4c 100644 --- a/drivers/pwm/pwm-renesas-tpu.c +++ b/drivers/pwm/pwm-renesas-tpu.c @@ -455,7 +455,6 @@ static const struct of_device_id tpu_of_table[] = { { .compatible = "renesas,tpu-r8a73a4", }, { .compatible = "renesas,tpu-r8a7740", }, { .compatible = "renesas,tpu-r8a7790", }, - { .compatible = "renesas,tpu-sh7372", }, { .compatible = "renesas,tpu", }, { }, }; From 27922ff59893e3445f69e397bcd92ae06fa89ca7 Mon Sep 17 00:00:00 2001 From: David Wu Date: Tue, 8 Aug 2017 23:38:29 +0800 Subject: [PATCH 05/32] pwm: rockchip: Add APB and function both clocks support New PWM module provides two individual clocks for APB clock and function clock. Signed-off-by: David Wu Acked-by: Rob Herring Signed-off-by: Thierry Reding --- .../devicetree/bindings/pwm/pwm-rockchip.txt | 8 ++- drivers/pwm/pwm-rockchip.c | 58 ++++++++++++++++--- 2 files changed, 56 insertions(+), 10 deletions(-) diff --git a/Documentation/devicetree/bindings/pwm/pwm-rockchip.txt b/Documentation/devicetree/bindings/pwm/pwm-rockchip.txt index b8be3d09ee26..2350ef918bef 100644 --- a/Documentation/devicetree/bindings/pwm/pwm-rockchip.txt +++ b/Documentation/devicetree/bindings/pwm/pwm-rockchip.txt @@ -6,7 +6,13 @@ Required properties: "rockchip,rk3288-pwm": found on RK3288 SoC "rockchip,vop-pwm": found integrated in VOP on RK3288 SoC - reg: physical base address and length of the controller's registers - - clocks: phandle and clock specifier of the PWM reference clock + - clocks: See ../clock/clock-bindings.txt + - For older hardware (rk2928, rk3066, rk3188, rk3228, rk3288, rk3399): + - There is one clock that's used both to derive the functional clock + for the device and as the bus clock. + - For newer hardware (rk3328 and future socs): specified by name + - "pwm": This is used to derive the functional clock. + - "pclk": This is the APB bus clock. - #pwm-cells: must be 2 (rk2928) or 3 (rk3288). See pwm.txt in this directory for a description of the cell format. diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c index 744d56197286..ac3cd5ec5310 100644 --- a/drivers/pwm/pwm-rockchip.c +++ b/drivers/pwm/pwm-rockchip.c @@ -33,6 +33,7 @@ struct rockchip_pwm_chip { struct pwm_chip chip; struct clk *clk; + struct clk *pclk; const struct rockchip_pwm_data *data; void __iomem *base; }; @@ -145,7 +146,7 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip, u64 tmp; int ret; - ret = clk_enable(pc->clk); + ret = clk_enable(pc->pclk); if (ret) return; @@ -161,7 +162,7 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip, pc->data->get_state(chip, pwm, state); - clk_disable(pc->clk); + clk_disable(pc->pclk); } static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, @@ -224,7 +225,7 @@ static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, pwm_get_state(pwm, &curstate); enabled = curstate.enabled; - ret = clk_enable(pc->clk); + ret = clk_enable(pc->pclk); if (ret) return ret; @@ -257,7 +258,7 @@ static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, rockchip_pwm_get_state(chip, pwm, state); out: - clk_disable(pc->clk); + clk_disable(pc->pclk); return ret; } @@ -328,7 +329,7 @@ static int rockchip_pwm_probe(struct platform_device *pdev) const struct of_device_id *id; struct rockchip_pwm_chip *pc; struct resource *r; - int ret; + int ret, count; id = of_match_device(rockchip_pwm_dt_ids, &pdev->dev); if (!id) @@ -343,13 +344,43 @@ static int rockchip_pwm_probe(struct platform_device *pdev) if (IS_ERR(pc->base)) return PTR_ERR(pc->base); - pc->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(pc->clk)) - return PTR_ERR(pc->clk); + pc->clk = devm_clk_get(&pdev->dev, "pwm"); + if (IS_ERR(pc->clk)) { + pc->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pc->clk)) { + ret = PTR_ERR(pc->clk); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "Can't get bus clk: %d\n", + ret); + return ret; + } + } + + count = of_count_phandle_with_args(pdev->dev.of_node, + "clocks", "#clock-cells"); + if (count == 2) + pc->pclk = devm_clk_get(&pdev->dev, "pclk"); + else + pc->pclk = pc->clk; + + if (IS_ERR(pc->pclk)) { + ret = PTR_ERR(pc->pclk); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "Can't get APB clk: %d\n", ret); + return ret; + } ret = clk_prepare_enable(pc->clk); - if (ret) + if (ret) { + dev_err(&pdev->dev, "Can't prepare enable bus clk: %d\n", ret); return ret; + } + + ret = clk_prepare(pc->pclk); + if (ret) { + dev_err(&pdev->dev, "Can't prepare APB clk: %d\n", ret); + goto err_clk; + } platform_set_drvdata(pdev, pc); @@ -368,12 +399,20 @@ static int rockchip_pwm_probe(struct platform_device *pdev) if (ret < 0) { clk_unprepare(pc->clk); dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); + goto err_pclk; } /* Keep the PWM clk enabled if the PWM appears to be up and running. */ if (!pwm_is_enabled(pc->chip.pwms)) clk_disable(pc->clk); + return 0; + +err_pclk: + clk_unprepare(pc->pclk); +err_clk: + clk_disable_unprepare(pc->clk); + return ret; } @@ -395,6 +434,7 @@ static int rockchip_pwm_remove(struct platform_device *pdev) if (pwm_is_enabled(pc->chip.pwms)) clk_disable(pc->clk); + clk_unprepare(pc->pclk); clk_unprepare(pc->clk); return pwmchip_remove(&pc->chip); From f90df9cda68d774c600860e32602b04c741bf95c Mon Sep 17 00:00:00 2001 From: David Wu Date: Tue, 8 Aug 2017 23:38:30 +0800 Subject: [PATCH 06/32] pwm: rockchip: Remove the judge from return value of pwm_config() It seems the rockchip_pwm_config() always returns the result 0, so remove the judge. Signed-off-by: David Wu Acked-by: Boris Brezillon Signed-off-by: Thierry Reding --- drivers/pwm/pwm-rockchip.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c index ac3cd5ec5310..33bbb5a97b72 100644 --- a/drivers/pwm/pwm-rockchip.c +++ b/drivers/pwm/pwm-rockchip.c @@ -165,7 +165,7 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip, clk_disable(pc->pclk); } -static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, +static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); @@ -188,8 +188,6 @@ static int rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, writel(period, pc->base + pc->data->regs.period); writel(duty, pc->base + pc->data->regs.duty); - - return 0; } static int rockchip_pwm_enable(struct pwm_chip *chip, @@ -236,13 +234,7 @@ static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, enabled = false; } - ret = rockchip_pwm_config(chip, pwm, state->duty_cycle, state->period); - if (ret) { - if (enabled != curstate.enabled) - rockchip_pwm_enable(chip, pwm, !enabled, - state->polarity); - goto out; - } + rockchip_pwm_config(chip, pwm, state->duty_cycle, state->period); if (state->enabled != enabled) { ret = rockchip_pwm_enable(chip, pwm, state->enabled, From ed054693d77f9c98da18f7c3ff19dfa41692520f Mon Sep 17 00:00:00 2001 From: David Wu Date: Tue, 8 Aug 2017 23:38:31 +0800 Subject: [PATCH 07/32] pwm: rockchip: Use pwm_apply() instead of pwm_enable() Drop the custom hook of pwm_enable() and implement pwm_apply_v1() and pwm_apply_v2() instead. Signed-off-by: David Wu Signed-off-by: Thierry Reding --- drivers/pwm/pwm-rockchip.c | 141 ++++++++++++++++++++----------------- 1 file changed, 78 insertions(+), 63 deletions(-) diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c index 33bbb5a97b72..4f7ebe132ee9 100644 --- a/drivers/pwm/pwm-rockchip.c +++ b/drivers/pwm/pwm-rockchip.c @@ -51,11 +51,10 @@ struct rockchip_pwm_data { bool supports_polarity; const struct pwm_ops *ops; - void (*set_enable)(struct pwm_chip *chip, - struct pwm_device *pwm, bool enable, - enum pwm_polarity polarity); void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state); + int (*pwm_apply)(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state); }; static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c) @@ -63,24 +62,6 @@ static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c) return container_of(c, struct rockchip_pwm_chip, chip); } -static void rockchip_pwm_set_enable_v1(struct pwm_chip *chip, - struct pwm_device *pwm, bool enable, - enum pwm_polarity polarity) -{ - struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); - u32 enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN; - u32 val; - - val = readl_relaxed(pc->base + pc->data->regs.ctrl); - - if (enable) - val |= enable_conf; - else - val &= ~enable_conf; - - writel_relaxed(val, pc->base + pc->data->regs.ctrl); -} - static void rockchip_pwm_get_state_v1(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state) @@ -94,30 +75,6 @@ static void rockchip_pwm_get_state_v1(struct pwm_chip *chip, state->enabled = true; } -static void rockchip_pwm_set_enable_v2(struct pwm_chip *chip, - struct pwm_device *pwm, bool enable, - enum pwm_polarity polarity) -{ - struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); - u32 enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | - PWM_CONTINUOUS; - u32 val; - - if (polarity == PWM_POLARITY_INVERSED) - enable_conf |= PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSITIVE; - else - enable_conf |= PWM_DUTY_POSITIVE | PWM_INACTIVE_NEGATIVE; - - val = readl_relaxed(pc->base + pc->data->regs.ctrl); - - if (enable) - val |= enable_conf; - else - val &= ~enable_conf; - - writel_relaxed(val, pc->base + pc->data->regs.ctrl); -} - static void rockchip_pwm_get_state_v2(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state) @@ -193,10 +150,12 @@ static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, static int rockchip_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm, bool enable, - enum pwm_polarity polarity) + enum pwm_polarity polarity, + u32 enable_conf) { struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); int ret; + u32 val; if (enable) { ret = clk_enable(pc->clk); @@ -204,7 +163,23 @@ static int rockchip_pwm_enable(struct pwm_chip *chip, return ret; } - pc->data->set_enable(chip, pwm, enable, polarity); + if (pc->data->supports_polarity) { + if (polarity == PWM_POLARITY_INVERSED) + enable_conf |= PWM_DUTY_NEGATIVE | + PWM_INACTIVE_POSITIVE; + else + enable_conf |= PWM_DUTY_POSITIVE | + PWM_INACTIVE_NEGATIVE; + } + + val = readl_relaxed(pc->base + pc->data->regs.ctrl); + + if (enable) + val |= enable_conf; + else + val &= ~enable_conf; + + writel_relaxed(val, pc->base + pc->data->regs.ctrl); if (!enable) clk_disable(pc->clk); @@ -212,37 +187,77 @@ static int rockchip_pwm_enable(struct pwm_chip *chip, return 0; } -static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, - struct pwm_state *state) +static int rockchip_pwm_apply_v1(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) { - struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); + u32 enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN; struct pwm_state curstate; bool enabled; - int ret; + int ret = 0; pwm_get_state(pwm, &curstate); enabled = curstate.enabled; - ret = clk_enable(pc->pclk); - if (ret) - return ret; - if (state->polarity != curstate.polarity && enabled) { - ret = rockchip_pwm_enable(chip, pwm, false, state->polarity); + ret = rockchip_pwm_enable(chip, pwm, false, state->polarity, + enable_conf); if (ret) - goto out; + return ret; enabled = false; } rockchip_pwm_config(chip, pwm, state->duty_cycle, state->period); - if (state->enabled != enabled) { + if (state->enabled != enabled) ret = rockchip_pwm_enable(chip, pwm, state->enabled, - state->polarity); + state->polarity, enable_conf); + + return ret; +} + +static int rockchip_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + u32 enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | + PWM_CONTINUOUS; + struct pwm_state curstate; + bool enabled; + int ret = 0; + + pwm_get_state(pwm, &curstate); + enabled = curstate.enabled; + + if (state->polarity != curstate.polarity && enabled) { + ret = rockchip_pwm_enable(chip, pwm, false, state->polarity, + enable_conf); if (ret) - goto out; + return ret; + enabled = false; } + rockchip_pwm_config(chip, pwm, state->duty_cycle, state->period); + + if (state->enabled != enabled) + ret = rockchip_pwm_enable(chip, pwm, state->enabled, + state->polarity, enable_conf); + + return ret; +} + +static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); + int ret; + + ret = clk_enable(pc->pclk); + if (ret) + return ret; + + ret = pc->data->pwm_apply(chip, pwm, state); + if (ret) + goto out; + /* * Update the state with the real hardware, which can differ a bit * because of period/duty_cycle approximation. @@ -276,8 +291,8 @@ static const struct rockchip_pwm_data pwm_data_v1 = { }, .prescaler = 2, .ops = &rockchip_pwm_ops_v1, - .set_enable = rockchip_pwm_set_enable_v1, .get_state = rockchip_pwm_get_state_v1, + .pwm_apply = rockchip_pwm_apply_v1, }; static const struct rockchip_pwm_data pwm_data_v2 = { @@ -290,8 +305,8 @@ static const struct rockchip_pwm_data pwm_data_v2 = { .prescaler = 1, .supports_polarity = true, .ops = &rockchip_pwm_ops_v2, - .set_enable = rockchip_pwm_set_enable_v2, .get_state = rockchip_pwm_get_state_v2, + .pwm_apply = rockchip_pwm_apply_v2, }; static const struct rockchip_pwm_data pwm_data_vop = { @@ -304,8 +319,8 @@ static const struct rockchip_pwm_data pwm_data_vop = { .prescaler = 1, .supports_polarity = true, .ops = &rockchip_pwm_ops_v2, - .set_enable = rockchip_pwm_set_enable_v2, .get_state = rockchip_pwm_get_state_v2, + .pwm_apply = rockchip_pwm_apply_v2, }; static const struct of_device_id rockchip_pwm_dt_ids[] = { From bc834d7b07b4e57c89607c929dcb5eabb17b47a7 Mon Sep 17 00:00:00 2001 From: David Wu Date: Tue, 8 Aug 2017 23:38:32 +0800 Subject: [PATCH 08/32] pwm: rockchip: Move the configuration of polarity It is usually possible to configure the polarity, cycle and duty all at once, so that the polarity and cycle and duty are applied atomically. Move it from rockchip_pwm_set_enable() into rockchip_pwm_config(), as well as prepare for the next atomic update commit. Signed-off-by: David Wu Signed-off-by: Thierry Reding --- drivers/pwm/pwm-rockchip.c | 48 +++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c index 4f7ebe132ee9..911329a15da0 100644 --- a/drivers/pwm/pwm-rockchip.c +++ b/drivers/pwm/pwm-rockchip.c @@ -27,6 +27,7 @@ #define PWM_DUTY_NEGATIVE (0 << 3) #define PWM_INACTIVE_NEGATIVE (0 << 4) #define PWM_INACTIVE_POSITIVE (1 << 4) +#define PWM_POLARITY_MASK (PWM_DUTY_POSITIVE | PWM_INACTIVE_POSITIVE) #define PWM_OUTPUT_LEFT (0 << 5) #define PWM_LP_DISABLE (0 << 8) @@ -123,11 +124,12 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip, } static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) + struct pwm_state *state) { struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); unsigned long period, duty; u64 clk_rate, div; + u32 ctrl; clk_rate = clk_get_rate(pc->clk); @@ -136,22 +138,31 @@ static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, * bits, every possible input period can be obtained using the * default prescaler value for all practical clock rate values. */ - div = clk_rate * period_ns; + div = clk_rate * state->period; period = DIV_ROUND_CLOSEST_ULL(div, pc->data->prescaler * NSEC_PER_SEC); - div = clk_rate * duty_ns; + div = clk_rate * state->duty_cycle; duty = DIV_ROUND_CLOSEST_ULL(div, pc->data->prescaler * NSEC_PER_SEC); writel(period, pc->base + pc->data->regs.period); writel(duty, pc->base + pc->data->regs.duty); + + ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl); + if (pc->data->supports_polarity) { + ctrl &= ~PWM_POLARITY_MASK; + if (state->polarity == PWM_POLARITY_INVERSED) + ctrl |= PWM_DUTY_NEGATIVE | PWM_INACTIVE_POSITIVE; + else + ctrl |= PWM_DUTY_POSITIVE | PWM_INACTIVE_NEGATIVE; + } + writel(ctrl, pc->base + pc->data->regs.ctrl); } static int rockchip_pwm_enable(struct pwm_chip *chip, - struct pwm_device *pwm, - bool enable, - enum pwm_polarity polarity, - u32 enable_conf) + struct pwm_device *pwm, + bool enable, + u32 enable_conf) { struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); int ret; @@ -163,15 +174,6 @@ static int rockchip_pwm_enable(struct pwm_chip *chip, return ret; } - if (pc->data->supports_polarity) { - if (polarity == PWM_POLARITY_INVERSED) - enable_conf |= PWM_DUTY_NEGATIVE | - PWM_INACTIVE_POSITIVE; - else - enable_conf |= PWM_DUTY_POSITIVE | - PWM_INACTIVE_NEGATIVE; - } - val = readl_relaxed(pc->base + pc->data->regs.ctrl); if (enable) @@ -199,18 +201,17 @@ static int rockchip_pwm_apply_v1(struct pwm_chip *chip, struct pwm_device *pwm, enabled = curstate.enabled; if (state->polarity != curstate.polarity && enabled) { - ret = rockchip_pwm_enable(chip, pwm, false, state->polarity, - enable_conf); + ret = rockchip_pwm_enable(chip, pwm, false, enable_conf); if (ret) return ret; enabled = false; } - rockchip_pwm_config(chip, pwm, state->duty_cycle, state->period); + rockchip_pwm_config(chip, pwm, state); if (state->enabled != enabled) ret = rockchip_pwm_enable(chip, pwm, state->enabled, - state->polarity, enable_conf); + enable_conf); return ret; } @@ -228,18 +229,17 @@ static int rockchip_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm, enabled = curstate.enabled; if (state->polarity != curstate.polarity && enabled) { - ret = rockchip_pwm_enable(chip, pwm, false, state->polarity, - enable_conf); + ret = rockchip_pwm_enable(chip, pwm, false, enable_conf); if (ret) return ret; enabled = false; } - rockchip_pwm_config(chip, pwm, state->duty_cycle, state->period); + rockchip_pwm_config(chip, pwm, state); if (state->enabled != enabled) ret = rockchip_pwm_enable(chip, pwm, state->enabled, - state->polarity, enable_conf); + enable_conf); return ret; } From 831b2790507b3aac3213e9f39c714d85b0220098 Mon Sep 17 00:00:00 2001 From: David Wu Date: Tue, 8 Aug 2017 23:41:28 +0800 Subject: [PATCH 09/32] pwm: rockchip: Use same PWM ops for each IP Just use the same PWM ops for each IP, and get rid of the ops in struct rockchip_pwm_data, but still define the three different instances of the struct to use common interface for each IP. Signed-off-by: David Wu Signed-off-by: Thierry Reding --- drivers/pwm/pwm-rockchip.c | 163 ++++++++++--------------------------- 1 file changed, 45 insertions(+), 118 deletions(-) diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c index 911329a15da0..a3fcb404036d 100644 --- a/drivers/pwm/pwm-rockchip.c +++ b/drivers/pwm/pwm-rockchip.c @@ -50,12 +50,7 @@ struct rockchip_pwm_data { struct rockchip_pwm_regs regs; unsigned int prescaler; bool supports_polarity; - const struct pwm_ops *ops; - - void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm, - struct pwm_state *state); - int (*pwm_apply)(struct pwm_chip *chip, struct pwm_device *pwm, - struct pwm_state *state); + u32 enable_conf; }; static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c) @@ -63,45 +58,15 @@ static inline struct rockchip_pwm_chip *to_rockchip_pwm_chip(struct pwm_chip *c) return container_of(c, struct rockchip_pwm_chip, chip); } -static void rockchip_pwm_get_state_v1(struct pwm_chip *chip, - struct pwm_device *pwm, - struct pwm_state *state) -{ - struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); - u32 enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN; - u32 val; - - val = readl_relaxed(pc->base + pc->data->regs.ctrl); - if ((val & enable_conf) == enable_conf) - state->enabled = true; -} - -static void rockchip_pwm_get_state_v2(struct pwm_chip *chip, - struct pwm_device *pwm, - struct pwm_state *state) -{ - struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); - u32 enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | - PWM_CONTINUOUS; - u32 val; - - val = readl_relaxed(pc->base + pc->data->regs.ctrl); - if ((val & enable_conf) != enable_conf) - return; - - state->enabled = true; - - if (!(val & PWM_DUTY_POSITIVE)) - state->polarity = PWM_POLARITY_INVERSED; -} - static void rockchip_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state) { struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); + u32 enable_conf = pc->data->enable_conf; unsigned long clk_rate; u64 tmp; + u32 val; int ret; ret = clk_enable(pc->pclk); @@ -116,9 +81,20 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip, tmp = readl_relaxed(pc->base + pc->data->regs.duty); tmp *= pc->data->prescaler * NSEC_PER_SEC; - state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate); + state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate); - pc->data->get_state(chip, pwm, state); + val = readl_relaxed(pc->base + pc->data->regs.ctrl); + if (pc->data->supports_polarity) + state->enabled = ((val & enable_conf) != enable_conf) ? + false : true; + else + state->enabled = ((val & enable_conf) == enable_conf) ? + true : false; + + if (pc->data->supports_polarity) { + if (!(val & PWM_DUTY_POSITIVE)) + state->polarity = PWM_POLARITY_INVERSED; + } clk_disable(pc->pclk); } @@ -161,10 +137,10 @@ static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, static int rockchip_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm, - bool enable, - u32 enable_conf) + bool enable) { struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); + u32 enable_conf = pc->data->enable_conf; int ret; u32 val; @@ -189,74 +165,34 @@ static int rockchip_pwm_enable(struct pwm_chip *chip, return 0; } -static int rockchip_pwm_apply_v1(struct pwm_chip *chip, struct pwm_device *pwm, - struct pwm_state *state) -{ - u32 enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN; - struct pwm_state curstate; - bool enabled; - int ret = 0; - - pwm_get_state(pwm, &curstate); - enabled = curstate.enabled; - - if (state->polarity != curstate.polarity && enabled) { - ret = rockchip_pwm_enable(chip, pwm, false, enable_conf); - if (ret) - return ret; - enabled = false; - } - - rockchip_pwm_config(chip, pwm, state); - - if (state->enabled != enabled) - ret = rockchip_pwm_enable(chip, pwm, state->enabled, - enable_conf); - - return ret; -} - -static int rockchip_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm, - struct pwm_state *state) -{ - u32 enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | - PWM_CONTINUOUS; - struct pwm_state curstate; - bool enabled; - int ret = 0; - - pwm_get_state(pwm, &curstate); - enabled = curstate.enabled; - - if (state->polarity != curstate.polarity && enabled) { - ret = rockchip_pwm_enable(chip, pwm, false, enable_conf); - if (ret) - return ret; - enabled = false; - } - - rockchip_pwm_config(chip, pwm, state); - - if (state->enabled != enabled) - ret = rockchip_pwm_enable(chip, pwm, state->enabled, - enable_conf); - - return ret; -} - static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state) { struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip); - int ret; + struct pwm_state curstate; + bool enabled; + int ret = 0; ret = clk_enable(pc->pclk); if (ret) return ret; - ret = pc->data->pwm_apply(chip, pwm, state); - if (ret) - goto out; + pwm_get_state(pwm, &curstate); + enabled = curstate.enabled; + + if (state->polarity != curstate.polarity && enabled) { + ret = rockchip_pwm_enable(chip, pwm, false); + if (ret) + goto out; + enabled = false; + } + + rockchip_pwm_config(chip, pwm, state); + if (state->enabled != enabled) { + ret = rockchip_pwm_enable(chip, pwm, state->enabled); + if (ret) + goto out; + } /* * Update the state with the real hardware, which can differ a bit @@ -270,13 +206,7 @@ out: return ret; } -static const struct pwm_ops rockchip_pwm_ops_v1 = { - .get_state = rockchip_pwm_get_state, - .apply = rockchip_pwm_apply, - .owner = THIS_MODULE, -}; - -static const struct pwm_ops rockchip_pwm_ops_v2 = { +static const struct pwm_ops rockchip_pwm_ops = { .get_state = rockchip_pwm_get_state, .apply = rockchip_pwm_apply, .owner = THIS_MODULE, @@ -290,9 +220,8 @@ static const struct rockchip_pwm_data pwm_data_v1 = { .ctrl = 0x0c, }, .prescaler = 2, - .ops = &rockchip_pwm_ops_v1, - .get_state = rockchip_pwm_get_state_v1, - .pwm_apply = rockchip_pwm_apply_v1, + .supports_polarity = false, + .enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN, }; static const struct rockchip_pwm_data pwm_data_v2 = { @@ -304,9 +233,8 @@ static const struct rockchip_pwm_data pwm_data_v2 = { }, .prescaler = 1, .supports_polarity = true, - .ops = &rockchip_pwm_ops_v2, - .get_state = rockchip_pwm_get_state_v2, - .pwm_apply = rockchip_pwm_apply_v2, + .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | + PWM_CONTINUOUS, }; static const struct rockchip_pwm_data pwm_data_vop = { @@ -318,9 +246,8 @@ static const struct rockchip_pwm_data pwm_data_vop = { }, .prescaler = 1, .supports_polarity = true, - .ops = &rockchip_pwm_ops_v2, - .get_state = rockchip_pwm_get_state_v2, - .pwm_apply = rockchip_pwm_apply_v2, + .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | + PWM_CONTINUOUS, }; static const struct of_device_id rockchip_pwm_dt_ids[] = { @@ -393,7 +320,7 @@ static int rockchip_pwm_probe(struct platform_device *pdev) pc->data = id->data; pc->chip.dev = &pdev->dev; - pc->chip.ops = pc->data->ops; + pc->chip.ops = &rockchip_pwm_ops; pc->chip.base = -1; pc->chip.npwm = 1; From 3f9a363133eae0d01e77200af4ce9b58a4a9fc24 Mon Sep 17 00:00:00 2001 From: David Wu Date: Tue, 8 Aug 2017 23:42:47 +0800 Subject: [PATCH 10/32] pwm: rockchip: Add rk3328 support The rk3328 SoC supports atomic update, we could lock the configuration of period and duty at first, after unlock is configured, the period and duty are effective at the same time. If the polarity, period and duty need to be configured together, the way for atomic update is "configure lock and old polarity" -> "configure period and duty" -> "configure unlock and new polarity". Signed-off-by: David Wu Acked-by: Rob Herring Signed-off-by: Thierry Reding --- drivers/pwm/pwm-rockchip.c | 43 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c index a3fcb404036d..4d99d468df09 100644 --- a/drivers/pwm/pwm-rockchip.c +++ b/drivers/pwm/pwm-rockchip.c @@ -29,6 +29,7 @@ #define PWM_INACTIVE_POSITIVE (1 << 4) #define PWM_POLARITY_MASK (PWM_DUTY_POSITIVE | PWM_INACTIVE_POSITIVE) #define PWM_OUTPUT_LEFT (0 << 5) +#define PWM_LOCK_EN (1 << 6) #define PWM_LP_DISABLE (0 << 8) struct rockchip_pwm_chip { @@ -50,6 +51,7 @@ struct rockchip_pwm_data { struct rockchip_pwm_regs regs; unsigned int prescaler; bool supports_polarity; + bool supports_lock; u32 enable_conf; }; @@ -121,10 +123,19 @@ static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, div = clk_rate * state->duty_cycle; duty = DIV_ROUND_CLOSEST_ULL(div, pc->data->prescaler * NSEC_PER_SEC); + /* + * Lock the period and duty of previous configuration, then + * change the duty and period, that would not be effective. + */ + ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl); + if (pc->data->supports_lock) { + ctrl |= PWM_LOCK_EN; + writel_relaxed(ctrl, pc->base + pc->data->regs.ctrl); + } + writel(period, pc->base + pc->data->regs.period); writel(duty, pc->base + pc->data->regs.duty); - ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl); if (pc->data->supports_polarity) { ctrl &= ~PWM_POLARITY_MASK; if (state->polarity == PWM_POLARITY_INVERSED) @@ -132,6 +143,15 @@ static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, else ctrl |= PWM_DUTY_POSITIVE | PWM_INACTIVE_NEGATIVE; } + + /* + * Unlock and set polarity at the same time, + * the configuration of duty, period and polarity + * would be effective together at next period. + */ + if (pc->data->supports_lock) + ctrl &= ~PWM_LOCK_EN; + writel(ctrl, pc->base + pc->data->regs.ctrl); } @@ -180,7 +200,8 @@ static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, pwm_get_state(pwm, &curstate); enabled = curstate.enabled; - if (state->polarity != curstate.polarity && enabled) { + if (state->polarity != curstate.polarity && enabled && + !pc->data->supports_lock) { ret = rockchip_pwm_enable(chip, pwm, false); if (ret) goto out; @@ -221,6 +242,7 @@ static const struct rockchip_pwm_data pwm_data_v1 = { }, .prescaler = 2, .supports_polarity = false, + .supports_lock = false, .enable_conf = PWM_CTRL_OUTPUT_EN | PWM_CTRL_TIMER_EN, }; @@ -233,6 +255,7 @@ static const struct rockchip_pwm_data pwm_data_v2 = { }, .prescaler = 1, .supports_polarity = true, + .supports_lock = false, .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | PWM_CONTINUOUS, }; @@ -246,6 +269,21 @@ static const struct rockchip_pwm_data pwm_data_vop = { }, .prescaler = 1, .supports_polarity = true, + .supports_lock = false, + .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | + PWM_CONTINUOUS, +}; + +static const struct rockchip_pwm_data pwm_data_v3 = { + .regs = { + .duty = 0x08, + .period = 0x04, + .cntr = 0x00, + .ctrl = 0x0c, + }, + .prescaler = 1, + .supports_polarity = true, + .supports_lock = true, .enable_conf = PWM_OUTPUT_LEFT | PWM_LP_DISABLE | PWM_ENABLE | PWM_CONTINUOUS, }; @@ -254,6 +292,7 @@ static const struct of_device_id rockchip_pwm_dt_ids[] = { { .compatible = "rockchip,rk2928-pwm", .data = &pwm_data_v1}, { .compatible = "rockchip,rk3288-pwm", .data = &pwm_data_v2}, { .compatible = "rockchip,vop-pwm", .data = &pwm_data_vop}, + { .compatible = "rockchip,rk3328-pwm", .data = &pwm_data_v3}, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, rockchip_pwm_dt_ids); From 226d1005dbad77f1587ea4cfd151b15cd0a17da1 Mon Sep 17 00:00:00 2001 From: Andy Yan Date: Mon, 14 Aug 2017 16:37:48 +0800 Subject: [PATCH 11/32] dt-bindings: pwm: Add description for rv1108 PWM Add device tree bindings document for PWM on Rockchip rv1108 SoC. Signed-off-by: Andy Yan Acked-by: Rob Herring Signed-off-by: Thierry Reding --- Documentation/devicetree/bindings/pwm/pwm-rockchip.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/pwm/pwm-rockchip.txt b/Documentation/devicetree/bindings/pwm/pwm-rockchip.txt index 2350ef918bef..2c5e52a5bede 100644 --- a/Documentation/devicetree/bindings/pwm/pwm-rockchip.txt +++ b/Documentation/devicetree/bindings/pwm/pwm-rockchip.txt @@ -3,7 +3,8 @@ Rockchip PWM controller Required properties: - compatible: should be "rockchip,-pwm" "rockchip,rk2928-pwm": found on RK29XX,RK3066 and RK3188 SoCs - "rockchip,rk3288-pwm": found on RK3288 SoC + "rockchip,rk3288-pwm": found on RK3288 SOC + "rockchip,rv1108-pwm", "rockchip,rk3288-pwm": found on RV1108 SoC "rockchip,vop-pwm": found integrated in VOP on RK3288 SoC - reg: physical base address and length of the controller's registers - clocks: See ../clock/clock-bindings.txt From 46421d9d8e802e570dfa4d793a4938d2642ec7a7 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Sat, 12 Aug 2017 12:19:43 +0200 Subject: [PATCH 12/32] dt-bindings: pwm: bcm2835: Increase pwm-cells We need to increase the pwm-cells for the optional flags parameter, before we can implement support for polarity setting via DT. Signed-off-by: Stefan Wahren Acked-by: Rob Herring Signed-off-by: Thierry Reding --- Documentation/devicetree/bindings/pwm/pwm-bcm2835.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/pwm/pwm-bcm2835.txt b/Documentation/devicetree/bindings/pwm/pwm-bcm2835.txt index cf573e85b11d..8cf87d1bfca5 100644 --- a/Documentation/devicetree/bindings/pwm/pwm-bcm2835.txt +++ b/Documentation/devicetree/bindings/pwm/pwm-bcm2835.txt @@ -6,7 +6,7 @@ Required properties: - clocks: This clock defines the base clock frequency of the PWM hardware system, the period and the duty_cycle of the PWM signal is a multiple of the base period. -- #pwm-cells: Should be 2. See pwm.txt in this directory for a description of +- #pwm-cells: Should be 3. See pwm.txt in this directory for a description of the cells format. Examples: @@ -15,7 +15,7 @@ pwm@2020c000 { compatible = "brcm,bcm2835-pwm"; reg = <0x2020c000 0x28>; clocks = <&clk_pwm>; - #pwm-cells = <2>; + #pwm-cells = <3>; }; clocks { From 8a88b2a2017d1e7e80db53080baff591fd454722 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Sat, 12 Aug 2017 12:19:44 +0200 Subject: [PATCH 13/32] pwm: bcm2835: Support for polarity setting via DT This adds support for the third (optional) pwm cell to specify the polarity, which is needed by display backlights for example. Signed-off-by: Stefan Wahren Reviewed-by: Eric Anholt Signed-off-by: Thierry Reding --- drivers/pwm/pwm-bcm2835.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pwm/pwm-bcm2835.c b/drivers/pwm/pwm-bcm2835.c index c5dbf16d810b..db001cba937f 100644 --- a/drivers/pwm/pwm-bcm2835.c +++ b/drivers/pwm/pwm-bcm2835.c @@ -167,6 +167,8 @@ 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); From acfc3e348c4132d44c433b784f83fe3a26e1e328 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Thu, 27 Jul 2017 16:23:36 +0800 Subject: [PATCH 14/32] dt-bindings: pwm: Add bindings doc for ZTE ZX PWM controller It adds bindings document for ZTE ZX PWM controller. The device has two clocks: PCLK and WCLK. The PCLK is for register access, and WCLK is the reference clock for calculating period and duty cycles. Also, the device supports polarity configuration, so #pwm-cells should be 3. Signed-off-by: Shawn Guo Acked-by: Rob Herring Signed-off-by: Thierry Reding --- .../devicetree/bindings/pwm/pwm-zx.txt | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 Documentation/devicetree/bindings/pwm/pwm-zx.txt diff --git a/Documentation/devicetree/bindings/pwm/pwm-zx.txt b/Documentation/devicetree/bindings/pwm/pwm-zx.txt new file mode 100644 index 000000000000..a6bcc75c9164 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-zx.txt @@ -0,0 +1,22 @@ +ZTE ZX PWM controller + +Required properties: + - compatible: Should be "zte,zx296718-pwm". + - reg: Physical base address and length of the controller's registers. + - clocks : The phandle and specifier referencing the controller's clocks. + - clock-names: "pclk" for PCLK, "wclk" for WCLK to the PWM controller. The + PCLK is for register access, while WCLK is the reference clock for + calculating period and duty cycles. + - #pwm-cells: Should be 3. See pwm.txt in this directory for a description of + the cells format. + +Example: + + pwm: pwm@1439000 { + compatible = "zte,zx296718-pwm"; + reg = <0x1439000 0x1000>; + clocks = <&lsp1crm LSP1_PWM_PCLK>, + <&lsp1crm LSP1_PWM_WCLK>; + clock-names = "pclk", "wclk"; + #pwm-cells = <3>; + }; From 4836193c435c95443fdebb6b9d4a58032999dbb3 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Thu, 27 Jul 2017 16:23:37 +0800 Subject: [PATCH 15/32] pwm: Add ZTE ZX PWM device driver It adds PWM device driver for ZTE ZX family SoCs. The PWM controller supports 4 devices with polarity configuration. The driver has been tested with pwm-regulator support to scale core voltage via cpufreq. Signed-off-by: Shawn Guo Signed-off-by: Thierry Reding --- drivers/pwm/Kconfig | 9 ++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-zx.c | 282 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 292 insertions(+) create mode 100644 drivers/pwm/pwm-zx.c diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 313c10789ca2..e98175331a69 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -500,4 +500,13 @@ config PWM_VT8500 To compile this driver as a module, choose M here: the module will be called pwm-vt8500. +config PWM_ZX + tristate "ZTE ZX PWM support" + depends on ARCH_ZX + help + Generic PWM framework driver for ZTE ZX family SoCs. + + To compile this driver as a module, choose M here: the module + will be called pwm-zx. + endif diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 93da1f79a3b8..75ab74094d7b 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -49,3 +49,4 @@ obj-$(CONFIG_PWM_TIPWMSS) += pwm-tipwmss.o obj-$(CONFIG_PWM_TWL) += pwm-twl.o obj-$(CONFIG_PWM_TWL_LED) += pwm-twl-led.o obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o +obj-$(CONFIG_PWM_ZX) += pwm-zx.o diff --git a/drivers/pwm/pwm-zx.c b/drivers/pwm/pwm-zx.c new file mode 100644 index 000000000000..5d27c16edfb1 --- /dev/null +++ b/drivers/pwm/pwm-zx.c @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2017 Sanechips Technology Co., Ltd. + * Copyright 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define ZX_PWM_MODE 0x0 +#define ZX_PWM_CLKDIV_SHIFT 2 +#define ZX_PWM_CLKDIV_MASK GENMASK(11, 2) +#define ZX_PWM_CLKDIV(x) (((x) << ZX_PWM_CLKDIV_SHIFT) & \ + ZX_PWM_CLKDIV_MASK) +#define ZX_PWM_POLAR BIT(1) +#define ZX_PWM_EN BIT(0) +#define ZX_PWM_PERIOD 0x4 +#define ZX_PWM_DUTY 0x8 + +#define ZX_PWM_CLKDIV_MAX 1023 +#define ZX_PWM_PERIOD_MAX 65535 + +struct zx_pwm_chip { + struct pwm_chip chip; + struct clk *pclk; + struct clk *wclk; + void __iomem *base; +}; + +static inline struct zx_pwm_chip *to_zx_pwm_chip(struct pwm_chip *chip) +{ + return container_of(chip, struct zx_pwm_chip, chip); +} + +static inline u32 zx_pwm_readl(struct zx_pwm_chip *zpc, unsigned int hwpwm, + unsigned int offset) +{ + return readl(zpc->base + (hwpwm + 1) * 0x10 + offset); +} + +static inline void zx_pwm_writel(struct zx_pwm_chip *zpc, unsigned int hwpwm, + unsigned int offset, u32 value) +{ + writel(value, zpc->base + (hwpwm + 1) * 0x10 + offset); +} + +static void zx_pwm_set_mask(struct zx_pwm_chip *zpc, unsigned int hwpwm, + unsigned int offset, u32 mask, u32 value) +{ + u32 data; + + data = zx_pwm_readl(zpc, hwpwm, offset); + data &= ~mask; + data |= value & mask; + zx_pwm_writel(zpc, hwpwm, offset, data); +} + +static void zx_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + struct zx_pwm_chip *zpc = to_zx_pwm_chip(chip); + unsigned long rate; + unsigned int div; + u32 value; + u64 tmp; + + value = zx_pwm_readl(zpc, pwm->hwpwm, ZX_PWM_MODE); + + if (value & ZX_PWM_POLAR) + state->polarity = PWM_POLARITY_NORMAL; + else + state->polarity = PWM_POLARITY_INVERSED; + + if (value & ZX_PWM_EN) + state->enabled = true; + else + state->enabled = false; + + div = (value & ZX_PWM_CLKDIV_MASK) >> ZX_PWM_CLKDIV_SHIFT; + rate = clk_get_rate(zpc->wclk); + + tmp = zx_pwm_readl(zpc, pwm->hwpwm, ZX_PWM_PERIOD); + tmp *= div * NSEC_PER_SEC; + state->period = DIV_ROUND_CLOSEST_ULL(tmp, rate); + + tmp = zx_pwm_readl(zpc, pwm->hwpwm, ZX_PWM_DUTY); + tmp *= div * NSEC_PER_SEC; + state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, rate); +} + +static int zx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, + unsigned int duty_ns, unsigned int period_ns) +{ + struct zx_pwm_chip *zpc = to_zx_pwm_chip(chip); + unsigned int period_cycles, duty_cycles; + unsigned long long c; + unsigned int div = 1; + unsigned long rate; + + /* Find out the best divider */ + rate = clk_get_rate(zpc->wclk); + + while (1) { + c = rate / div; + c = c * period_ns; + do_div(c, NSEC_PER_SEC); + + if (c < ZX_PWM_PERIOD_MAX) + break; + + div++; + + if (div > ZX_PWM_CLKDIV_MAX) + return -ERANGE; + } + + /* Calculate duty cycles */ + period_cycles = c; + c *= duty_ns; + do_div(c, period_ns); + duty_cycles = c; + + /* + * If the PWM is being enabled, we have to temporarily disable it + * before configuring the registers. + */ + if (pwm_is_enabled(pwm)) + zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE, ZX_PWM_EN, 0); + + /* Set up registers */ + zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE, ZX_PWM_CLKDIV_MASK, + ZX_PWM_CLKDIV(div)); + zx_pwm_writel(zpc, pwm->hwpwm, ZX_PWM_PERIOD, period_cycles); + zx_pwm_writel(zpc, pwm->hwpwm, ZX_PWM_DUTY, duty_cycles); + + /* Re-enable the PWM if needed */ + if (pwm_is_enabled(pwm)) + zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE, + ZX_PWM_EN, ZX_PWM_EN); + + return 0; +} + +static int zx_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + struct zx_pwm_chip *zpc = to_zx_pwm_chip(chip); + struct pwm_state cstate; + int ret; + + pwm_get_state(pwm, &cstate); + + if (state->polarity != cstate.polarity) + zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE, ZX_PWM_POLAR, + (state->polarity == PWM_POLARITY_INVERSED) ? + 0 : ZX_PWM_POLAR); + + if (state->period != cstate.period || + state->duty_cycle != cstate.duty_cycle) { + ret = zx_pwm_config(chip, pwm, state->duty_cycle, + state->period); + if (ret) + return ret; + } + + if (state->enabled != cstate.enabled) { + if (state->enabled) { + ret = clk_prepare_enable(zpc->wclk); + if (ret) + return ret; + + zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE, + ZX_PWM_EN, ZX_PWM_EN); + } else { + zx_pwm_set_mask(zpc, pwm->hwpwm, ZX_PWM_MODE, + ZX_PWM_EN, 0); + clk_disable_unprepare(zpc->wclk); + } + } + + return 0; +} + +static const struct pwm_ops zx_pwm_ops = { + .apply = zx_pwm_apply, + .get_state = zx_pwm_get_state, + .owner = THIS_MODULE, +}; + +static int zx_pwm_probe(struct platform_device *pdev) +{ + struct zx_pwm_chip *zpc; + struct resource *res; + unsigned int i; + int ret; + + zpc = devm_kzalloc(&pdev->dev, sizeof(*zpc), GFP_KERNEL); + if (!zpc) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + zpc->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(zpc->base)) + return PTR_ERR(zpc->base); + + zpc->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(zpc->pclk)) + return PTR_ERR(zpc->pclk); + + zpc->wclk = devm_clk_get(&pdev->dev, "wclk"); + if (IS_ERR(zpc->wclk)) + return PTR_ERR(zpc->wclk); + + ret = clk_prepare_enable(zpc->pclk); + if (ret) + return ret; + + zpc->chip.dev = &pdev->dev; + zpc->chip.ops = &zx_pwm_ops; + zpc->chip.base = -1; + zpc->chip.npwm = 4; + zpc->chip.of_xlate = of_pwm_xlate_with_flags; + zpc->chip.of_pwm_n_cells = 3; + + /* + * PWM devices may be enabled by firmware, and let's disable all of + * them initially to save power. + */ + for (i = 0; i < zpc->chip.npwm; i++) + zx_pwm_set_mask(zpc, i, ZX_PWM_MODE, ZX_PWM_EN, 0); + + ret = pwmchip_add(&zpc->chip); + if (ret < 0) { + dev_err(&pdev->dev, "failed to add PWM chip: %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, zpc); + + return 0; +} + +static int zx_pwm_remove(struct platform_device *pdev) +{ + struct zx_pwm_chip *zpc = platform_get_drvdata(pdev); + int ret; + + ret = pwmchip_remove(&zpc->chip); + clk_disable_unprepare(zpc->pclk); + + return ret; +} + +static const struct of_device_id zx_pwm_dt_ids[] = { + { .compatible = "zte,zx296718-pwm", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, zx_pwm_dt_ids); + +static struct platform_driver zx_pwm_driver = { + .driver = { + .name = "zx-pwm", + .of_match_table = zx_pwm_dt_ids, + }, + .probe = zx_pwm_probe, + .remove = zx_pwm_remove, +}; +module_platform_driver(zx_pwm_driver); + +MODULE_ALIAS("platform:zx-pwm"); +MODULE_AUTHOR("Shawn Guo "); +MODULE_DESCRIPTION("ZTE ZX PWM Driver"); +MODULE_LICENSE("GPL v2"); From cd9b53daafaa635219b46a79f00177ddd3081e4f Mon Sep 17 00:00:00 2001 From: Vignesh R Date: Wed, 2 Aug 2017 11:43:44 +0530 Subject: [PATCH 16/32] pwm: Kconfig: Enable pwm-tiecap to be built for Keystone 66AK2G SoC has ECAP subsystem that is used as pwm-backlight provider for display. Hence, enable pwm-tiecap driver to be built for Keystone architecture. Signed-off-by: Vignesh R Signed-off-by: Thierry Reding --- drivers/pwm/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index e98175331a69..a288c1b2689f 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -446,7 +446,7 @@ config PWM_TEGRA config PWM_TIECAP tristate "ECAP PWM support" - depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX + depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_KEYSTONE help PWM driver support for the ECAP APWM controller found on AM33XX TI SOC From c7fdd3f52944b81d807ce7a5fde7d1ca8a2a0919 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 20 Jul 2017 12:48:16 +0200 Subject: [PATCH 17/32] pwm: tiehrpwm: Fix runtime PM imbalance at unbind Remove unbalanced RPM put at driver unbind which resulted in a negative usage count. Fixes: 19891b20e7c2 ("pwm: pwm-tiehrpwm: PWM driver support for EHRPWM") Signed-off-by: Johan Hovold Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tiehrpwm.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c index b5c6b0636893..6e51a075d1a7 100644 --- a/drivers/pwm/pwm-tiehrpwm.c +++ b/drivers/pwm/pwm-tiehrpwm.c @@ -504,7 +504,6 @@ static int ehrpwm_pwm_remove(struct platform_device *pdev) clk_unprepare(pc->tbclk); - pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); return pwmchip_remove(&pc->chip); } From e2b5602af76dec75f474e4173afb4215007ecfa5 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 20 Jul 2017 12:48:17 +0200 Subject: [PATCH 18/32] pwm: tiehrpwm: fix clock imbalance in probe error path Make sure to unprepare the clock before returning on late probe errors. Fixes: b388f15fd14c ("pwm: pwm-tiehrpwm: Use clk_enable/disable instead clk_prepare/unprepare.") Signed-off-by: Johan Hovold Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tiehrpwm.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c index 6e51a075d1a7..387eaf1bac85 100644 --- a/drivers/pwm/pwm-tiehrpwm.c +++ b/drivers/pwm/pwm-tiehrpwm.c @@ -489,13 +489,18 @@ static int ehrpwm_pwm_probe(struct platform_device *pdev) ret = pwmchip_add(&pc->chip); if (ret < 0) { dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); - return ret; + goto err_clk_unprepare; } pm_runtime_enable(&pdev->dev); platform_set_drvdata(pdev, pc); return 0; + +err_clk_unprepare: + clk_unprepare(pc->tbclk); + + return ret; } static int ehrpwm_pwm_remove(struct platform_device *pdev) From 999f6f71f4b56f91acb9626ddc6fa188ec8f0cb3 Mon Sep 17 00:00:00 2001 From: Vignesh R Date: Mon, 7 Aug 2017 17:19:40 +0530 Subject: [PATCH 19/32] dt-bindings: pwm: tiecap: Add TI 66AK2G SoC specific compatible Add a new compatible string "ti,k2g-ecap" to support PWM ECAP IP of TI 66AK2G SoC. Signed-off-by: Vignesh R Acked-by: Rob Herring Signed-off-by: Thierry Reding --- Documentation/devicetree/bindings/pwm/pwm-tiecap.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt b/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt index 8007e839a716..06a363d9ccef 100644 --- a/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt +++ b/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt @@ -6,6 +6,7 @@ Required properties: 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"; - #pwm-cells: should be 3. See pwm.txt 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. From 53c7972d6d39db96a3c68752a2ce8c7f6e0c73b4 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 21 Aug 2017 08:29:41 +0200 Subject: [PATCH 20/32] pwm: tiecap: Miscellaneous coding style fixups I noticed most of these while reviewing another patch and thought I'd fix them while at it. These are mostly changes to make variable types more strict and whitespace fixups. Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tiecap.c | 88 +++++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 41 deletions(-) diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c index 6ec342dd3eea..635408af2cf0 100644 --- a/drivers/pwm/pwm-tiecap.c +++ b/drivers/pwm/pwm-tiecap.c @@ -39,15 +39,15 @@ #define ECCTL2_TSCTR_FREERUN BIT(4) struct ecap_context { - u32 cap3; - u32 cap4; - u16 ecctl2; + u32 cap3; + u32 cap4; + u16 ecctl2; }; struct ecap_pwm_chip { - struct pwm_chip chip; - unsigned int clk_rate; - void __iomem *mmio_base; + struct pwm_chip chip; + unsigned int clk_rate; + void __iomem *mmio_base; struct ecap_context ctx; }; @@ -64,9 +64,9 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip); + u32 period_cycles, duty_cycles; unsigned long long c; - unsigned long period_cycles, duty_cycles; - unsigned int reg_val; + u16 value; if (period_ns > NSEC_PER_SEC) return -ERANGE; @@ -74,7 +74,7 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, c = pc->clk_rate; c = c * period_ns; do_div(c, NSEC_PER_SEC); - period_cycles = (unsigned long)c; + period_cycles = (u32)c; if (period_cycles < 1) { period_cycles = 1; @@ -83,17 +83,17 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, c = pc->clk_rate; c = c * duty_ns; do_div(c, NSEC_PER_SEC); - duty_cycles = (unsigned long)c; + duty_cycles = (u32)c; } pm_runtime_get_sync(pc->chip.dev); - reg_val = readw(pc->mmio_base + ECCTL2); + value = readw(pc->mmio_base + ECCTL2); /* Configure APWM mode & disable sync option */ - reg_val |= ECCTL2_APWM_MODE | ECCTL2_SYNC_SEL_DISA; + value |= ECCTL2_APWM_MODE | ECCTL2_SYNC_SEL_DISA; - writew(reg_val, pc->mmio_base + ECCTL2); + writew(value, pc->mmio_base + ECCTL2); if (!pwm_is_enabled(pwm)) { /* Update active registers if not running */ @@ -110,40 +110,45 @@ static int ecap_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, } if (!pwm_is_enabled(pwm)) { - reg_val = readw(pc->mmio_base + ECCTL2); + value = readw(pc->mmio_base + ECCTL2); /* Disable APWM mode to put APWM output Low */ - reg_val &= ~ECCTL2_APWM_MODE; - writew(reg_val, pc->mmio_base + ECCTL2); + value &= ~ECCTL2_APWM_MODE; + writew(value, pc->mmio_base + ECCTL2); } pm_runtime_put_sync(pc->chip.dev); + return 0; } static int ecap_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, - enum pwm_polarity polarity) + enum pwm_polarity polarity) { struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip); - unsigned short reg_val; + u16 value; pm_runtime_get_sync(pc->chip.dev); - reg_val = readw(pc->mmio_base + ECCTL2); + + value = readw(pc->mmio_base + ECCTL2); + if (polarity == PWM_POLARITY_INVERSED) /* Duty cycle defines LOW period of PWM */ - reg_val |= ECCTL2_APWM_POL_LOW; + value |= ECCTL2_APWM_POL_LOW; else /* Duty cycle defines HIGH period of PWM */ - reg_val &= ~ECCTL2_APWM_POL_LOW; + value &= ~ECCTL2_APWM_POL_LOW; + + writew(value, pc->mmio_base + ECCTL2); - writew(reg_val, pc->mmio_base + ECCTL2); pm_runtime_put_sync(pc->chip.dev); + return 0; } static int ecap_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip); - unsigned int reg_val; + u16 value; /* Leave clock enabled on enabling PWM */ pm_runtime_get_sync(pc->chip.dev); @@ -152,24 +157,25 @@ static int ecap_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) * Enable 'Free run Time stamp counter mode' to start counter * and 'APWM mode' to enable APWM output */ - reg_val = readw(pc->mmio_base + ECCTL2); - reg_val |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE; - writew(reg_val, pc->mmio_base + ECCTL2); + value = readw(pc->mmio_base + ECCTL2); + value |= ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE; + writew(value, pc->mmio_base + ECCTL2); + return 0; } static void ecap_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) { struct ecap_pwm_chip *pc = to_ecap_pwm_chip(chip); - unsigned int reg_val; + u16 value; /* * Disable 'Free run Time stamp counter mode' to stop counter * and 'APWM mode' to put APWM output to low */ - reg_val = readw(pc->mmio_base + ECCTL2); - reg_val &= ~(ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE); - writew(reg_val, pc->mmio_base + ECCTL2); + value = readw(pc->mmio_base + ECCTL2); + value &= ~(ECCTL2_TSCTR_FREERUN | ECCTL2_APWM_MODE); + writew(value, pc->mmio_base + ECCTL2); /* Disable clock on PWM disable */ pm_runtime_put_sync(pc->chip.dev); @@ -184,12 +190,12 @@ static void ecap_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) } 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, - .disable = ecap_pwm_disable, - .owner = THIS_MODULE, + .free = ecap_pwm_free, + .config = ecap_pwm_config, + .set_polarity = ecap_pwm_set_polarity, + .enable = ecap_pwm_enable, + .disable = ecap_pwm_disable, + .owner = THIS_MODULE, }; static const struct of_device_id ecap_of_match[] = { @@ -202,10 +208,10 @@ MODULE_DEVICE_TABLE(of, ecap_of_match); static int ecap_pwm_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - int ret; + struct ecap_pwm_chip *pc; struct resource *r; struct clk *clk; - struct ecap_pwm_chip *pc; + int ret; pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); if (!pc) @@ -259,6 +265,7 @@ static int ecap_pwm_remove(struct platform_device *pdev) struct ecap_pwm_chip *pc = platform_get_drvdata(pdev); pm_runtime_disable(&pdev->dev); + return pwmchip_remove(&pc->chip); } @@ -311,14 +318,13 @@ static SIMPLE_DEV_PM_OPS(ecap_pwm_pm_ops, ecap_pwm_suspend, ecap_pwm_resume); static struct platform_driver ecap_pwm_driver = { .driver = { - .name = "ecap", + .name = "ecap", .of_match_table = ecap_of_match, - .pm = &ecap_pwm_pm_ops, + .pm = &ecap_pwm_pm_ops, }, .probe = ecap_pwm_probe, .remove = ecap_pwm_remove, }; - module_platform_driver(ecap_pwm_driver); MODULE_DESCRIPTION("ECAP PWM driver"); From 23f373e6fe7462df2d921eeced5d36da1244990e Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 21 Aug 2017 08:31:37 +0200 Subject: [PATCH 21/32] pwm: tiecap: Set driver data before runtime PM enable Runtime PM callbacks can be run right after runtime PM is enabled, so make sure to set the driver data before that. This is unlikely to ever happen with the current driver, but it doesn't hurt to follow best practices anyway. Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tiecap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c index 635408af2cf0..34b228626bd5 100644 --- a/drivers/pwm/pwm-tiecap.c +++ b/drivers/pwm/pwm-tiecap.c @@ -254,9 +254,9 @@ static int ecap_pwm_probe(struct platform_device *pdev) return ret; } + platform_set_drvdata(pdev, pc); pm_runtime_enable(&pdev->dev); - platform_set_drvdata(pdev, pc); return 0; } From d2c95e47f868322fe8c07495f1c589ebeb1c3fc9 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 21 Aug 2017 08:42:25 +0200 Subject: [PATCH 22/32] pwm: tiehrpwm: Miscellaneous coding style fixups I noticed most of these while reviewing another patch and thought I'd fix them while at it. These are mostly changes to make variable types more strict and whitespace fixups. Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tiehrpwm.c | 112 ++++++++++++++++++++----------------- 1 file changed, 62 insertions(+), 50 deletions(-) diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c index 387eaf1bac85..6f87a0d5828d 100644 --- a/drivers/pwm/pwm-tiehrpwm.c +++ b/drivers/pwm/pwm-tiehrpwm.c @@ -122,12 +122,12 @@ struct ehrpwm_context { }; struct ehrpwm_pwm_chip { - struct pwm_chip chip; - unsigned int clk_rate; - void __iomem *mmio_base; + struct pwm_chip chip; + unsigned long clk_rate; + void __iomem *mmio_base; unsigned long period_cycles[NUM_PWM_CHANNEL]; enum pwm_polarity polarity[NUM_PWM_CHANNEL]; - struct clk *tbclk; + struct clk *tbclk; struct ehrpwm_context ctx; }; @@ -136,25 +136,26 @@ static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip) return container_of(chip, struct ehrpwm_pwm_chip, chip); } -static inline u16 ehrpwm_read(void __iomem *base, int offset) +static inline u16 ehrpwm_read(void __iomem *base, unsigned int offset) { return readw(base + offset); } -static inline void ehrpwm_write(void __iomem *base, int offset, unsigned int val) +static inline void ehrpwm_write(void __iomem *base, unsigned int offset, + u16 value) { - writew(val & 0xFFFF, base + offset); + writew(value, base + offset); } -static void ehrpwm_modify(void __iomem *base, int offset, - unsigned short mask, unsigned short val) +static void ehrpwm_modify(void __iomem *base, unsigned int offset, u16 mask, + u16 value) { - unsigned short regval; + unsigned short val; - regval = readw(base + offset); - regval &= ~mask; - regval |= val & mask; - writew(regval, base + offset); + val = readw(base + offset); + val &= ~mask; + val |= value & mask; + writew(val, base + offset); } /** @@ -163,14 +164,13 @@ static void ehrpwm_modify(void __iomem *base, int offset, * @prescale_div: prescaler value set * @tb_clk_div: Time Base Control prescaler bits */ -static int set_prescale_div(unsigned long rqst_prescaler, - unsigned short *prescale_div, unsigned short *tb_clk_div) +static int set_prescale_div(unsigned long rqst_prescaler, u16 *prescale_div, + u16 *tb_clk_div) { unsigned int clkdiv, hspclkdiv; for (clkdiv = 0; clkdiv <= CLKDIV_MAX; clkdiv++) { for (hspclkdiv = 0; hspclkdiv <= HSPCLKDIV_MAX; hspclkdiv++) { - /* * calculations for prescaler value : * prescale_div = HSPCLKDIVIDER * CLKDIVIDER. @@ -191,13 +191,14 @@ static int set_prescale_div(unsigned long rqst_prescaler, } } } + return 1; } static void configure_polarity(struct ehrpwm_pwm_chip *pc, int chan) { - int aqctl_reg; - unsigned short aqctl_val, aqctl_mask; + u16 aqctl_val, aqctl_mask; + unsigned int aqctl_reg; /* * Configure PWM output to HIGH/LOW level on counter @@ -232,13 +233,13 @@ static void configure_polarity(struct ehrpwm_pwm_chip *pc, int chan) * duty_ns = 10^9 * (ps_divval * duty_cycles) / PWM_CLK_RATE */ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) + int duty_ns, int period_ns) { struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); + u32 period_cycles, duty_cycles; + u16 ps_divval, tb_divval; + unsigned int i, cmp_reg; unsigned long long c; - unsigned long period_cycles, duty_cycles; - unsigned short ps_divval, tb_divval; - int i, cmp_reg; if (period_ns > NSEC_PER_SEC) return -ERANGE; @@ -272,8 +273,9 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, if (i == pwm->hwpwm) continue; - dev_err(chip->dev, "Period value conflicts with channel %d\n", - i); + dev_err(chip->dev, + "period value conflicts with channel %u\n", + i); return -EINVAL; } } @@ -282,7 +284,7 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, /* Configure clock prescaler to support Low frequency PWM wave */ if (set_prescale_div(period_cycles/PERIOD_MAX, &ps_divval, - &tb_divval)) { + &tb_divval)) { dev_err(chip->dev, "Unsupported values\n"); return -EINVAL; } @@ -303,7 +305,7 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, /* Configure ehrpwm counter for up-count mode */ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_CTRMODE_MASK, - TBCTL_CTRMODE_UP); + TBCTL_CTRMODE_UP); if (pwm->hwpwm == 1) /* Channel 1 configured with compare B register */ @@ -315,23 +317,26 @@ static int ehrpwm_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ehrpwm_write(pc->mmio_base, cmp_reg, duty_cycles); pm_runtime_put_sync(chip->dev); + return 0; } static int ehrpwm_pwm_set_polarity(struct pwm_chip *chip, - struct pwm_device *pwm, enum pwm_polarity polarity) + struct pwm_device *pwm, + enum pwm_polarity polarity) { struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); /* Configuration of polarity in hardware delayed, do at enable */ pc->polarity[pwm->hwpwm] = polarity; + return 0; } static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) { struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); - unsigned short aqcsfrc_val, aqcsfrc_mask; + u16 aqcsfrc_val, aqcsfrc_mask; int ret; /* Leave clock enabled on enabling PWM */ @@ -348,7 +353,7 @@ static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) /* Changes to shadow mode */ ehrpwm_modify(pc->mmio_base, AQSFRC, AQSFRC_RLDCSF_MASK, - AQSFRC_RLDCSF_ZRO); + AQSFRC_RLDCSF_ZRO); ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val); @@ -358,20 +363,21 @@ static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) /* Enable TBCLK before enabling PWM device */ ret = clk_enable(pc->tbclk); if (ret) { - dev_err(chip->dev, "Failed to enable TBCLK for %s\n", - dev_name(pc->chip.dev)); + dev_err(chip->dev, "Failed to enable TBCLK for %s: %d\n", + dev_name(pc->chip.dev), ret); return ret; } /* Enable time counter for free_run */ ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_FREE_RUN); + return 0; } static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) { struct ehrpwm_pwm_chip *pc = to_ehrpwm_pwm_chip(chip); - unsigned short aqcsfrc_val, aqcsfrc_mask; + u16 aqcsfrc_val, aqcsfrc_mask; /* Action Qualifier puts PWM output low forcefully */ if (pwm->hwpwm) { @@ -387,7 +393,7 @@ static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) * Action Qualifier control on PWM output from next TBCLK */ ehrpwm_modify(pc->mmio_base, AQSFRC, AQSFRC_RLDCSF_MASK, - AQSFRC_RLDCSF_IMDT); + AQSFRC_RLDCSF_IMDT); ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val); @@ -415,17 +421,17 @@ static void ehrpwm_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) } static const struct pwm_ops ehrpwm_pwm_ops = { - .free = ehrpwm_pwm_free, - .config = ehrpwm_pwm_config, - .set_polarity = ehrpwm_pwm_set_polarity, - .enable = ehrpwm_pwm_enable, - .disable = ehrpwm_pwm_disable, - .owner = THIS_MODULE, + .free = ehrpwm_pwm_free, + .config = ehrpwm_pwm_config, + .set_polarity = ehrpwm_pwm_set_polarity, + .enable = ehrpwm_pwm_enable, + .disable = ehrpwm_pwm_disable, + .owner = THIS_MODULE, }; static const struct of_device_id ehrpwm_of_match[] = { - { .compatible = "ti,am3352-ehrpwm" }, - { .compatible = "ti,am33xx-ehrpwm" }, + { .compatible = "ti,am3352-ehrpwm" }, + { .compatible = "ti,am33xx-ehrpwm" }, {}, }; MODULE_DEVICE_TABLE(of, ehrpwm_of_match); @@ -433,10 +439,10 @@ MODULE_DEVICE_TABLE(of, ehrpwm_of_match); static int ehrpwm_pwm_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - int ret; + struct ehrpwm_pwm_chip *pc; struct resource *r; struct clk *clk; - struct ehrpwm_pwm_chip *pc; + int ret; pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); if (!pc) @@ -510,6 +516,7 @@ static int ehrpwm_pwm_remove(struct platform_device *pdev) clk_unprepare(pc->tbclk); pm_runtime_disable(&pdev->dev); + return pwmchip_remove(&pc->chip); } @@ -517,6 +524,7 @@ static int ehrpwm_pwm_remove(struct platform_device *pdev) static void ehrpwm_pwm_save_context(struct ehrpwm_pwm_chip *pc) { pm_runtime_get_sync(pc->chip.dev); + pc->ctx.tbctl = ehrpwm_read(pc->mmio_base, TBCTL); pc->ctx.tbprd = ehrpwm_read(pc->mmio_base, TBPRD); pc->ctx.cmpa = ehrpwm_read(pc->mmio_base, CMPA); @@ -525,6 +533,7 @@ static void ehrpwm_pwm_save_context(struct ehrpwm_pwm_chip *pc) pc->ctx.aqctlb = ehrpwm_read(pc->mmio_base, AQCTLB); pc->ctx.aqsfrc = ehrpwm_read(pc->mmio_base, AQSFRC); pc->ctx.aqcsfrc = ehrpwm_read(pc->mmio_base, AQCSFRC); + pm_runtime_put_sync(pc->chip.dev); } @@ -543,9 +552,10 @@ static void ehrpwm_pwm_restore_context(struct ehrpwm_pwm_chip *pc) static int ehrpwm_pwm_suspend(struct device *dev) { struct ehrpwm_pwm_chip *pc = dev_get_drvdata(dev); - int i; + unsigned int i; ehrpwm_pwm_save_context(pc); + for (i = 0; i < pc->chip.npwm; i++) { struct pwm_device *pwm = &pc->chip.pwms[i]; @@ -555,13 +565,14 @@ static int ehrpwm_pwm_suspend(struct device *dev) /* Disable explicitly if PWM is running */ pm_runtime_put_sync(dev); } + return 0; } static int ehrpwm_pwm_resume(struct device *dev) { struct ehrpwm_pwm_chip *pc = dev_get_drvdata(dev); - int i; + unsigned int i; for (i = 0; i < pc->chip.npwm; i++) { struct pwm_device *pwm = &pc->chip.pwms[i]; @@ -572,24 +583,25 @@ static int ehrpwm_pwm_resume(struct device *dev) /* Enable explicitly if PWM was running */ pm_runtime_get_sync(dev); } + ehrpwm_pwm_restore_context(pc); + return 0; } #endif static SIMPLE_DEV_PM_OPS(ehrpwm_pwm_pm_ops, ehrpwm_pwm_suspend, - ehrpwm_pwm_resume); + ehrpwm_pwm_resume); static struct platform_driver ehrpwm_pwm_driver = { .driver = { - .name = "ehrpwm", + .name = "ehrpwm", .of_match_table = ehrpwm_of_match, - .pm = &ehrpwm_pwm_pm_ops, + .pm = &ehrpwm_pwm_pm_ops, }, .probe = ehrpwm_pwm_probe, .remove = ehrpwm_pwm_remove, }; - module_platform_driver(ehrpwm_pwm_driver); MODULE_DESCRIPTION("EHRPWM PWM driver"); From d870c80e1d1523e449e98d230ea048257123b7cb Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 21 Aug 2017 08:42:56 +0200 Subject: [PATCH 23/32] pwm: tiehrpwm: Set driver data before runtime PM enable Runtime PM callbacks can be run right after runtime PM is enabled, so make sure to set the driver data before that. This is unlikely to ever happen with the current driver, but it doesn't hurt to follow best practices anyway. Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tiehrpwm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c index 6f87a0d5828d..4c22cb395040 100644 --- a/drivers/pwm/pwm-tiehrpwm.c +++ b/drivers/pwm/pwm-tiehrpwm.c @@ -498,9 +498,9 @@ static int ehrpwm_pwm_probe(struct platform_device *pdev) goto err_clk_unprepare; } + platform_set_drvdata(pdev, pc); pm_runtime_enable(&pdev->dev); - platform_set_drvdata(pdev, pc); return 0; err_clk_unprepare: From 0fd3b93f612e6c7e9a7e8807a8cd421c1bb87643 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Wed, 19 Jul 2017 17:26:13 +0200 Subject: [PATCH 24/32] pwm: hibvt: Explicitly request exclusive reset control Commit a53e35db70d1 ("reset: Ensure drivers are explicit when requesting reset lines") started to transition the reset control request API calls to explicitly state whether the driver needs exclusive or shared reset control behavior. Convert all drivers requesting exclusive resets to the explicit API call so the temporary transition helpers can be removed. No functional changes. Cc: Thierry Reding Cc: linux-pwm@vger.kernel.org Signed-off-by: Philipp Zabel Signed-off-by: Thierry Reding --- drivers/pwm/pwm-hibvt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-hibvt.c b/drivers/pwm/pwm-hibvt.c index 8dadc58d6cdf..27c107e78d59 100644 --- a/drivers/pwm/pwm-hibvt.c +++ b/drivers/pwm/pwm-hibvt.c @@ -208,7 +208,7 @@ static int hibvt_pwm_probe(struct platform_device *pdev) if (ret < 0) return ret; - pwm_chip->rstc = devm_reset_control_get(&pdev->dev, NULL); + pwm_chip->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(pwm_chip->rstc)) { clk_disable_unprepare(pwm_chip->clk); return PTR_ERR(pwm_chip->rstc); From 6b03ef24eac778f2af8d4f17ce6f638e393b0024 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Wed, 19 Jul 2017 17:26:14 +0200 Subject: [PATCH 25/32] pwm: tegra: Explicitly request exclusive reset control Commit a53e35db70d1 ("reset: Ensure drivers are explicit when requesting reset lines") started to transition the reset control request API calls to explicitly state whether the driver needs exclusive or shared reset control behavior. Convert all drivers requesting exclusive resets to the explicit API call so the temporary transition helpers can be removed. No functional changes. Cc: Thierry Reding Cc: Jonathan Hunter Cc: linux-pwm@vger.kernel.org Cc: linux-tegra@vger.kernel.org Signed-off-by: Philipp Zabel Signed-off-by: Thierry Reding --- drivers/pwm/pwm-tegra.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c index e9b33f09ff09..f8ebbece57b7 100644 --- a/drivers/pwm/pwm-tegra.c +++ b/drivers/pwm/pwm-tegra.c @@ -218,7 +218,7 @@ static int tegra_pwm_probe(struct platform_device *pdev) */ pwm->clk_rate = clk_get_rate(pwm->clk); - pwm->rst = devm_reset_control_get(&pdev->dev, "pwm"); + pwm->rst = devm_reset_control_get_exclusive(&pdev->dev, "pwm"); if (IS_ERR(pwm->rst)) { ret = PTR_ERR(pwm->rst); dev_err(&pdev->dev, "Reset control is not found: %d\n", ret); From aa12d7a7a978ac5f3202cac8f2f671fd267bf5e3 Mon Sep 17 00:00:00 2001 From: Zhi Mao Date: Fri, 30 Jun 2017 14:05:16 +0800 Subject: [PATCH 26/32] pwm: mediatek: Fix Kconfig description Fix a copy/paste error that sneaked into the Kconfig description of the Mediatek PWM driver. Signed-off-by: Zhi Mao Acked-by: John Crispin Signed-off-by: Thierry Reding --- drivers/pwm/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index a288c1b2689f..abfddcef36d1 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -300,7 +300,7 @@ config PWM_MEDIATEK Generic PWM framework driver for Mediatek ARM SoC. To compile this driver as a module, choose M here: the module - will be called pwm-mxs. + will be called pwm-mediatek. config PWM_MXS tristate "Freescale MXS PWM support" From cd30798a6c17c1fa182e9b0bb85bd973776ff193 Mon Sep 17 00:00:00 2001 From: Zhi Mao Date: Fri, 30 Jun 2017 14:05:17 +0800 Subject: [PATCH 27/32] pwm: mediatek: Fix PWM source clock selection In original code, the PWM output frequency is not correct when set bit<3>=1 to PWMCON register. Signed-off-by: Zhi Mao Reviewed-by: Matthias Brugger Acked-by: John Crispin Signed-off-by: Thierry Reding --- drivers/pwm/pwm-mediatek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c index 5c11bc708a3c..d08b5b3dca71 100644 --- a/drivers/pwm/pwm-mediatek.c +++ b/drivers/pwm/pwm-mediatek.c @@ -91,7 +91,7 @@ static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, if (clkdiv > 7) return -EINVAL; - mtk_pwm_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | BIT(3) | clkdiv); + mtk_pwm_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv); mtk_pwm_writel(pc, pwm->hwpwm, PWMDWIDTH, period_ns / resolution); mtk_pwm_writel(pc, pwm->hwpwm, PWMTHRES, duty_ns / resolution); From e7c197ec97c3ac3ed3698be7a21d51a338582e1d Mon Sep 17 00:00:00 2001 From: Zhi Mao Date: Fri, 30 Jun 2017 14:05:18 +0800 Subject: [PATCH 28/32] pwm: mediatek: Fix clock control issue In order to save some power, do not prepare the top and main clocks during mtk_pwm_probe(). Instead, prepare the clocks only when necessary and also make sure to enable the clocks to match the semantics of the common clock framework. While at it, don't explicitly disable all PWM channels in ->remove() because all users should have done that already. Signed-off-by: Zhi Mao Acked-by: John Crispin Signed-off-by: Thierry Reding --- drivers/pwm/pwm-mediatek.c | 69 ++++++++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c index d08b5b3dca71..637045998318 100644 --- a/drivers/pwm/pwm-mediatek.c +++ b/drivers/pwm/pwm-mediatek.c @@ -2,6 +2,7 @@ * Mediatek Pulse Width Modulator driver * * Copyright (C) 2015 John Crispin + * Copyright (C) 2017 Zhi Mao * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any @@ -61,6 +62,42 @@ static inline struct mtk_pwm_chip *to_mtk_pwm_chip(struct pwm_chip *chip) return container_of(chip, struct mtk_pwm_chip, chip); } +static int mtk_pwm_clk_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); + int ret; + + ret = clk_prepare_enable(pc->clks[MTK_CLK_TOP]); + if (ret < 0) + return ret; + + ret = clk_prepare_enable(pc->clks[MTK_CLK_MAIN]); + if (ret < 0) + goto disable_clk_top; + + ret = clk_prepare_enable(pc->clks[MTK_CLK_PWM1 + pwm->hwpwm]); + if (ret < 0) + goto disable_clk_main; + + return 0; + +disable_clk_main: + clk_disable_unprepare(pc->clks[MTK_CLK_MAIN]); +disable_clk_top: + clk_disable_unprepare(pc->clks[MTK_CLK_TOP]); + + return ret; +} + +static void mtk_pwm_clk_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); + + clk_disable_unprepare(pc->clks[MTK_CLK_PWM1 + pwm->hwpwm]); + clk_disable_unprepare(pc->clks[MTK_CLK_MAIN]); + clk_disable_unprepare(pc->clks[MTK_CLK_TOP]); +} + static inline u32 mtk_pwm_readl(struct mtk_pwm_chip *chip, unsigned int num, unsigned int offset) { @@ -80,6 +117,11 @@ static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, struct mtk_pwm_chip *pc = to_mtk_pwm_chip(chip); struct clk *clk = pc->clks[MTK_CLK_PWM1 + pwm->hwpwm]; u32 resolution, clkdiv = 0; + int ret; + + ret = mtk_pwm_clk_enable(chip, pwm); + if (ret < 0) + return ret; resolution = NSEC_PER_SEC / clk_get_rate(clk); @@ -95,6 +137,8 @@ static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, mtk_pwm_writel(pc, pwm->hwpwm, PWMDWIDTH, period_ns / resolution); mtk_pwm_writel(pc, pwm->hwpwm, PWMTHRES, duty_ns / resolution); + mtk_pwm_clk_disable(chip, pwm); + return 0; } @@ -104,7 +148,7 @@ static int mtk_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) u32 value; int ret; - ret = clk_prepare(pc->clks[MTK_CLK_PWM1 + pwm->hwpwm]); + ret = mtk_pwm_clk_enable(chip, pwm); if (ret < 0) return ret; @@ -124,7 +168,7 @@ static void mtk_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) value &= ~BIT(pwm->hwpwm); writel(value, pc->regs); - clk_unprepare(pc->clks[MTK_CLK_PWM1 + pwm->hwpwm]); + mtk_pwm_clk_disable(chip, pwm); } static const struct pwm_ops mtk_pwm_ops = { @@ -156,14 +200,6 @@ static int mtk_pwm_probe(struct platform_device *pdev) return PTR_ERR(pc->clks[i]); } - ret = clk_prepare(pc->clks[MTK_CLK_TOP]); - if (ret < 0) - return ret; - - ret = clk_prepare(pc->clks[MTK_CLK_MAIN]); - if (ret < 0) - goto disable_clk_top; - platform_set_drvdata(pdev, pc); pc->chip.dev = &pdev->dev; @@ -174,26 +210,15 @@ static int mtk_pwm_probe(struct platform_device *pdev) ret = pwmchip_add(&pc->chip); if (ret < 0) { dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); - goto disable_clk_main; + return ret; } return 0; - -disable_clk_main: - clk_unprepare(pc->clks[MTK_CLK_MAIN]); -disable_clk_top: - clk_unprepare(pc->clks[MTK_CLK_TOP]); - - return ret; } static int mtk_pwm_remove(struct platform_device *pdev) { struct mtk_pwm_chip *pc = platform_get_drvdata(pdev); - unsigned int i; - - for (i = 0; i < pc->chip.npwm; i++) - pwm_disable(&pc->chip.pwms[i]); return pwmchip_remove(&pc->chip); } From 62843a6152e7c19f28c368bb51cac1bbfcdf4249 Mon Sep 17 00:00:00 2001 From: Zhi Mao Date: Fri, 30 Jun 2017 14:05:19 +0800 Subject: [PATCH 29/32] dt-bindings: pwm: Add MT2712/MT7622 information Enhance the MediaTek PWM binding with details about the IP found in the MT2712 and MT7622 SoCs. Acked-by: Rob Herring Signed-off-by: Zhi Mao Acked-by: John Crispin Signed-off-by: Thierry Reding --- Documentation/devicetree/bindings/pwm/pwm-mediatek.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/pwm/pwm-mediatek.txt b/Documentation/devicetree/bindings/pwm/pwm-mediatek.txt index 54c59b0560ad..ef8bd3cb67ab 100644 --- a/Documentation/devicetree/bindings/pwm/pwm-mediatek.txt +++ b/Documentation/devicetree/bindings/pwm/pwm-mediatek.txt @@ -2,6 +2,8 @@ MediaTek PWM controller Required properties: - compatible: should be "mediatek,-pwm": + - "mediatek,mt2712-pwm": found on mt2712 SoC. + - "mediatek,mt7622-pwm": found on mt7622 SoC. - "mediatek,mt7623-pwm": found on mt7623 SoC. - reg: physical base address and length of the controller's registers. - #pwm-cells: must be 2. See pwm.txt in this directory for a description of @@ -10,7 +12,9 @@ Required properties: - clock-names: must contain the following: - "top": the top clock generator - "main": clock used by the PWM core - - "pwm1-5": the five per PWM clocks + - "pwm1-8": the eight per PWM clocks for mt2712 + - "pwm1-6": the six per PWM clocks for mt7622 + - "pwm1-5": the five per PWM clocks for mt7623 - pinctrl-names: Must contain a "default" entry. - pinctrl-0: One property must exist for each entry in pinctrl-names. See pinctrl/pinctrl-bindings.txt for details of the property values. From 8bdb65dc8575978214785462870852a56b6a21ac Mon Sep 17 00:00:00 2001 From: Zhi Mao Date: Fri, 30 Jun 2017 14:05:20 +0800 Subject: [PATCH 30/32] pwm: mediatek: Disable clock on PWM configuration failure Make sure to disable the PWM clock if the PWM cannot be configured due to the clock divider exceeding the maximum value. While at it, replace the hardcoded maximum clock divider with a defined constant to improve code readability. Signed-off-by: Zhi Mao Acked-by: John Crispin Signed-off-by: Thierry Reding --- drivers/pwm/pwm-mediatek.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-mediatek.c b/drivers/pwm/pwm-mediatek.c index 637045998318..b52f3afb2ba1 100644 --- a/drivers/pwm/pwm-mediatek.c +++ b/drivers/pwm/pwm-mediatek.c @@ -30,6 +30,8 @@ #define PWMDWIDTH 0x2c #define PWMTHRES 0x30 +#define PWM_CLK_DIV_MAX 7 + enum { MTK_CLK_MAIN = 0, MTK_CLK_TOP, @@ -130,8 +132,11 @@ static int mtk_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, clkdiv++; } - if (clkdiv > 7) + if (clkdiv > PWM_CLK_DIV_MAX) { + mtk_pwm_clk_disable(chip, pwm); + dev_err(chip->dev, "period %d not supported\n", period_ns); return -EINVAL; + } mtk_pwm_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv); mtk_pwm_writel(pc, pwm->hwpwm, PWMDWIDTH, period_ns / resolution); From 23aa19a22e7fcb49ebe198517d0a15b46810243f Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 24 Apr 2017 12:01:07 +0200 Subject: [PATCH 31/32] pwm: samsung: Remove redundant checks from pwm_samsung_config() If the requested period_ns and duty_ns values are identical to the last programmed ones pwm_samsung_config() returns early and skips the hardware configuration. The same checks are now done by the PWM core so the driver specific ones can be removed. Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Thierry Reding --- drivers/pwm/pwm-samsung.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c index f113cda47032..9ea7638228c8 100644 --- a/drivers/pwm/pwm-samsung.c +++ b/drivers/pwm/pwm-samsung.c @@ -312,9 +312,6 @@ static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, if (period_ns > NSEC_PER_SEC) return -ERANGE; - if (period_ns == chan->period_ns && duty_ns == chan->duty_ns) - return 0; - tcnt = readl(our_chip->base + REG_TCNTB(pwm->hwpwm)); oldtcmp = readl(our_chip->base + REG_TCMPB(pwm->hwpwm)); From 08a4d8ec4262c636010eaac99b5e5ea07f579643 Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 24 Apr 2017 12:01:08 +0200 Subject: [PATCH 32/32] pwm: pwm-samsung: fix suspend/resume support Fix suspend/resume support: - add disabled_mask to struct samsung_pwm_chip to track PWM disabled state information in pwm_samsung_{disable,enable}() - rename pwm_samsung_config() to __pwm_samsung_config() and add extra force_period parameter to be used during resume (to force tin_ns and tcnt recalculation) - add pwm_samsung_config() wrapper for preserving old behavior - properly restore PWM configuration in pwm_samsung_resume() - remove no longer needed pwm_samsung_suspend() - update Copyrights Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Thierry Reding --- drivers/pwm/pwm-samsung.c | 67 ++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c index 9ea7638228c8..062f2cfc45ec 100644 --- a/drivers/pwm/pwm-samsung.c +++ b/drivers/pwm/pwm-samsung.c @@ -3,6 +3,7 @@ * Copyright (c) 2008 Simtec Electronics * Ben Dooks , * Copyright (c) 2013 Tomasz Figa + * Copyright (c) 2017 Samsung Electronics Co., Ltd. * * PWM driver for Samsung SoCs * @@ -74,6 +75,7 @@ struct samsung_pwm_channel { * @chip: generic PWM chip * @variant: local copy of hardware variant data * @inverter_mask: inverter status for all channels - one bit per channel + * @disabled_mask: disabled status for all channels - one bit per channel * @base: base address of mapped PWM registers * @base_clk: base clock used to drive the timers * @tclk0: external clock 0 (can be ERR_PTR if not present) @@ -83,6 +85,7 @@ struct samsung_pwm_chip { struct pwm_chip chip; struct samsung_pwm_variant variant; u8 inverter_mask; + u8 disabled_mask; void __iomem *base; struct clk *base_clk; @@ -257,6 +260,8 @@ static int pwm_samsung_enable(struct pwm_chip *chip, struct pwm_device *pwm) tcon |= TCON_START(tcon_chan) | TCON_AUTORELOAD(tcon_chan); writel(tcon, our_chip->base + REG_TCON); + our_chip->disabled_mask &= ~BIT(pwm->hwpwm); + spin_unlock_irqrestore(&samsung_pwm_lock, flags); return 0; @@ -275,6 +280,8 @@ static void pwm_samsung_disable(struct pwm_chip *chip, struct pwm_device *pwm) tcon &= ~TCON_AUTORELOAD(tcon_chan); writel(tcon, our_chip->base + REG_TCON); + our_chip->disabled_mask |= BIT(pwm->hwpwm); + spin_unlock_irqrestore(&samsung_pwm_lock, flags); } @@ -297,8 +304,8 @@ static void pwm_samsung_manual_update(struct samsung_pwm_chip *chip, spin_unlock_irqrestore(&samsung_pwm_lock, flags); } -static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) +static int __pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns, bool force_period) { struct samsung_pwm_chip *our_chip = to_samsung_pwm_chip(chip); struct samsung_pwm_channel *chan = pwm_get_chip_data(pwm); @@ -319,7 +326,7 @@ static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, ++tcnt; /* Check to see if we are changing the clock rate of the PWM. */ - if (chan->period_ns != period_ns) { + if (chan->period_ns != period_ns || force_period) { unsigned long tin_rate; u32 period; @@ -378,6 +385,12 @@ static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, return 0; } +static int pwm_samsung_config(struct pwm_chip *chip, struct pwm_device *pwm, + int duty_ns, int period_ns) +{ + return __pwm_samsung_config(chip, pwm, duty_ns, period_ns, false); +} + static void pwm_samsung_set_invert(struct samsung_pwm_chip *chip, unsigned int channel, bool invert) { @@ -589,51 +602,41 @@ static int pwm_samsung_remove(struct platform_device *pdev) } #ifdef CONFIG_PM_SLEEP -static int pwm_samsung_suspend(struct device *dev) +static int pwm_samsung_resume(struct device *dev) { - struct samsung_pwm_chip *chip = dev_get_drvdata(dev); + struct samsung_pwm_chip *our_chip = dev_get_drvdata(dev); + struct pwm_chip *chip = &our_chip->chip; unsigned int i; - /* - * No one preserves these values during suspend so reset them. - * Otherwise driver leaves PWM unconfigured if same values are - * passed to pwm_config() next time. - */ - for (i = 0; i < SAMSUNG_PWM_NUM; ++i) { - struct pwm_device *pwm = &chip->chip.pwms[i]; + for (i = 0; i < SAMSUNG_PWM_NUM; i++) { + struct pwm_device *pwm = &chip->pwms[i]; struct samsung_pwm_channel *chan = pwm_get_chip_data(pwm); if (!chan) continue; - chan->period_ns = 0; - chan->duty_ns = 0; - } + if (our_chip->variant.output_mask & BIT(i)) + pwm_samsung_set_invert(our_chip, i, + our_chip->inverter_mask & BIT(i)); - return 0; -} + if (chan->period_ns) { + __pwm_samsung_config(chip, pwm, chan->duty_ns, + chan->period_ns, true); + /* needed to make PWM disable work on Odroid-XU3 */ + pwm_samsung_manual_update(our_chip, pwm); + } -static int pwm_samsung_resume(struct device *dev) -{ - struct samsung_pwm_chip *chip = dev_get_drvdata(dev); - unsigned int chan; - - /* - * Inverter setting must be preserved across suspend/resume - * as nobody really seems to configure it more than once. - */ - for (chan = 0; chan < SAMSUNG_PWM_NUM; ++chan) { - if (chip->variant.output_mask & BIT(chan)) - pwm_samsung_set_invert(chip, chan, - chip->inverter_mask & BIT(chan)); + if (our_chip->disabled_mask & BIT(i)) + pwm_samsung_disable(chip, pwm); + else + pwm_samsung_enable(chip, pwm); } return 0; } #endif -static SIMPLE_DEV_PM_OPS(pwm_samsung_pm_ops, pwm_samsung_suspend, - pwm_samsung_resume); +static SIMPLE_DEV_PM_OPS(pwm_samsung_pm_ops, NULL, pwm_samsung_resume); static struct platform_driver pwm_samsung_driver = { .driver = {