From 5df5a577a6b4e92c74931e19c2b1f5820c834171 Mon Sep 17 00:00:00 2001 From: Dafna Hirschfeld Date: Mon, 27 Jan 2020 11:34:41 +0100 Subject: dt-bindings: pwm: Convert google,cros-ec-pwm.txt to YAML format Convert the binding file google,cros-ec-pwm.txt to YAML format. This was tested and verified on ARM64 with: make dt_binding_check DT_SCHEMA_FILES=Documentation/devicetree/bindings/pwm/google,cros-ec-pwm.yaml make dtbs_check DT_SCHEMA_FILES=Documentation/devicetree/bindings/pwm/google,cros-ec-pwm.yaml Signed-off-by: Dafna Hirschfeld Reviewed-by: Rob Herring Signed-off-by: Thierry Reding --- .../devicetree/bindings/pwm/google,cros-ec-pwm.txt | 23 ------------- .../bindings/pwm/google,cros-ec-pwm.yaml | 40 ++++++++++++++++++++++ 2 files changed, 40 insertions(+), 23 deletions(-) delete mode 100644 Documentation/devicetree/bindings/pwm/google,cros-ec-pwm.txt create mode 100644 Documentation/devicetree/bindings/pwm/google,cros-ec-pwm.yaml diff --git a/Documentation/devicetree/bindings/pwm/google,cros-ec-pwm.txt b/Documentation/devicetree/bindings/pwm/google,cros-ec-pwm.txt deleted file mode 100644 index 472bd46ab5a4..000000000000 --- a/Documentation/devicetree/bindings/pwm/google,cros-ec-pwm.txt +++ /dev/null @@ -1,23 +0,0 @@ -* PWM controlled by ChromeOS EC - -Google's ChromeOS EC PWM is a simple PWM attached to the Embedded Controller -(EC) and controlled via a host-command interface. - -An EC PWM node should be only found as a sub-node of the EC node (see -Documentation/devicetree/bindings/mfd/cros-ec.txt). - -Required properties: -- compatible: Must contain "google,cros-ec-pwm" -- #pwm-cells: Should be 1. The cell specifies the PWM index. - -Example: - cros-ec@0 { - compatible = "google,cros-ec-spi"; - - ... - - cros_ec_pwm: ec-pwm { - compatible = "google,cros-ec-pwm"; - #pwm-cells = <1>; - }; - }; diff --git a/Documentation/devicetree/bindings/pwm/google,cros-ec-pwm.yaml b/Documentation/devicetree/bindings/pwm/google,cros-ec-pwm.yaml new file mode 100644 index 000000000000..24c217b76580 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/google,cros-ec-pwm.yaml @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/google,cros-ec-pwm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: PWM controlled by ChromeOS EC + +maintainers: + - Thierry Reding + - '"Uwe Kleine-König" ' + +description: | + Google's ChromeOS EC PWM is a simple PWM attached to the Embedded Controller + (EC) and controlled via a host-command interface. + An EC PWM node should be only found as a sub-node of the EC node (see + Documentation/devicetree/bindings/mfd/cros-ec.txt). + +properties: + compatible: + const: google,cros-ec-pwm + "#pwm-cells": + description: The cell specifies the PWM index. + const: 1 + +required: + - compatible + - '#pwm-cells' + +additionalProperties: false + +examples: + - | + cros-ec@0 { + compatible = "google,cros-ec-spi"; + cros_ec_pwm: ec-pwm { + compatible = "google,cros-ec-pwm"; + #pwm-cells = <1>; + }; + }; -- cgit v1.2.3 From 2c25b07e5ec119cab609e41407a1fb3fa61442f5 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 3 Feb 2020 13:35:35 -0800 Subject: pwm: bcm2835: Dynamically allocate base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The newer 2711 and 7211 chips have two PWM controllers and failure to dynamically allocate the PWM base would prevent the second PWM controller instance being probed for succeeding with an -EEXIST error from alloc_pwms(). Fixes: e5a06dc5ac1f ("pwm: Add BCM2835 PWM driver") Signed-off-by: Florian Fainelli Acked-by: Uwe Kleine-König Reviewed-by: Nicolas Saenz Julienne Signed-off-by: Thierry Reding --- drivers/pwm/pwm-bcm2835.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pwm/pwm-bcm2835.c b/drivers/pwm/pwm-bcm2835.c index 91e24f01b54e..d78f86f8e462 100644 --- a/drivers/pwm/pwm-bcm2835.c +++ b/drivers/pwm/pwm-bcm2835.c @@ -166,6 +166,7 @@ static int bcm2835_pwm_probe(struct platform_device *pdev) pc->chip.dev = &pdev->dev; pc->chip.ops = &bcm2835_pwm_ops; + pc->chip.base = -1; pc->chip.npwm = 2; pc->chip.of_xlate = of_pwm_xlate_with_flags; pc->chip.of_pwm_n_cells = 3; -- cgit v1.2.3 From 15d4dbd601591858611184f9ddeb5bf21569159c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Sun, 9 Feb 2020 22:31:06 +0100 Subject: pwm: imx27: Fix clock handling in pwm_imx27_apply() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pwm_imx27_apply() enables the clocks if the previous PWM state was disabled. Given that the clocks are supposed to be left on iff the PWM is running, the decision to disable the clocks at the end of the function must not depend on the previous state. Without this fix the enable count of the two affected clocks increases by one whenever ->apply() changes from one disabled state to another. Fixes: bd88d319abe9 ("pwm: imx27: Unconditionally write state to hardware") Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx27.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-imx27.c b/drivers/pwm/pwm-imx27.c index 35a7ac42269c..7e5ed0152977 100644 --- a/drivers/pwm/pwm-imx27.c +++ b/drivers/pwm/pwm-imx27.c @@ -289,7 +289,7 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm, writel(cr, imx->mmio_base + MX3_PWMCR); - if (!state->enabled && cstate.enabled) + if (!state->enabled) pwm_imx27_clk_disable_unprepare(chip); return 0; -- cgit v1.2.3 From aad4e530c241cbe862aff445f13db9099c9980eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 10 Feb 2020 22:22:38 +0100 Subject: pwm: imx27: Simplify helper function to enable and disable clocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pwm_imx27_clk_prepare_enable() took a pointer to a struct pwm_chip just to convert it to a struct pwm_imx27_chip pointer while all callers already have the latter. Ditto for pwm_imx27_clk_disable_unprepare(). Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx27.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/pwm/pwm-imx27.c b/drivers/pwm/pwm-imx27.c index 7e5ed0152977..e04ae566bbf9 100644 --- a/drivers/pwm/pwm-imx27.c +++ b/drivers/pwm/pwm-imx27.c @@ -96,9 +96,8 @@ struct pwm_imx27_chip { #define to_pwm_imx27_chip(chip) container_of(chip, struct pwm_imx27_chip, chip) -static int pwm_imx27_clk_prepare_enable(struct pwm_chip *chip) +static int pwm_imx27_clk_prepare_enable(struct pwm_imx27_chip *imx) { - struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); int ret; ret = clk_prepare_enable(imx->clk_ipg); @@ -114,10 +113,8 @@ static int pwm_imx27_clk_prepare_enable(struct pwm_chip *chip) return 0; } -static void pwm_imx27_clk_disable_unprepare(struct pwm_chip *chip) +static void pwm_imx27_clk_disable_unprepare(struct pwm_imx27_chip *imx) { - struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); - clk_disable_unprepare(imx->clk_per); clk_disable_unprepare(imx->clk_ipg); } @@ -130,7 +127,7 @@ static void pwm_imx27_get_state(struct pwm_chip *chip, u64 tmp; int ret; - ret = pwm_imx27_clk_prepare_enable(chip); + ret = pwm_imx27_clk_prepare_enable(imx); if (ret < 0) return; @@ -175,7 +172,7 @@ static void pwm_imx27_get_state(struct pwm_chip *chip, state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, pwm_clk); if (!state->enabled) - pwm_imx27_clk_disable_unprepare(chip); + pwm_imx27_clk_disable_unprepare(imx); } static void pwm_imx27_sw_reset(struct pwm_chip *chip) @@ -259,7 +256,7 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm, if (cstate.enabled) { pwm_imx27_wait_fifo_slot(chip, pwm); } else { - ret = pwm_imx27_clk_prepare_enable(chip); + ret = pwm_imx27_clk_prepare_enable(imx); if (ret) return ret; @@ -290,7 +287,7 @@ static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm, writel(cr, imx->mmio_base + MX3_PWMCR); if (!state->enabled) - pwm_imx27_clk_disable_unprepare(chip); + pwm_imx27_clk_disable_unprepare(imx); return 0; } @@ -361,7 +358,7 @@ static int pwm_imx27_remove(struct platform_device *pdev) imx = platform_get_drvdata(pdev); - pwm_imx27_clk_disable_unprepare(&imx->chip); + pwm_imx27_clk_disable_unprepare(imx); return pwmchip_remove(&imx->chip); } -- cgit v1.2.3 From 4563654fddc05a572f889373ba22abc616b3aa1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 10 Feb 2020 22:22:39 +0100 Subject: pwm: imx27: 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 is not supposed to modify hardware state. This is in the responsibility of the PWM consumer. After the PWM was disabled the clocks are off (apart from a bug that is fixed in the next patch), so unbinding the driver either stops the PWM (which it should not) or disables already disabled clocks yielding warnings from the clk core. 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-imx27.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/pwm/pwm-imx27.c b/drivers/pwm/pwm-imx27.c index e04ae566bbf9..fb142813d455 100644 --- a/drivers/pwm/pwm-imx27.c +++ b/drivers/pwm/pwm-imx27.c @@ -358,8 +358,6 @@ static int pwm_imx27_remove(struct platform_device *pdev) imx = platform_get_drvdata(pdev); - pwm_imx27_clk_disable_unprepare(imx); - return pwmchip_remove(&imx->chip); } -- cgit v1.2.3 From 2cb5cd90f4cd3f819178bb47f3d1ef86ce612db7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 10 Feb 2020 22:22:40 +0100 Subject: pwm: imx27: Ensure clocks being on iff the PWM is on MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Up to now the .probe() function didn't enable clocks and relied on the core to call the .get_state() callback to have the clock running. The latter enabled the needed clocks and kept them running if the PWM wass enabled. This only works correctly if the .get_state() callback is called exactly once and this single call happens before unused clocks are disabled by the clk core. The former wasn't true for a short period while commit 01ccf903edd6 ("pwm: Let pwm_get_state() return the last implemented state") applied and not reverted yet and might become wrong in the future. The latter isn't true any more since commit cfc4c189bc70 ("pwm: Read initial hardware state at request time") which results in a running PWM being stopped at boot time if for example the consumer lives in a kernel module that is only loaded after the clk core disabled unused clocks. So ensure .probe() is left with the clocks on if the PWM is running and .get_state() disables everything it enabled. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx27.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/pwm/pwm-imx27.c b/drivers/pwm/pwm-imx27.c index fb142813d455..e83c077bb7cc 100644 --- a/drivers/pwm/pwm-imx27.c +++ b/drivers/pwm/pwm-imx27.c @@ -171,8 +171,7 @@ static void pwm_imx27_get_state(struct pwm_chip *chip, tmp = NSEC_PER_SEC * (u64)(val); state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, pwm_clk); - if (!state->enabled) - pwm_imx27_clk_disable_unprepare(imx); + pwm_imx27_clk_disable_unprepare(imx); } static void pwm_imx27_sw_reset(struct pwm_chip *chip) @@ -307,6 +306,8 @@ MODULE_DEVICE_TABLE(of, pwm_imx27_dt_ids); static int pwm_imx27_probe(struct platform_device *pdev) { struct pwm_imx27_chip *imx; + int ret; + u32 pwmcr; imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL); if (imx == NULL) @@ -349,6 +350,15 @@ static int pwm_imx27_probe(struct platform_device *pdev) if (IS_ERR(imx->mmio_base)) return PTR_ERR(imx->mmio_base); + ret = pwm_imx27_clk_prepare_enable(imx); + if (ret) + return ret; + + /* keep clks on if pwm is running */ + pwmcr = readl(imx->mmio_base + MX3_PWMCR); + if (!(pwmcr & MX3_PWMCR_EN)) + pwm_imx27_clk_disable_unprepare(imx); + return pwmchip_add(&imx->chip); } -- cgit v1.2.3 From 3ad1f3a33286dc67d595f6fab3a3a9e583bc738a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 10 Feb 2020 22:35:18 +0100 Subject: pwm: Implement some checks for lowlevel drivers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are some expectations which the callbacks provided by lowlevel drivers should fulfill. Implement checks that help driver authors to get these semantics right. As these have some overhead the checks can be disabled using a Kconfig setting. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/Kconfig | 9 ++++ drivers/pwm/core.c | 135 +++++++++++++++++++++++++++++++++++++++++++++++++--- include/linux/pwm.h | 4 +- 3 files changed, 140 insertions(+), 8 deletions(-) diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 30190beeb6e9..e21834f44a29 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -33,6 +33,15 @@ config PWM_SYSFS bool default y if SYSFS +config PWM_DEBUG + bool "PWM lowlevel drivers additional checks and debug messages" + depends on DEBUG_KERNEL + help + This option enables some additional checks to help lowlevel driver + authors to get their callbacks implemented correctly. + It is expected to introduce some runtime overhead and diagnostic + output to the kernel log, so only enable while working on a driver. + config PWM_AB8500 tristate "AB8500 PWM support" depends on AB8500_CORE && ARCH_U8500 diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 5a7f6598c05f..e9b9283cff28 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -120,6 +120,9 @@ static int pwm_device_request(struct pwm_device *pwm, const char *label) if (pwm->chip->ops->get_state) { pwm->chip->ops->get_state(pwm->chip, pwm, &pwm->state); trace_pwm_get(pwm, &pwm->state); + + if (IS_ENABLED(PWM_DEBUG)) + pwm->last = pwm->state; } set_bit(PWMF_REQUESTED, &pwm->flags); @@ -232,17 +235,28 @@ void *pwm_get_chip_data(struct pwm_device *pwm) } EXPORT_SYMBOL_GPL(pwm_get_chip_data); -static bool pwm_ops_check(const struct pwm_ops *ops) +static bool pwm_ops_check(const struct pwm_chip *chip) { + + const struct pwm_ops *ops = chip->ops; + /* driver supports legacy, non-atomic operation */ - if (ops->config && ops->enable && ops->disable) - return true; + if (ops->config && ops->enable && ops->disable) { + if (IS_ENABLED(CONFIG_PWM_DEBUG)) + dev_warn(chip->dev, + "Driver needs updating to atomic API\n"); - /* driver supports atomic operation */ - if (ops->apply) return true; + } - return false; + if (!ops->apply) + return false; + + if (IS_ENABLED(CONFIG_PWM_DEBUG) && !ops->get_state) + dev_warn(chip->dev, + "Please implement the .get_state() callback\n"); + + return true; } /** @@ -266,7 +280,7 @@ int pwmchip_add_with_polarity(struct pwm_chip *chip, if (!chip || !chip->dev || !chip->ops || !chip->npwm) return -EINVAL; - if (!pwm_ops_check(chip->ops)) + if (!pwm_ops_check(chip)) return -EINVAL; mutex_lock(&pwm_lock); @@ -450,6 +464,107 @@ void pwm_free(struct pwm_device *pwm) } EXPORT_SYMBOL_GPL(pwm_free); +void pwm_apply_state_debug(struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct pwm_state *last = &pwm->last; + struct pwm_chip *chip = pwm->chip; + struct pwm_state s1, s2; + int err; + + if (!IS_ENABLED(CONFIG_PWM_DEBUG)) + return; + + /* No reasonable diagnosis possible without .get_state() */ + if (!chip->ops->get_state) + return; + + /* + * *state was just applied. Read out the hardware state and do some + * checks. + */ + + chip->ops->get_state(chip, pwm, &s1); + trace_pwm_get(pwm, &s1); + + /* + * The lowlevel driver either ignored .polarity (which is a bug) or as + * best effort inverted .polarity and fixed .duty_cycle respectively. + * Undo this inversion and fixup for further tests. + */ + if (s1.enabled && s1.polarity != state->polarity) { + s2.polarity = state->polarity; + s2.duty_cycle = s1.period - s1.duty_cycle; + s2.period = s1.period; + s2.enabled = s1.enabled; + } else { + s2 = s1; + } + + if (s2.polarity != state->polarity && + state->duty_cycle < state->period) + dev_warn(chip->dev, ".apply ignored .polarity\n"); + + if (state->enabled && + last->polarity == state->polarity && + last->period > s2.period && + last->period <= state->period) + dev_warn(chip->dev, + ".apply didn't pick the best available period (requested: %u, applied: %u, possible: %u)\n", + state->period, s2.period, last->period); + + if (state->enabled && state->period < s2.period) + dev_warn(chip->dev, + ".apply is supposed to round down period (requested: %u, applied: %u)\n", + state->period, s2.period); + + if (state->enabled && + last->polarity == state->polarity && + last->period == s2.period && + last->duty_cycle > s2.duty_cycle && + last->duty_cycle <= state->duty_cycle) + dev_warn(chip->dev, + ".apply didn't pick the best available duty cycle (requested: %u/%u, applied: %u/%u, possible: %u/%u)\n", + state->duty_cycle, state->period, + s2.duty_cycle, s2.period, + last->duty_cycle, last->period); + + if (state->enabled && state->duty_cycle < s2.duty_cycle) + dev_warn(chip->dev, + ".apply is supposed to round down duty_cycle (requested: %u/%u, applied: %u/%u)\n", + state->duty_cycle, state->period, + s2.duty_cycle, s2.period); + + if (!state->enabled && s2.enabled && s2.duty_cycle > 0) + dev_warn(chip->dev, + "requested disabled, but yielded enabled with duty > 0"); + + /* reapply the state that the driver reported being configured. */ + err = chip->ops->apply(chip, pwm, &s1); + if (err) { + *last = s1; + dev_err(chip->dev, "failed to reapply current setting\n"); + return; + } + + trace_pwm_apply(pwm, &s1); + + chip->ops->get_state(chip, pwm, last); + trace_pwm_get(pwm, last); + + /* reapplication of the current state should give an exact match */ + if (s1.enabled != last->enabled || + s1.polarity != last->polarity || + (s1.enabled && s1.period != last->period) || + (s1.enabled && s1.duty_cycle != last->duty_cycle)) { + dev_err(chip->dev, + ".apply is not idempotent (ena=%d pol=%d %u/%u) -> (ena=%d pol=%d %u/%u)\n", + s1.enabled, s1.polarity, s1.duty_cycle, s1.period, + last->enabled, last->polarity, last->duty_cycle, + last->period); + } +} + /** * pwm_apply_state() - atomically apply a new state to a PWM device * @pwm: PWM device @@ -480,6 +595,12 @@ int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state) trace_pwm_apply(pwm, state); pwm->state = *state; + + /* + * only do this after pwm->state was applied as some + * implementations of .get_state depend on this + */ + pwm_apply_state_debug(pwm, state); } else { /* * FIXME: restore the initial state in case of error. diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 0ef808d925bb..2635b2a55090 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -71,7 +71,8 @@ struct pwm_state { * @chip: PWM chip providing this PWM device * @chip_data: chip-private data associated with the PWM device * @args: PWM arguments - * @state: curent PWM channel state + * @state: last applied state + * @last: last implemented state (for PWM_DEBUG) */ struct pwm_device { const char *label; @@ -83,6 +84,7 @@ struct pwm_device { struct pwm_args args; struct pwm_state state; + struct pwm_state last; }; /** -- cgit v1.2.3 From c0adbd1cdfcdf78f48b1d7797c775b9d36d665a9 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Fri, 21 Feb 2020 15:45:27 +0800 Subject: pwm: imx-tpm: Remove unused includes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is nothing in use from log2.h/of_address.h, remove them. Signed-off-by: Anson Huang Acked-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx-tpm.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/pwm/pwm-imx-tpm.c b/drivers/pwm/pwm-imx-tpm.c index 9145f6160649..5f3d7f7e6aef 100644 --- a/drivers/pwm/pwm-imx-tpm.c +++ b/drivers/pwm/pwm-imx-tpm.c @@ -18,10 +18,8 @@ #include #include #include -#include #include #include -#include #include #include #include -- cgit v1.2.3 From cf7987320a1aba758f2f3e9f71e1bc23e9558e83 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Fri, 21 Feb 2020 15:45:28 +0800 Subject: pwm: imx27: Remove unused include of of_device.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is nothing in use from of_device.h, remove it. Signed-off-by: Anson Huang Acked-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx27.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pwm/pwm-imx27.c b/drivers/pwm/pwm-imx27.c index e83c077bb7cc..a6e40d4c485f 100644 --- a/drivers/pwm/pwm-imx27.c +++ b/drivers/pwm/pwm-imx27.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From cef6df8bcdda10a0b2984030f161dcd01e2e934c Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Fri, 21 Feb 2020 15:45:29 +0800 Subject: pwm: mxs: Remove unused include of of_address.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is nothing in use from of_address.h, remove it. Signed-off-by: Anson Huang Acked-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-mxs.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pwm/pwm-mxs.c b/drivers/pwm/pwm-mxs.c index f2e57fcf8f8b..7ce616923c52 100644 --- a/drivers/pwm/pwm-mxs.c +++ b/drivers/pwm/pwm-mxs.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From 69ee15f1b7a506b0484d58f2ee6add598fbca29f Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Wed, 26 Feb 2020 14:52:26 +0100 Subject: pwm: pca9685: Remove unused duty_cycle struct element duty_cycle was only set, never read. Signed-off-by: Matthias Schiffer Signed-off-by: Thierry Reding --- drivers/pwm/pwm-pca9685.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c index b07bdca3d510..19ac97108a64 100644 --- a/drivers/pwm/pwm-pca9685.c +++ b/drivers/pwm/pwm-pca9685.c @@ -69,7 +69,6 @@ struct pca9685 { struct pwm_chip chip; struct regmap *regmap; - int duty_ns; int period_ns; #if IS_ENABLED(CONFIG_GPIOLIB) struct mutex lock; @@ -272,8 +271,6 @@ static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, } } - pca->duty_ns = duty_ns; - if (duty_ns < 1) { if (pwm->hwpwm >= PCA9685_MAXCHAN) reg = PCA9685_ALL_LED_OFF_H; @@ -449,7 +446,6 @@ static int pca9685_pwm_probe(struct i2c_client *client, ret); return ret; } - pca->duty_ns = 0; pca->period_ns = PCA9685_DEFAULT_PERIOD; i2c_set_clientdata(client, pca); -- cgit v1.2.3 From e96c0ff4b1e013a4e9174344b0fcda0d566d3689 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 3 Mar 2020 21:24:47 +0100 Subject: pwm: Enable compile testing for some of drivers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some of the PWM drivers can be compile tested to increase build coverage. The Meson PWM driver requires COMMON_CLK dependency. Signed-off-by: Krzysztof Kozlowski Acked-by: Florian Fainelli # For Broadcoam Reviewed-by: Martin Blumenstingl # For Meson Acked-by: Claudiu Beznea # For Atmel Acked-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/Kconfig | 47 +++++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index e21834f44a29..0e64c62d4b6b 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -53,7 +53,8 @@ config PWM_AB8500 config PWM_ATMEL tristate "Atmel PWM support" - depends on ARCH_AT91 && OF + depends on OF + depends on ARCH_AT91 || COMPILE_TEST help Generic PWM framework driver for Atmel SoC. @@ -109,7 +110,7 @@ config PWM_BCM_KONA config PWM_BCM2835 tristate "BCM2835 PWM support" - depends on ARCH_BCM2835 || ARCH_BRCMSTB + depends on ARCH_BCM2835 || ARCH_BRCMSTB || COMPILE_TEST help PWM framework driver for BCM2835 controller (Raspberry Pi) @@ -118,7 +119,7 @@ config PWM_BCM2835 config PWM_BERLIN tristate "Marvell Berlin PWM support" - depends on ARCH_BERLIN + depends on ARCH_BERLIN || COMPILE_TEST help PWM framework driver for Marvell Berlin SoCs. @@ -127,7 +128,7 @@ config PWM_BERLIN config PWM_BRCMSTB tristate "Broadcom STB PWM support" - depends on ARCH_BRCMSTB || BMIPS_GENERIC + depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST help Generic PWM framework driver for the Broadcom Set-top-Box SoCs (BCM7xxx). @@ -161,7 +162,7 @@ config PWM_CROS_EC config PWM_EP93XX tristate "Cirrus Logic EP93xx PWM support" - depends on ARCH_EP93XX + depends on ARCH_EP93XX || COMPILE_TEST help Generic PWM framework driver for Cirrus Logic EP93xx. @@ -204,7 +205,7 @@ config PWM_IMG config PWM_IMX1 tristate "i.MX1 PWM support" - depends on ARCH_MXC + depends on ARCH_MXC || COMPILE_TEST help Generic PWM framework driver for i.MX1 and i.MX21 @@ -213,7 +214,7 @@ config PWM_IMX1 config PWM_IMX27 tristate "i.MX27 PWM support" - depends on ARCH_MXC + depends on ARCH_MXC || COMPILE_TEST help Generic PWM framework driver for i.MX27 and later i.MX SoCs. @@ -253,7 +254,7 @@ config PWM_LP3943 config PWM_LPC18XX_SCT tristate "LPC18xx/43xx PWM/SCT support" - depends on ARCH_LPC18XX + depends on ARCH_LPC18XX || COMPILE_TEST help Generic PWM framework driver for NXP LPC18xx PWM/SCT which supports 16 channels. @@ -265,7 +266,7 @@ config PWM_LPC18XX_SCT config PWM_LPC32XX tristate "LPC32XX PWM support" - depends on ARCH_LPC32XX + depends on ARCH_LPC32XX || COMPILE_TEST help Generic PWM framework driver for LPC32XX. The LPC32XX SOC has two PWM controllers. @@ -298,7 +299,8 @@ config PWM_LPSS_PLATFORM config PWM_MESON tristate "Amlogic Meson PWM driver" - depends on ARCH_MESON + depends on ARCH_MESON || COMPILE_TEST + depends on COMMON_CLK help The platform driver for Amlogic Meson PWM controller. @@ -327,7 +329,8 @@ config PWM_MEDIATEK config PWM_MXS tristate "Freescale MXS PWM support" - depends on ARCH_MXS && OF + depends on OF + depends on ARCH_MXS || COMPILE_TEST select STMP_DEVICE help Generic PWM framework driver for Freescale MXS. @@ -366,7 +369,7 @@ config PWM_PUV3 config PWM_PXA tristate "PXA PWM support" - depends on ARCH_PXA + depends on ARCH_PXA || COMPILE_TEST help Generic PWM framework driver for PXA. @@ -397,14 +400,14 @@ config PWM_RENESAS_TPU config PWM_ROCKCHIP tristate "Rockchip PWM support" - depends on ARCH_ROCKCHIP + depends on ARCH_ROCKCHIP || COMPILE_TEST help Generic PWM framework driver for the PWM controller found on Rockchip SoCs. config PWM_SAMSUNG tristate "Samsung PWM support" - depends on PLAT_SAMSUNG || ARCH_EXYNOS + depends on PLAT_SAMSUNG || ARCH_EXYNOS || COMPILE_TEST help Generic PWM framework driver for Samsung. @@ -424,7 +427,7 @@ config PWM_SIFIVE config PWM_SPEAR tristate "STMicroelectronics SPEAr PWM support" - depends on PLAT_SPEAR + depends on PLAT_SPEAR || COMPILE_TEST depends on OF help Generic PWM framework driver for the PWM controller on ST @@ -446,7 +449,7 @@ config PWM_SPRD config PWM_STI tristate "STiH4xx PWM support" - depends on ARCH_STI + depends on ARCH_STI || COMPILE_TEST depends on OF help Generic PWM framework driver for STiH4xx SoCs. @@ -456,7 +459,7 @@ config PWM_STI config PWM_STM32 tristate "STMicroelectronics STM32 PWM" - depends on MFD_STM32_TIMERS + depends on MFD_STM32_TIMERS || COMPILE_TEST help Generic PWM framework driver for STM32 SoCs. @@ -492,7 +495,7 @@ config PWM_SUN4I config PWM_TEGRA tristate "NVIDIA Tegra PWM support" - depends on ARCH_TEGRA + depends on ARCH_TEGRA || COMPILE_TEST help Generic PWM framework driver for the PWFM controller found on NVIDIA Tegra SoCs. @@ -502,7 +505,7 @@ config PWM_TEGRA config PWM_TIECAP tristate "ECAP PWM support" - depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_KEYSTONE || ARCH_K3 + depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_KEYSTONE || ARCH_K3 || COMPILE_TEST help PWM driver support for the ECAP APWM controller found on TI SOCs @@ -511,7 +514,7 @@ config PWM_TIECAP config PWM_TIEHRPWM tristate "EHRPWM PWM support" - depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_K3 + depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_K3 || COMPILE_TEST help PWM driver support for the EHRPWM controller found on TI SOCs @@ -538,7 +541,7 @@ config PWM_TWL_LED config PWM_VT8500 tristate "vt8500 PWM support" - depends on ARCH_VT8500 + depends on ARCH_VT8500 || COMPILE_TEST help Generic PWM framework driver for vt8500. @@ -547,7 +550,7 @@ config PWM_VT8500 config PWM_ZX tristate "ZTE ZX PWM support" - depends on ARCH_ZX + depends on ARCH_ZX || COMPILE_TEST help Generic PWM framework driver for ZTE ZX family SoCs. -- cgit v1.2.3 From 2d0c08fcd67c23cf8433344544fb5a6c059c2572 Mon Sep 17 00:00:00 2001 From: Sandipan Patra Date: Thu, 5 Mar 2020 16:57:33 +0530 Subject: pwm: tegra: Add support for Tegra194 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tegra194 has multiple PWM controllers with each having only one output. Also the maxmimum frequency is higher than earlier SoCs. Add support for Tegra194 and specify the number of PWM outputs and maximum supported frequency using device tree match data. Signed-off-by: Sandipan Patra Acked-by: Uwe Kleine-König Acked-by: Laxman Dewangan Acked-by: Rob Herring Acked-by: Jon Hunter Signed-off-by: Thierry Reding --- Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt | 1 + drivers/pwm/pwm-tegra.c | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt b/Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt index 0a69eadf44ce..74c41e34c3b6 100644 --- a/Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt +++ b/Documentation/devicetree/bindings/pwm/nvidia,tegra20-pwm.txt @@ -9,6 +9,7 @@ Required properties: - "nvidia,tegra132-pwm", "nvidia,tegra20-pwm": for Tegra132 - "nvidia,tegra210-pwm", "nvidia,tegra20-pwm": for Tegra210 - "nvidia,tegra186-pwm": for Tegra186 + - "nvidia,tegra194-pwm": for Tegra194 - reg: physical base address and length of the controller's registers - #pwm-cells: should be 2. See pwm.yaml in this directory for a description of the cells format. diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c index aa12fb3ed92e..d26ed8f579ff 100644 --- a/drivers/pwm/pwm-tegra.c +++ b/drivers/pwm/pwm-tegra.c @@ -282,9 +282,15 @@ static const struct tegra_pwm_soc tegra186_pwm_soc = { .max_frequency = 102000000UL, }; +static const struct tegra_pwm_soc tegra194_pwm_soc = { + .num_channels = 1, + .max_frequency = 408000000UL, +}; + static const struct of_device_id tegra_pwm_of_match[] = { { .compatible = "nvidia,tegra20-pwm", .data = &tegra20_pwm_soc }, { .compatible = "nvidia,tegra186-pwm", .data = &tegra186_pwm_soc }, + { .compatible = "nvidia,tegra194-pwm", .data = &tegra194_pwm_soc }, { } }; MODULE_DEVICE_TABLE(of, tegra_pwm_of_match); -- cgit v1.2.3 From 408a7591d91a8bb935109776e889bbaa42787c09 Mon Sep 17 00:00:00 2001 From: Rishi Gupta Date: Wed, 11 Mar 2020 21:13:49 +0530 Subject: pwm: pca9685: Replace CONFIG_PM with __maybe_unused MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The __maybe_unused attribute is preferred over CONFIG_PM to prevent potential build time issues. This commit replaces CONFIG_PM with this attribute. Signed-off-by: Rishi Gupta Acked-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-pca9685.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c index 19ac97108a64..9467fb68442d 100644 --- a/drivers/pwm/pwm-pca9685.c +++ b/drivers/pwm/pwm-pca9685.c @@ -508,8 +508,7 @@ static int pca9685_pwm_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM -static int pca9685_pwm_runtime_suspend(struct device *dev) +static int __maybe_unused pca9685_pwm_runtime_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct pca9685 *pca = i2c_get_clientdata(client); @@ -518,7 +517,7 @@ static int pca9685_pwm_runtime_suspend(struct device *dev) return 0; } -static int pca9685_pwm_runtime_resume(struct device *dev) +static int __maybe_unused pca9685_pwm_runtime_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct pca9685 *pca = i2c_get_clientdata(client); @@ -526,7 +525,6 @@ static int pca9685_pwm_runtime_resume(struct device *dev) pca9685_set_sleep_mode(pca, false); return 0; } -#endif static const struct i2c_device_id pca9685_id[] = { { "pca9685", 0 }, -- cgit v1.2.3 From a37507d5bfec9224e425a055683101560514dff8 Mon Sep 17 00:00:00 2001 From: Rishi Gupta Date: Wed, 11 Mar 2020 21:22:20 +0530 Subject: pwm: pca9685: Use gpio core provided macro GPIO_LINE_DIRECTION_OUT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GPIO core recently added macro to uniformly specify direction of a GPIO line, so use it. Signed-off-by: Rishi Gupta Acked-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-pca9685.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-pca9685.c b/drivers/pwm/pwm-pca9685.c index 9467fb68442d..20bdc59a0cbb 100644 --- a/drivers/pwm/pwm-pca9685.c +++ b/drivers/pwm/pwm-pca9685.c @@ -167,7 +167,7 @@ static int pca9685_pwm_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { /* Always out */ - return 0; + return GPIO_LINE_DIRECTION_OUT; } static int pca9685_pwm_gpio_direction_input(struct gpio_chip *gpio, -- cgit v1.2.3 From b33d232e6112aa175f08e8105de0a4da14f5dcbb Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 14 Mar 2020 12:35:24 +0100 Subject: pwm: meson: Fix confusing indentation Fix indentation of return block. Smatch warning: drivers/pwm/pwm-meson.c:139 meson_pwm_request() warn: inconsistent indenting Reported-by: kbuild test robot Signed-off-by: Krzysztof Kozlowski Acked-by: Neil Armstrong Fixes: 211ed630753d ("pwm: Add support for Meson PWM Controller") 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 6245bbdb6e6c..8cf9129caa39 100644 --- a/drivers/pwm/pwm-meson.c +++ b/drivers/pwm/pwm-meson.c @@ -136,7 +136,7 @@ static int meson_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) dev_err(dev, "failed to set parent %s for %s: %d\n", __clk_get_name(channel->clk_parent), __clk_get_name(channel->clk), err); - return err; + return err; } } -- cgit v1.2.3 From 5928ce02a7d95b400f9b9a455448e228d4635198 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 16 Mar 2020 11:14:53 +0100 Subject: dt-bindings: pwm: renesas-tpu: Document more R-Car Gen2 support All R-Car Gen2 SoCs have a Renesas Timer Pulse Unit. Document support for the missing variants. Signed-off-by: Geert Uytterhoeven Reviewed-by: Laurent Pinchart Signed-off-by: Thierry Reding --- Documentation/devicetree/bindings/pwm/renesas,tpu-pwm.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/pwm/renesas,tpu-pwm.yaml b/Documentation/devicetree/bindings/pwm/renesas,tpu-pwm.yaml index 4969a954993c..4bf62a3d5bba 100644 --- a/Documentation/devicetree/bindings/pwm/renesas,tpu-pwm.yaml +++ b/Documentation/devicetree/bindings/pwm/renesas,tpu-pwm.yaml @@ -19,6 +19,10 @@ properties: - renesas,tpu-r8a7744 # RZ/G1N - renesas,tpu-r8a7745 # RZ/G1E - renesas,tpu-r8a7790 # R-Car H2 + - renesas,tpu-r8a7791 # R-Car M2-W + - renesas,tpu-r8a7792 # R-Car V2H + - renesas,tpu-r8a7793 # R-Car M2-N + - renesas,tpu-r8a7794 # R-Car E2 - renesas,tpu-r8a7795 # R-Car H3 - renesas,tpu-r8a7796 # R-Car M3-W - renesas,tpu-r8a77965 # R-Car M3-N -- cgit v1.2.3 From 1451a3eed24b5fd6a604683f0b6995e0e7e16c79 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 16 Mar 2020 11:32:14 +0100 Subject: pwm: rcar: Fix late Runtime PM enablement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Runtime PM should be enabled before calling pwmchip_add(), as PWM users can appear immediately after the PWM chip has been added. Likewise, Runtime PM should be disabled after the removal of the PWM chip. Fixes: ed6c1476bf7f16d5 ("pwm: Add support for R-Car PWM Timer") Signed-off-by: Geert Uytterhoeven Reviewed-by: Uwe Kleine-König Reviewed-by: Laurent Pinchart Signed-off-by: Thierry Reding --- drivers/pwm/pwm-rcar.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/pwm/pwm-rcar.c b/drivers/pwm/pwm-rcar.c index 2685577b6dd4..7ab9eb6616d9 100644 --- a/drivers/pwm/pwm-rcar.c +++ b/drivers/pwm/pwm-rcar.c @@ -229,24 +229,28 @@ static int rcar_pwm_probe(struct platform_device *pdev) rcar_pwm->chip.base = -1; rcar_pwm->chip.npwm = 1; + pm_runtime_enable(&pdev->dev); + ret = pwmchip_add(&rcar_pwm->chip); if (ret < 0) { dev_err(&pdev->dev, "failed to register PWM chip: %d\n", ret); + pm_runtime_disable(&pdev->dev); return ret; } - pm_runtime_enable(&pdev->dev); - return 0; } static int rcar_pwm_remove(struct platform_device *pdev) { struct rcar_pwm_chip *rcar_pwm = platform_get_drvdata(pdev); + int ret; + + ret = pwmchip_remove(&rcar_pwm->chip); pm_runtime_disable(&pdev->dev); - return pwmchip_remove(&rcar_pwm->chip); + return ret; } static const struct of_device_id rcar_pwm_of_table[] = { -- cgit v1.2.3 From d5a3c7a4536e1329a758e14340efd0e65252bd3d Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 16 Mar 2020 11:32:15 +0100 Subject: pwm: renesas-tpu: Fix late Runtime PM enablement Runtime PM should be enabled before calling pwmchip_add(), as PWM users can appear immediately after the PWM chip has been added. Likewise, Runtime PM should always be disabled after the removal of the PWM chip, even if the latter failed. Fixes: 99b82abb0a35b073 ("pwm: Add Renesas TPU PWM driver") Signed-off-by: Geert Uytterhoeven Signed-off-by: Thierry Reding --- drivers/pwm/pwm-renesas-tpu.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/pwm/pwm-renesas-tpu.c b/drivers/pwm/pwm-renesas-tpu.c index 4a855a21b782..8032acc84161 100644 --- a/drivers/pwm/pwm-renesas-tpu.c +++ b/drivers/pwm/pwm-renesas-tpu.c @@ -415,16 +415,17 @@ static int tpu_probe(struct platform_device *pdev) tpu->chip.base = -1; tpu->chip.npwm = TPU_CHANNEL_MAX; + pm_runtime_enable(&pdev->dev); + ret = pwmchip_add(&tpu->chip); if (ret < 0) { dev_err(&pdev->dev, "failed to register PWM chip\n"); + pm_runtime_disable(&pdev->dev); return ret; } dev_info(&pdev->dev, "TPU PWM %d registered\n", tpu->pdev->id); - pm_runtime_enable(&pdev->dev); - return 0; } @@ -434,12 +435,10 @@ static int tpu_remove(struct platform_device *pdev) int ret; ret = pwmchip_remove(&tpu->chip); - if (ret) - return ret; pm_runtime_disable(&pdev->dev); - return 0; + return ret; } #ifdef CONFIG_OF -- cgit v1.2.3 From a1098c13a3ecd61015f63753c80c212ceaea7fb4 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 16 Mar 2020 11:32:16 +0100 Subject: pwm: renesas-tpu: Drop confusing registered message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During device probe, the message TPU PWM -1 registered is printed. While the "-1" looks suspicious, it is perfectly normal for a device instantiated from DT. Remove the message, as there are no non-DT users left, and other drivers don't print such messages either. Signed-off-by: Geert Uytterhoeven Reviewed-by: Laurent Pinchart Acked-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-renesas-tpu.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/pwm/pwm-renesas-tpu.c b/drivers/pwm/pwm-renesas-tpu.c index 8032acc84161..81ad5a551455 100644 --- a/drivers/pwm/pwm-renesas-tpu.c +++ b/drivers/pwm/pwm-renesas-tpu.c @@ -424,8 +424,6 @@ static int tpu_probe(struct platform_device *pdev) return ret; } - dev_info(&pdev->dev, "TPU PWM %d registered\n", tpu->pdev->id); - return 0; } -- cgit v1.2.3 From 54091b5f195b45a9a7d394008c06d2b9646ab126 Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Thu, 12 Mar 2020 09:52:06 +0530 Subject: pwm: omap-dmtimer: Drop unused header file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pwm_omap_dmtimer.h is used only: - to typedef struct omap_dm_timer to pwm_omap_dmtimer - for macro PWM_OMAP_DMTIMER_TRIGGER_OVERFLOW_AND_COMPARE Rest of the file is pretty mush unsed. So reuse omap_dm_timer and OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE in pwm-omap-dmtimer.c and delete the header file. Acked-by: Tony Lindgren Signed-off-by: Lokesh Vutla Acked-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-omap-dmtimer.c | 20 +++--- include/clocksource/timer-ti-dm.h | 3 +- include/linux/platform_data/pwm_omap_dmtimer.h | 90 -------------------------- 3 files changed, 10 insertions(+), 103 deletions(-) delete mode 100644 include/linux/platform_data/pwm_omap_dmtimer.h diff --git a/drivers/pwm/pwm-omap-dmtimer.c b/drivers/pwm/pwm-omap-dmtimer.c index 9e4378dc6897..e4f5f710bfaa 100644 --- a/drivers/pwm/pwm-omap-dmtimer.c +++ b/drivers/pwm/pwm-omap-dmtimer.c @@ -20,8 +20,8 @@ #include #include #include +#include #include -#include #include #include #include @@ -34,7 +34,7 @@ struct pwm_omap_dmtimer_chip { struct pwm_chip chip; struct mutex mutex; - pwm_omap_dmtimer *dm_timer; + struct omap_dm_timer *dm_timer; const struct omap_dm_timer_ops *pdata; struct platform_device *dm_timer_pdev; }; @@ -190,10 +190,9 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip, load_value, load_value, match_value, match_value); omap->pdata->set_pwm(omap->dm_timer, - pwm_get_polarity(pwm) == PWM_POLARITY_INVERSED, - true, - PWM_OMAP_DMTIMER_TRIGGER_OVERFLOW_AND_COMPARE, - true); + pwm_get_polarity(pwm) == PWM_POLARITY_INVERSED, + true, OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE, + true); /* If config was called while timer was running it must be reenabled. */ if (timer_active) @@ -221,10 +220,9 @@ static int pwm_omap_dmtimer_set_polarity(struct pwm_chip *chip, */ mutex_lock(&omap->mutex); omap->pdata->set_pwm(omap->dm_timer, - polarity == PWM_POLARITY_INVERSED, - true, - PWM_OMAP_DMTIMER_TRIGGER_OVERFLOW_AND_COMPARE, - true); + polarity == PWM_POLARITY_INVERSED, + true, OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE, + true); mutex_unlock(&omap->mutex); return 0; @@ -246,7 +244,7 @@ static int pwm_omap_dmtimer_probe(struct platform_device *pdev) struct pwm_omap_dmtimer_chip *omap; struct dmtimer_platform_data *timer_pdata; const struct omap_dm_timer_ops *pdata; - pwm_omap_dmtimer *dm_timer; + struct omap_dm_timer *dm_timer; u32 v; int ret = 0; diff --git a/include/clocksource/timer-ti-dm.h b/include/clocksource/timer-ti-dm.h index 25f05235866e..531ca87fcd08 100644 --- a/include/clocksource/timer-ti-dm.h +++ b/include/clocksource/timer-ti-dm.h @@ -248,8 +248,7 @@ int omap_dm_timers_active(void); /* * The below are inlined to optimize code size for system timers. Other code - * should not need these at all, see - * include/linux/platform_data/pwm_omap_dmtimer.h + * should not need these at all. */ #if defined(CONFIG_ARCH_OMAP1) || defined(CONFIG_ARCH_OMAP2PLUS) static inline u32 __omap_dm_timer_read(struct omap_dm_timer *timer, u32 reg, diff --git a/include/linux/platform_data/pwm_omap_dmtimer.h b/include/linux/platform_data/pwm_omap_dmtimer.h deleted file mode 100644 index e7d521e48855..000000000000 --- a/include/linux/platform_data/pwm_omap_dmtimer.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * include/linux/platform_data/pwm_omap_dmtimer.h - * - * OMAP Dual-Mode Timer PWM platform data - * - * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ - * Tarun Kanti DebBarma - * Thara Gopinath - * - * Platform device conversion and hwmod support. - * - * Copyright (C) 2005 Nokia Corporation - * Author: Lauri Leukkunen - * PWM and clock framework support by Timo Teras. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef __PWM_OMAP_DMTIMER_PDATA_H -#define __PWM_OMAP_DMTIMER_PDATA_H - -/* clock sources */ -#define PWM_OMAP_DMTIMER_SRC_SYS_CLK 0x00 -#define PWM_OMAP_DMTIMER_SRC_32_KHZ 0x01 -#define PWM_OMAP_DMTIMER_SRC_EXT_CLK 0x02 - -/* timer interrupt enable bits */ -#define PWM_OMAP_DMTIMER_INT_CAPTURE (1 << 2) -#define PWM_OMAP_DMTIMER_INT_OVERFLOW (1 << 1) -#define PWM_OMAP_DMTIMER_INT_MATCH (1 << 0) - -/* trigger types */ -#define PWM_OMAP_DMTIMER_TRIGGER_NONE 0x00 -#define PWM_OMAP_DMTIMER_TRIGGER_OVERFLOW 0x01 -#define PWM_OMAP_DMTIMER_TRIGGER_OVERFLOW_AND_COMPARE 0x02 - -struct omap_dm_timer; -typedef struct omap_dm_timer pwm_omap_dmtimer; - -struct pwm_omap_dmtimer_pdata { - pwm_omap_dmtimer *(*request_by_node)(struct device_node *np); - pwm_omap_dmtimer *(*request_specific)(int timer_id); - pwm_omap_dmtimer *(*request)(void); - - int (*free)(pwm_omap_dmtimer *timer); - - void (*enable)(pwm_omap_dmtimer *timer); - void (*disable)(pwm_omap_dmtimer *timer); - - int (*get_irq)(pwm_omap_dmtimer *timer); - int (*set_int_enable)(pwm_omap_dmtimer *timer, unsigned int value); - int (*set_int_disable)(pwm_omap_dmtimer *timer, u32 mask); - - struct clk *(*get_fclk)(pwm_omap_dmtimer *timer); - - int (*start)(pwm_omap_dmtimer *timer); - int (*stop)(pwm_omap_dmtimer *timer); - int (*set_source)(pwm_omap_dmtimer *timer, int source); - - int (*set_load)(pwm_omap_dmtimer *timer, int autoreload, - unsigned int value); - int (*set_match)(pwm_omap_dmtimer *timer, int enable, - unsigned int match); - int (*set_pwm)(pwm_omap_dmtimer *timer, int def_on, - int toggle, int trigger); - int (*set_prescaler)(pwm_omap_dmtimer *timer, int prescaler); - - unsigned int (*read_counter)(pwm_omap_dmtimer *timer); - int (*write_counter)(pwm_omap_dmtimer *timer, unsigned int value); - unsigned int (*read_status)(pwm_omap_dmtimer *timer); - int (*write_status)(pwm_omap_dmtimer *timer, unsigned int value); -}; - -#endif /* __PWM_OMAP_DMTIMER_PDATA_H */ -- cgit v1.2.3 From 348fb6f7fb4cc7ebc35d1cde6e5c2ada64b683c6 Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Thu, 12 Mar 2020 09:52:07 +0530 Subject: pwm: omap-dmtimer: Update description for PWM OMAP DM timer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the description with a brief about how PWM is generated using OMAP DM timer and add limitations for the PWM generations. Also add a link to the reference manual. Suggested-by: Uwe Kleine-König Acked-by: Tony Lindgren Signed-off-by: Lokesh Vutla Acked-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-omap-dmtimer.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/pwm/pwm-omap-dmtimer.c b/drivers/pwm/pwm-omap-dmtimer.c index e4f5f710bfaa..92aac6c86b95 100644 --- a/drivers/pwm/pwm-omap-dmtimer.c +++ b/drivers/pwm/pwm-omap-dmtimer.c @@ -10,7 +10,15 @@ * * Description: * This file is the core OMAP support for the generic, Linux - * PWM driver / controller, using the OMAP's dual-mode timers. + * PWM driver / controller, using the OMAP's dual-mode timers + * with a timer counter that goes up. When it overflows it gets + * reloaded with the load value and the pwm output goes up. + * When counter matches with match register, the output goes down. + * Reference Manual: http://www.ti.com/lit/ug/spruh73q/spruh73q.pdf + * + * Limitations: + * - When PWM is stopped, timer counter gets stopped immediately. This + * doesn't allow the current PWM period to complete and stops abruptly. */ #include -- cgit v1.2.3 From 867beb60d131f7a5cde88ba375338285fdc6ddb8 Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Thu, 12 Mar 2020 09:52:08 +0530 Subject: pwm: omap-dmtimer: Fix PWM enabling sequence To configure DM timer in PWM mode the following needs to be set in OMAP_TIMER_CTRL_REG using set_pwm callback: - Set toggle mode on PORTIMERPWM output pin - Set trigger on overflow and match on PORTIMERPWM output pin. - Set auto reload This is a one time configuration and needs to be set before the start of the DM timer. But the current driver tries to set the same configuration for every period/duty cycle update, which is not needed. So move the PWM setup before enabling timer and do not update it in pwm_omap_dmtimer_config(). Tested-by: Tony Lindgren Signed-off-by: Lokesh Vutla Signed-off-by: Thierry Reding --- drivers/pwm/pwm-omap-dmtimer.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/pwm/pwm-omap-dmtimer.c b/drivers/pwm/pwm-omap-dmtimer.c index 92aac6c86b95..85b17b49980b 100644 --- a/drivers/pwm/pwm-omap-dmtimer.c +++ b/drivers/pwm/pwm-omap-dmtimer.c @@ -81,6 +81,11 @@ static int pwm_omap_dmtimer_enable(struct pwm_chip *chip, struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip); mutex_lock(&omap->mutex); + omap->pdata->set_pwm(omap->dm_timer, + pwm_get_polarity(pwm) == PWM_POLARITY_INVERSED, + true, OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE, + true); + pwm_omap_dmtimer_start(omap); mutex_unlock(&omap->mutex); @@ -197,11 +202,6 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip, dev_dbg(chip->dev, "load value: %#08x (%d), match value: %#08x (%d)\n", load_value, load_value, match_value, match_value); - omap->pdata->set_pwm(omap->dm_timer, - pwm_get_polarity(pwm) == PWM_POLARITY_INVERSED, - true, OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE, - true); - /* If config was called while timer was running it must be reenabled. */ if (timer_active) pwm_omap_dmtimer_start(omap); -- cgit v1.2.3 From e793eef8062f4a5b24433d0e0aac2ddf9cf71da8 Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Thu, 12 Mar 2020 09:52:09 +0530 Subject: pwm: omap-dmtimer: Do not disable PWM before changing period/duty_cycle Only the Timer control register(TCLR) cannot be updated when the timer is running. Registers like Counter register (TCRR), loader register (TLDR) and match register (TMAR) can be updated while the counter is running. Since TCLR is not updated in pwm_omap_dmtimer_config(), do not stop the timer for period/duty_cycle update. Tested-by: Tony Lindgren Signed-off-by: Lokesh Vutla Signed-off-by: Thierry Reding --- drivers/pwm/pwm-omap-dmtimer.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/drivers/pwm/pwm-omap-dmtimer.c b/drivers/pwm/pwm-omap-dmtimer.c index 85b17b49980b..c56e7256e923 100644 --- a/drivers/pwm/pwm-omap-dmtimer.c +++ b/drivers/pwm/pwm-omap-dmtimer.c @@ -19,6 +19,13 @@ * Limitations: * - When PWM is stopped, timer counter gets stopped immediately. This * doesn't allow the current PWM period to complete and stops abruptly. + * - When PWM is running and changing both duty cycle and period, + * we cannot prevent in software that the output might produce + * a period with mixed settings. Especially when period/duty_cyle + * is updated while the pwm pin is high, current pwm period/duty_cycle + * can get updated as below based on the current timer counter: + * - period for current cycle = current_period + new period + * - duty_cycle for current period = current period + new duty_cycle. */ #include @@ -111,7 +118,6 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip, u32 load_value, match_value; struct clk *fclk; unsigned long clk_rate; - bool timer_active; dev_dbg(chip->dev, "requested duty cycle: %d ns, period: %d ns\n", duty_ns, period_ns); @@ -187,25 +193,12 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip, load_value = (DM_TIMER_MAX - period_cycles) + 1; match_value = load_value + duty_cycles - 1; - /* - * We MUST stop the associated dual-mode timer before attempting to - * write its registers, but calls to omap_dm_timer_start/stop must - * be balanced so check if timer is active before calling timer_stop. - */ - timer_active = pm_runtime_active(&omap->dm_timer_pdev->dev); - if (timer_active) - omap->pdata->stop(omap->dm_timer); - omap->pdata->set_load(omap->dm_timer, load_value); omap->pdata->set_match(omap->dm_timer, true, match_value); dev_dbg(chip->dev, "load value: %#08x (%d), match value: %#08x (%d)\n", load_value, load_value, match_value, match_value); - /* If config was called while timer was running it must be reenabled. */ - if (timer_active) - pwm_omap_dmtimer_start(omap); - mutex_unlock(&omap->mutex); return 0; -- cgit v1.2.3 From 6b28fb6f3ca30bd98815041b4eb795743706402d Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Thu, 12 Mar 2020 09:52:10 +0530 Subject: pwm: omap-dmtimer: Implement .apply callback Implement .apply callback and drop the legacy callbacks(enable, disable, config, set_polarity). In .apply() check for the current hardware status before changing the PWM configuration. Signed-off-by: Lokesh Vutla Tested-by: Tony Lindgren Signed-off-by: Thierry Reding --- drivers/pwm/pwm-omap-dmtimer.c | 180 +++++++++++++++++++++++++++++------------ 1 file changed, 129 insertions(+), 51 deletions(-) diff --git a/drivers/pwm/pwm-omap-dmtimer.c b/drivers/pwm/pwm-omap-dmtimer.c index c56e7256e923..0d31833db2e2 100644 --- a/drivers/pwm/pwm-omap-dmtimer.c +++ b/drivers/pwm/pwm-omap-dmtimer.c @@ -26,6 +26,11 @@ * can get updated as below based on the current timer counter: * - period for current cycle = current_period + new period * - duty_cycle for current period = current period + new duty_cycle. + * - PWM OMAP DM timer cannot change the polarity when pwm is active. When + * user requests a change in polarity when in active state: + * - PWM is stopped abruptly(without completing the current cycle) + * - Polarity is changed + * - A fresh cycle is started. */ #include @@ -46,8 +51,18 @@ #define DM_TIMER_LOAD_MIN 0xfffffffe #define DM_TIMER_MAX 0xffffffff +/** + * struct pwm_omap_dmtimer_chip - Structure representing a pwm chip + * corresponding to omap dmtimer. + * @chip: PWM chip structure representing PWM controller + * @mutex: Mutex to protect pwm apply state + * @dm_timer: Pointer to omap dm timer. + * @pdata: Pointer to omap dm timer ops. + * dm_timer_pdev: Pointer to omap dm timer platform device + */ struct pwm_omap_dmtimer_chip { struct pwm_chip chip; + /* Mutex to protect pwm apply state */ struct mutex mutex; struct omap_dm_timer *dm_timer; const struct omap_dm_timer_ops *pdata; @@ -60,11 +75,22 @@ to_pwm_omap_dmtimer_chip(struct pwm_chip *chip) return container_of(chip, struct pwm_omap_dmtimer_chip, chip); } +/** + * pwm_omap_dmtimer_get_clock_cycles() - Get clock cycles in a time frame + * @clk_rate: pwm timer clock rate + * @ns: time frame in nano seconds. + * + * Return number of clock cycles in a given period(ins ns). + */ static u32 pwm_omap_dmtimer_get_clock_cycles(unsigned long clk_rate, int ns) { return DIV_ROUND_CLOSEST_ULL((u64)clk_rate * ns, NSEC_PER_SEC); } +/** + * pwm_omap_dmtimer_start() - Start the pwm omap dm timer in pwm mode + * @omap: Pointer to pwm omap dm timer chip + */ static void pwm_omap_dmtimer_start(struct pwm_omap_dmtimer_chip *omap) { /* @@ -82,33 +108,46 @@ static void pwm_omap_dmtimer_start(struct pwm_omap_dmtimer_chip *omap) omap->pdata->start(omap->dm_timer); } -static int pwm_omap_dmtimer_enable(struct pwm_chip *chip, - struct pwm_device *pwm) +/** + * pwm_omap_dmtimer_is_enabled() - Detect if the pwm is enabled. + * @omap: Pointer to pwm omap dm timer chip + * + * Return true if pwm is enabled else false. + */ +static bool pwm_omap_dmtimer_is_enabled(struct pwm_omap_dmtimer_chip *omap) { - struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip); + u32 status; - mutex_lock(&omap->mutex); - omap->pdata->set_pwm(omap->dm_timer, - pwm_get_polarity(pwm) == PWM_POLARITY_INVERSED, - true, OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE, - true); - - pwm_omap_dmtimer_start(omap); - mutex_unlock(&omap->mutex); + status = omap->pdata->get_pwm_status(omap->dm_timer); - return 0; + return !!(status & OMAP_TIMER_CTRL_ST); } -static void pwm_omap_dmtimer_disable(struct pwm_chip *chip, - struct pwm_device *pwm) +/** + * pwm_omap_dmtimer_polarity() - Detect the polarity of pwm. + * @omap: Pointer to pwm omap dm timer chip + * + * Return the polarity of pwm. + */ +static int pwm_omap_dmtimer_polarity(struct pwm_omap_dmtimer_chip *omap) { - struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip); + u32 status; - mutex_lock(&omap->mutex); - omap->pdata->stop(omap->dm_timer); - mutex_unlock(&omap->mutex); + status = omap->pdata->get_pwm_status(omap->dm_timer); + + return !!(status & OMAP_TIMER_CTRL_SCPWM); } +/** + * pwm_omap_dmtimer_config() - Update the configuration of pwm omap dm timer + * @chip: Pointer to PWM controller + * @pwm: Pointer to PWM channel + * @duty_ns: New duty cycle in nano seconds + * @period_ns: New period in nano seconds + * + * Return 0 if successfully changed the period/duty_cycle else appropriate + * error. + */ static int pwm_omap_dmtimer_config(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) @@ -116,30 +155,26 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip, struct pwm_omap_dmtimer_chip *omap = to_pwm_omap_dmtimer_chip(chip); u32 period_cycles, duty_cycles; u32 load_value, match_value; - struct clk *fclk; unsigned long clk_rate; + struct clk *fclk; dev_dbg(chip->dev, "requested duty cycle: %d ns, period: %d ns\n", duty_ns, period_ns); - mutex_lock(&omap->mutex); if (duty_ns == pwm_get_duty_cycle(pwm) && - period_ns == pwm_get_period(pwm)) { - /* No change - don't cause any transients. */ - mutex_unlock(&omap->mutex); + period_ns == pwm_get_period(pwm)) return 0; - } fclk = omap->pdata->get_fclk(omap->dm_timer); if (!fclk) { dev_err(chip->dev, "invalid pmtimer fclk\n"); - goto err_einval; + return -EINVAL; } clk_rate = clk_get_rate(fclk); if (!clk_rate) { dev_err(chip->dev, "invalid pmtimer fclk rate\n"); - goto err_einval; + return -EINVAL; } dev_dbg(chip->dev, "clk rate: %luHz\n", clk_rate); @@ -167,7 +202,7 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip, dev_info(chip->dev, "period %d ns too short for clock rate %lu Hz\n", period_ns, clk_rate); - goto err_einval; + return -EINVAL; } if (duty_cycles < 1) { @@ -199,55 +234,97 @@ static int pwm_omap_dmtimer_config(struct pwm_chip *chip, dev_dbg(chip->dev, "load value: %#08x (%d), match value: %#08x (%d)\n", load_value, load_value, match_value, match_value); - mutex_unlock(&omap->mutex); - return 0; - -err_einval: - mutex_unlock(&omap->mutex); - -