diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-12-25 14:34:48 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-12-25 14:34:48 -0800 |
commit | d923fd6dc133ee8d8fe800e4e4beb9175368b21b (patch) | |
tree | 2da22a097bc29b07eaaa44177552d129f0e58e5b | |
parent | e6d1315006383e525595bb3337d08bccec373ccc (diff) | |
parent | e94b815524f83536415d7d59cc1833ad05934d97 (diff) | |
download | linux-d923fd6dc133ee8d8fe800e4e4beb9175368b21b.tar.bz2 |
Merge tag 'pwm/for-4.21-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm
Pull pwm updates from Thierry Reding:
"Not a lot going on this cycle.
There's some more cleanup going on and new driver support that was not
quite ready in time for v4.21-rc1, but here are a few fixes and
improvements that are good to go.
The Kona PWM driver can now be built on the Cygnus architecture and
the i.MX driver gained support for hardware readback. Some small fixes
are provided for the clks711x and lpc18xx-sct drivers.
Finally, to round things off some drivers are switched to SPDX license
identifiers"
* tag 'pwm/for-4.21-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm:
dt-bindings: pwm: rcar: Add r8a774c0 support
pwm: imx: Add ipg clock operation
pwm: clps711x: Switch to SPDX identifier
pwm: clps711x: Fix period calculation
pwm: bcm2835: Switch to SPDX identifier
pwm: Enable Kona PWM to be built for the Cygnus architecture
pwm: Drop legacy wrapper for changing polarity
pwm: imx: Implement get_state() function for hardware readout
pwm: imx: Use bitops and bitfield macros to define register values
pwm: imx: Sort include files
pwm: lpc18xx-sct: Don't reconfigure PWM in .request and .free
-rw-r--r-- | Documentation/devicetree/bindings/pwm/renesas,pwm-rcar.txt | 1 | ||||
-rw-r--r-- | drivers/pwm/Kconfig | 4 | ||||
-rw-r--r-- | drivers/pwm/pwm-bcm2835.c | 5 | ||||
-rw-r--r-- | drivers/pwm/pwm-clps711x.c | 13 | ||||
-rw-r--r-- | drivers/pwm/pwm-imx.c | 194 | ||||
-rw-r--r-- | drivers/pwm/pwm-lpc18xx-sct.c | 3 | ||||
-rw-r--r-- | include/linux/pwm.h | 42 |
7 files changed, 173 insertions, 89 deletions
diff --git a/Documentation/devicetree/bindings/pwm/renesas,pwm-rcar.txt b/Documentation/devicetree/bindings/pwm/renesas,pwm-rcar.txt index 7f31fe7e2093..fbd6a4f943ce 100644 --- a/Documentation/devicetree/bindings/pwm/renesas,pwm-rcar.txt +++ b/Documentation/devicetree/bindings/pwm/renesas,pwm-rcar.txt @@ -6,6 +6,7 @@ Required Properties: - "renesas,pwm-r8a7744": for RZ/G1N - "renesas,pwm-r8a7745": for RZ/G1E - "renesas,pwm-r8a774a1": for RZ/G2M + - "renesas,pwm-r8a774c0": for RZ/G2E - "renesas,pwm-r8a7778": for R-Car M1A - "renesas,pwm-r8a7779": for R-Car H1 - "renesas,pwm-r8a7790": for R-Car H2 diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 27e5dd47a01f..a8f47df0655a 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -88,7 +88,9 @@ config PWM_BCM_IPROC config PWM_BCM_KONA tristate "Kona PWM support" - depends on ARCH_BCM_MOBILE + depends on ARCH_BCM_MOBILE || ARCH_BCM_CYGNUS || COMPILE_TEST + depends on HAVE_CLK && HAS_IOMEM + default ARCH_BCM_MOBILE || ARCH_BCM_CYGNUS help Generic PWM framework driver for Broadcom Kona PWM block. diff --git a/drivers/pwm/pwm-bcm2835.c b/drivers/pwm/pwm-bcm2835.c index db001cba937f..5652f461d994 100644 --- a/drivers/pwm/pwm-bcm2835.c +++ b/drivers/pwm/pwm-bcm2835.c @@ -1,9 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright 2014 Bart Tanghe <bart.tanghe@thomasmore.be> - * - * 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; version 2. */ #include <linux/clk.h> diff --git a/drivers/pwm/pwm-clps711x.c b/drivers/pwm/pwm-clps711x.c index 26ec24e457b1..924d39a797cf 100644 --- a/drivers/pwm/pwm-clps711x.c +++ b/drivers/pwm/pwm-clps711x.c @@ -1,12 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Cirrus Logic CLPS711X PWM driver - * - * Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru> - * - * 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. + * Author: Alexander Shiyan <shc_work@mail.ru> */ #include <linux/clk.h> @@ -48,7 +43,7 @@ static void clps711x_pwm_update_val(struct clps711x_chip *priv, u32 n, u32 v) static unsigned int clps711x_get_duty(struct pwm_device *pwm, unsigned int v) { /* Duty cycle 0..15 max */ - return DIV_ROUND_CLOSEST(v * 0xf, pwm_get_period(pwm)); + return DIV_ROUND_CLOSEST(v * 0xf, pwm->args.period); } static int clps711x_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) @@ -71,7 +66,7 @@ static int clps711x_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, struct clps711x_chip *priv = to_clps711x_chip(chip); unsigned int duty; - if (period_ns != pwm_get_period(pwm)) + if (period_ns != pwm->args.period) return -EINVAL; duty = clps711x_get_duty(pwm, duty_ns); diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index 1d5242c9cde0..55a3a363d5be 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -5,17 +5,19 @@ * Derived from pxa PWM driver by eric miao <eric.miao@marvell.com> */ -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/err.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> #include <linux/clk.h> #include <linux/delay.h> +#include <linux/err.h> #include <linux/io.h> -#include <linux/pwm.h> +#include <linux/kernel.h> +#include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/slab.h> /* i.MX1 and i.MX21 share the same PWM function block: */ @@ -23,7 +25,7 @@ #define MX1_PWMS 0x04 /* PWM Sample Register */ #define MX1_PWMP 0x08 /* PWM Period Register */ -#define MX1_PWMC_EN (1 << 4) +#define MX1_PWMC_EN BIT(4) /* i.MX27, i.MX31, i.MX35 share the same PWM function block: */ @@ -31,22 +33,62 @@ #define MX3_PWMSR 0x04 /* PWM Status Register */ #define MX3_PWMSAR 0x0C /* PWM Sample Register */ #define MX3_PWMPR 0x10 /* PWM Period Register */ -#define MX3_PWMCR_PRESCALER(x) ((((x) - 1) & 0xFFF) << 4) -#define MX3_PWMCR_STOPEN (1 << 25) -#define MX3_PWMCR_DOZEEN (1 << 24) -#define MX3_PWMCR_WAITEN (1 << 23) -#define MX3_PWMCR_DBGEN (1 << 22) -#define MX3_PWMCR_POUTC (1 << 18) -#define MX3_PWMCR_CLKSRC_IPG_HIGH (2 << 16) -#define MX3_PWMCR_CLKSRC_IPG (1 << 16) -#define MX3_PWMCR_SWR (1 << 3) -#define MX3_PWMCR_EN (1 << 0) -#define MX3_PWMSR_FIFOAV_4WORDS 0x4 -#define MX3_PWMSR_FIFOAV_MASK 0x7 + +#define MX3_PWMCR_FWM GENMASK(27, 26) +#define MX3_PWMCR_STOPEN BIT(25) +#define MX3_PWMCR_DOZEN BIT(24) +#define MX3_PWMCR_WAITEN BIT(23) +#define MX3_PWMCR_DBGEN BIT(22) +#define MX3_PWMCR_BCTR BIT(21) +#define MX3_PWMCR_HCTR BIT(20) + +#define MX3_PWMCR_POUTC GENMASK(19, 18) +#define MX3_PWMCR_POUTC_NORMAL 0 +#define MX3_PWMCR_POUTC_INVERTED 1 +#define MX3_PWMCR_POUTC_OFF 2 + +#define MX3_PWMCR_CLKSRC GENMASK(17, 16) +#define MX3_PWMCR_CLKSRC_OFF 0 +#define MX3_PWMCR_CLKSRC_IPG 1 +#define MX3_PWMCR_CLKSRC_IPG_HIGH 2 +#define MX3_PWMCR_CLKSRC_IPG_32K 3 + +#define MX3_PWMCR_PRESCALER GENMASK(15, 4) + +#define MX3_PWMCR_SWR BIT(3) + +#define MX3_PWMCR_REPEAT GENMASK(2, 1) +#define MX3_PWMCR_REPEAT_1X 0 +#define MX3_PWMCR_REPEAT_2X 1 +#define MX3_PWMCR_REPEAT_4X 2 +#define MX3_PWMCR_REPEAT_8X 3 + +#define MX3_PWMCR_EN BIT(0) + +#define MX3_PWMSR_FWE BIT(6) +#define MX3_PWMSR_CMP BIT(5) +#define MX3_PWMSR_ROV BIT(4) +#define MX3_PWMSR_FE BIT(3) + +#define MX3_PWMSR_FIFOAV GENMASK(2, 0) +#define MX3_PWMSR_FIFOAV_EMPTY 0 +#define MX3_PWMSR_FIFOAV_1WORD 1 +#define MX3_PWMSR_FIFOAV_2WORDS 2 +#define MX3_PWMSR_FIFOAV_3WORDS 3 +#define MX3_PWMSR_FIFOAV_4WORDS 4 + +#define MX3_PWMCR_PRESCALER_SET(x) FIELD_PREP(MX3_PWMCR_PRESCALER, (x) - 1) +#define MX3_PWMCR_PRESCALER_GET(x) (FIELD_GET(MX3_PWMCR_PRESCALER, \ + (x)) + 1) #define MX3_PWM_SWR_LOOP 5 +/* PWMPR register value of 0xffff has the same effect as 0xfffe */ +#define MX3_PWMPR_MAX 0xfffe + struct imx_chip { + struct clk *clk_ipg; + struct clk *clk_per; void __iomem *mmio_base; @@ -56,6 +98,87 @@ struct imx_chip { #define to_imx_chip(chip) container_of(chip, struct imx_chip, chip) +static int imx_pwm_clk_prepare_enable(struct pwm_chip *chip) +{ + struct imx_chip *imx = to_imx_chip(chip); + int ret; + + ret = clk_prepare_enable(imx->clk_ipg); + if (ret) + return ret; + + ret = clk_prepare_enable(imx->clk_per); + if (ret) { + clk_disable_unprepare(imx->clk_ipg); + return ret; + } + + return 0; +} + +static void imx_pwm_clk_disable_unprepare(struct pwm_chip *chip) +{ + struct imx_chip *imx = to_imx_chip(chip); + + clk_disable_unprepare(imx->clk_per); + clk_disable_unprepare(imx->clk_ipg); +} + +static void imx_pwm_get_state(struct pwm_chip *chip, + struct pwm_device *pwm, struct pwm_state *state) +{ + struct imx_chip *imx = to_imx_chip(chip); + u32 period, prescaler, pwm_clk, ret, val; + u64 tmp; + + ret = imx_pwm_clk_prepare_enable(chip); + if (ret < 0) + return; + + val = readl(imx->mmio_base + MX3_PWMCR); + + if (val & MX3_PWMCR_EN) { + state->enabled = true; + ret = imx_pwm_clk_prepare_enable(chip); + if (ret) + return; + } else { + state->enabled = false; + } + + switch (FIELD_GET(MX3_PWMCR_POUTC, val)) { + case MX3_PWMCR_POUTC_NORMAL: + state->polarity = PWM_POLARITY_NORMAL; + break; + case MX3_PWMCR_POUTC_INVERTED: + state->polarity = PWM_POLARITY_INVERSED; + break; + default: + dev_warn(chip->dev, "can't set polarity, output disconnected"); + } + + prescaler = MX3_PWMCR_PRESCALER_GET(val); + pwm_clk = clk_get_rate(imx->clk_per); + pwm_clk = DIV_ROUND_CLOSEST_ULL(pwm_clk, prescaler); + val = readl(imx->mmio_base + MX3_PWMPR); + period = val >= MX3_PWMPR_MAX ? MX3_PWMPR_MAX : val; + + /* PWMOUT (Hz) = PWMCLK / (PWMPR + 2) */ + tmp = NSEC_PER_SEC * (u64)(period + 2); + state->period = DIV_ROUND_CLOSEST_ULL(tmp, pwm_clk); + + /* PWMSAR can be read only if PWM is enabled */ + if (state->enabled) { + val = readl(imx->mmio_base + MX3_PWMSAR); + tmp = NSEC_PER_SEC * (u64)(val); + state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, pwm_clk); + } else { + state->duty_cycle = 0; + } + + imx_pwm_clk_disable_unprepare(chip); +} + static int imx_pwm_config_v1(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { @@ -91,7 +214,7 @@ static int imx_pwm_enable_v1(struct pwm_chip *chip, struct pwm_device *pwm) u32 val; int ret; - ret = clk_prepare_enable(imx->clk_per); + ret = imx_pwm_clk_prepare_enable(chip); if (ret < 0) return ret; @@ -111,7 +234,7 @@ static void imx_pwm_disable_v1(struct pwm_chip *chip, struct pwm_device *pwm) val &= ~MX1_PWMC_EN; writel(val, imx->mmio_base + MX1_PWMC); - clk_disable_unprepare(imx->clk_per); + imx_pwm_clk_disable_unprepare(chip); } static void imx_pwm_sw_reset(struct pwm_chip *chip) @@ -142,14 +265,14 @@ static void imx_pwm_wait_fifo_slot(struct pwm_chip *chip, u32 sr; sr = readl(imx->mmio_base + MX3_PWMSR); - fifoav = sr & MX3_PWMSR_FIFOAV_MASK; + fifoav = FIELD_GET(MX3_PWMSR_FIFOAV, sr); if (fifoav == MX3_PWMSR_FIFOAV_4WORDS) { period_ms = DIV_ROUND_UP(pwm_get_period(pwm), NSEC_PER_MSEC); msleep(period_ms); sr = readl(imx->mmio_base + MX3_PWMSR); - if (fifoav == (sr & MX3_PWMSR_FIFOAV_MASK)) + if (fifoav == FIELD_GET(MX3_PWMSR_FIFOAV, sr)) dev_warn(dev, "there is no free FIFO slot\n"); } } @@ -197,7 +320,7 @@ static int imx_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm, if (cstate.enabled) { imx_pwm_wait_fifo_slot(chip, pwm); } else { - ret = clk_prepare_enable(imx->clk_per); + ret = imx_pwm_clk_prepare_enable(chip); if (ret) return ret; @@ -207,19 +330,20 @@ static int imx_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm, writel(duty_cycles, imx->mmio_base + MX3_PWMSAR); writel(period_cycles, imx->mmio_base + MX3_PWMPR); - cr = MX3_PWMCR_PRESCALER(prescale) | - MX3_PWMCR_STOPEN | MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN | - MX3_PWMCR_DBGEN | MX3_PWMCR_CLKSRC_IPG_HIGH | - MX3_PWMCR_EN; + cr = MX3_PWMCR_PRESCALER_SET(prescale) | + MX3_PWMCR_STOPEN | MX3_PWMCR_DOZEN | MX3_PWMCR_WAITEN | + FIELD_PREP(MX3_PWMCR_CLKSRC, MX3_PWMCR_CLKSRC_IPG_HIGH) | + MX3_PWMCR_DBGEN | MX3_PWMCR_EN; if (state->polarity == PWM_POLARITY_INVERSED) - cr |= MX3_PWMCR_POUTC; + cr |= FIELD_PREP(MX3_PWMCR_POUTC, + MX3_PWMCR_POUTC_INVERTED); writel(cr, imx->mmio_base + MX3_PWMCR); } else if (cstate.enabled) { writel(0, imx->mmio_base + MX3_PWMCR); - clk_disable_unprepare(imx->clk_per); + imx_pwm_clk_disable_unprepare(chip); } return 0; @@ -234,6 +358,7 @@ static const struct pwm_ops imx_pwm_ops_v1 = { static const struct pwm_ops imx_pwm_ops_v2 = { .apply = imx_pwm_apply_v2, + .get_state = imx_pwm_get_state, .owner = THIS_MODULE, }; @@ -276,6 +401,13 @@ static int imx_pwm_probe(struct platform_device *pdev) if (imx == NULL) return -ENOMEM; + imx->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(imx->clk_ipg)) { + dev_err(&pdev->dev, "getting ipg clock failed with %ld\n", + PTR_ERR(imx->clk_ipg)); + return PTR_ERR(imx->clk_ipg); + } + imx->clk_per = devm_clk_get(&pdev->dev, "per"); if (IS_ERR(imx->clk_per)) { dev_err(&pdev->dev, "getting per clock failed with %ld\n", @@ -315,6 +447,8 @@ static int imx_pwm_remove(struct platform_device *pdev) if (imx == NULL) return -ENODEV; + imx_pwm_clk_disable_unprepare(&imx->chip); + return pwmchip_remove(&imx->chip); } diff --git a/drivers/pwm/pwm-lpc18xx-sct.c b/drivers/pwm/pwm-lpc18xx-sct.c index d7f5f7de030d..475918d9f543 100644 --- a/drivers/pwm/pwm-lpc18xx-sct.c +++ b/drivers/pwm/pwm-lpc18xx-sct.c @@ -296,7 +296,6 @@ static int lpc18xx_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) set_bit(event, &lpc18xx_pwm->event_map); lpc18xx_data->duty_event = event; - lpc18xx_pwm_config_duty(chip, pwm, pwm_get_duty_cycle(pwm)); return 0; } @@ -306,8 +305,6 @@ static void lpc18xx_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip); struct lpc18xx_pwm_data *lpc18xx_data = pwm_get_chip_data(pwm); - pwm_disable(pwm); - pwm_set_duty_cycle(pwm, 0); clear_bit(lpc18xx_data->duty_event, &lpc18xx_pwm->event_map); } diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 56518adc31dd..d5199b507d79 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -349,42 +349,6 @@ static inline int pwm_config(struct pwm_device *pwm, int duty_ns, } /** - * pwm_set_polarity() - configure the polarity of a PWM signal - * @pwm: PWM device - * @polarity: new polarity of the PWM signal - * - * Note that the polarity cannot be configured while the PWM device is - * enabled. - * - * Returns: 0 on success or a negative error code on failure. - */ -static inline int pwm_set_polarity(struct pwm_device *pwm, - enum pwm_polarity polarity) -{ - struct pwm_state state; - - if (!pwm) - return -EINVAL; - - pwm_get_state(pwm, &state); - if (state.polarity == polarity) - return 0; - - /* - * Changing the polarity of a running PWM without adjusting the - * dutycycle/period value is a bit risky (can introduce glitches). - * Return -EBUSY in this case. - * Note that this is allowed when using pwm_apply_state() because - * the user specifies all the parameters. - */ - if (state.enabled) - return -EBUSY; - - state.polarity = polarity; - return pwm_apply_state(pwm, &state); -} - -/** * pwm_enable() - start a PWM output toggling * @pwm: PWM device * @@ -483,12 +447,6 @@ static inline int pwm_capture(struct pwm_device *pwm, return -EINVAL; } -static inline int pwm_set_polarity(struct pwm_device *pwm, - enum pwm_polarity polarity) -{ - return -ENOTSUPP; -} - static inline int pwm_enable(struct pwm_device *pwm) { return -EINVAL; |