From 309b32fb97d8cf0771373fb55e16b2852840c9aa Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 7 Jan 2019 20:49:37 +0100 Subject: pwm: Don't use memcmp() to compare state variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Given that struct pwm_state is sparse (at least on some platforms), variables of this type might represent the same state because all fields are pairwise identical but still memcmp() returns a difference because some of the unused bits are different. To prevent surprises compare member by member instead of the whole occupied memory. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 1581f6ab1b1f..253a459fe0d8 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -472,7 +472,10 @@ int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state) state->duty_cycle > state->period) return -EINVAL; - if (!memcmp(state, &pwm->state, sizeof(*state))) + if (state->period == pwm->state.period && + state->duty_cycle == pwm->state.duty_cycle && + state->polarity == pwm->state.polarity && + state->enabled == pwm->state.enabled) return 0; if (pwm->chip->ops->apply) { -- cgit v1.2.3 From cc2d22477779f189595db5c515bd5ef9c75a1f35 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 7 Jan 2019 20:49:39 +0100 Subject: pwm: Drop per-chip dbg_show callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This callback was introduced in commit 62099abf67a2 ("pwm: Add debugfs interface") in 2012 and up to now there is not a single user. So drop this unused code. Signed-off-by: Uwe Kleine-König [thierry.reding@gmail.com: remove kerneldoc for ->dbg_show()] Signed-off-by: Thierry Reding --- drivers/pwm/core.c | 5 +---- include/linux/pwm.h | 4 ---- 2 files changed, 1 insertion(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index 253a459fe0d8..3149204567f3 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c @@ -1036,10 +1036,7 @@ static int pwm_seq_show(struct seq_file *s, void *v) dev_name(chip->dev), chip->npwm, (chip->npwm != 1) ? "s" : ""); - if (chip->ops->dbg_show) - chip->ops->dbg_show(chip, s); - else - pwm_dbg_show(chip, s); + pwm_dbg_show(chip, s); return 0; } diff --git a/include/linux/pwm.h b/include/linux/pwm.h index d5199b507d79..6a544cb89de4 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h @@ -254,7 +254,6 @@ pwm_set_relative_duty_cycle(struct pwm_state *state, unsigned int duty_cycle, * @get_state: get the current PWM state. This function is only * called once per PWM device when the PWM chip is * registered. - * @dbg_show: optional routine to show contents in debugfs * @owner: helps prevent removal of modules exporting active PWMs */ struct pwm_ops { @@ -272,9 +271,6 @@ struct pwm_ops { struct pwm_state *state); void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state); -#ifdef CONFIG_DEBUG_FS - void (*dbg_show)(struct pwm_chip *chip, struct seq_file *s); -#endif struct module *owner; }; -- cgit v1.2.3 From 01482d2443db24aa5234f3ffa03b09f03c94f603 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 7 Jan 2019 20:53:49 +0100 Subject: pwm: imx: Remove if block where the condition is always wrong MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ->remove() callback is only called when probe returned successfully. In this case the driver data cannot be NULL. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index 55a3a363d5be..1e90d2b78625 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -444,8 +444,6 @@ static int imx_pwm_remove(struct platform_device *pdev) struct imx_chip *imx; imx = platform_get_drvdata(pdev); - if (imx == NULL) - return -ENODEV; imx_pwm_clk_disable_unprepare(&imx->chip); -- cgit v1.2.3 From f20b187e32e07914ff9878880c7d08ca89b5c3ac Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 7 Jan 2019 20:53:50 +0100 Subject: pwm: imx: Set driver data earlier simplifying the end of ->probe() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When ->probe() fails the driver core takes care of unsetting the driver data. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index 1e90d2b78625..809493d86d22 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -390,7 +390,6 @@ static int imx_pwm_probe(struct platform_device *pdev) const struct imx_pwm_data *data; struct imx_chip *imx; struct resource *r; - int ret = 0; if (!of_id) return -ENODEV; @@ -401,6 +400,8 @@ static int imx_pwm_probe(struct platform_device *pdev) if (imx == NULL) return -ENOMEM; + platform_set_drvdata(pdev, imx); + 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", @@ -431,12 +432,7 @@ static int imx_pwm_probe(struct platform_device *pdev) if (IS_ERR(imx->mmio_base)) return PTR_ERR(imx->mmio_base); - ret = pwmchip_add(&imx->chip); - if (ret < 0) - return ret; - - platform_set_drvdata(pdev, imx); - return 0; + return pwmchip_add(&imx->chip); } static int imx_pwm_remove(struct platform_device *pdev) -- cgit v1.2.3 From b9a5c60bc2f65561535dc05d0c740aa6e9e3bdf2 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 7 Jan 2019 20:53:51 +0100 Subject: pwm: imx: Don't print an error on -EPROBE_DEFER MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When getting the peripheral clock fails with -EPROBE_DEFER the driver is usually probed again later and emitting an error message is irritating. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index 809493d86d22..30380fcb5cfb 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -411,9 +411,14 @@ static int imx_pwm_probe(struct platform_device *pdev) 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", - PTR_ERR(imx->clk_per)); - return PTR_ERR(imx->clk_per); + int ret = PTR_ERR(imx->clk_per); + + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "failed to get peripheral clock: %d\n", + ret); + + return ret; } imx->chip.ops = data->ops; -- cgit v1.2.3 From d80f8206905c1a8c3857d90f12bbfd6293b48a4b Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 7 Jan 2019 20:53:52 +0100 Subject: pwm: imx: Split into two drivers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The two PWM implementations called v1 (for i.MX1 and i.MX21) and v2 (for i.MX27 and later) have nothing in common apart from needing two clocks named "per" and "ipg" and being integrated in a SoC named i.MX. So split the file containing the two disjunct drivers into two files and two complete separate drivers. Signed-off-by: Uwe Kleine-König [thierry.reding@gmail.com: fix a modular build issue] Signed-off-by: Thierry Reding --- drivers/pwm/Kconfig | 17 +- drivers/pwm/Makefile | 3 +- drivers/pwm/pwm-imx.c | 466 ------------------------------------------------ drivers/pwm/pwm-imx1.c | 199 +++++++++++++++++++++ drivers/pwm/pwm-imx27.c | 361 +++++++++++++++++++++++++++++++++++++ 5 files changed, 575 insertions(+), 471 deletions(-) delete mode 100644 drivers/pwm/pwm-imx.c create mode 100644 drivers/pwm/pwm-imx1.c create mode 100644 drivers/pwm/pwm-imx27.c (limited to 'drivers') diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index a8f47df0655a..54f8238aac0d 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -192,14 +192,23 @@ config PWM_IMG To compile this driver as a module, choose M here: the module will be called pwm-img -config PWM_IMX - tristate "i.MX PWM support" +config PWM_IMX1 + tristate "i.MX1 PWM support" depends on ARCH_MXC help - Generic PWM framework driver for i.MX. + Generic PWM framework driver for i.MX1 and i.MX21 To compile this driver as a module, choose M here: the module - will be called pwm-imx. + will be called pwm-imx1. + +config PWM_IMX27 + tristate "i.MX27 PWM support" + depends on ARCH_MXC + help + Generic PWM framework driver for i.MX27 and later i.MX SoCs. + + To compile this driver as a module, choose M here: the module + will be called pwm-imx27. config PWM_JZ4740 tristate "Ingenic JZ47xx PWM support" diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 9c676a0dadf5..448825e892bc 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -17,7 +17,8 @@ obj-$(CONFIG_PWM_EP93XX) += pwm-ep93xx.o obj-$(CONFIG_PWM_FSL_FTM) += pwm-fsl-ftm.o obj-$(CONFIG_PWM_HIBVT) += pwm-hibvt.o obj-$(CONFIG_PWM_IMG) += pwm-img.o -obj-$(CONFIG_PWM_IMX) += pwm-imx.o +obj-$(CONFIG_PWM_IMX1) += pwm-imx1.o +obj-$(CONFIG_PWM_IMX27) += pwm-imx27.o obj-$(CONFIG_PWM_JZ4740) += pwm-jz4740.o obj-$(CONFIG_PWM_LP3943) += pwm-lp3943.o obj-$(CONFIG_PWM_LPC18XX_SCT) += pwm-lpc18xx-sct.o diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c deleted file mode 100644 index 30380fcb5cfb..000000000000 --- a/drivers/pwm/pwm-imx.c +++ /dev/null @@ -1,466 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * simple driver for PWM (Pulse Width Modulator) controller - * - * Derived from pxa PWM driver by eric miao - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* i.MX1 and i.MX21 share the same PWM function block: */ - -#define MX1_PWMC 0x00 /* PWM Control Register */ -#define MX1_PWMS 0x04 /* PWM Sample Register */ -#define MX1_PWMP 0x08 /* PWM Period Register */ - -#define MX1_PWMC_EN BIT(4) - -/* i.MX27, i.MX31, i.MX35 share the same PWM function block: */ - -#define MX3_PWMCR 0x00 /* PWM Control Register */ -#define MX3_PWMSR 0x04 /* PWM Status Register */ -#define MX3_PWMSAR 0x0C /* PWM Sample Register */ -#define MX3_PWMPR 0x10 /* PWM Period Register */ - -#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; - - struct pwm_chip 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) -{ - struct imx_chip *imx = to_imx_chip(chip); - - /* - * The PWM subsystem allows for exact frequencies. However, - * I cannot connect a scope on my device to the PWM line and - * thus cannot provide the program the PWM controller - * exactly. Instead, I'm relying on the fact that the - * Bootloader (u-boot or WinCE+haret) has programmed the PWM - * function group already. So I'll just modify the PWM sample - * register to follow the ratio of duty_ns vs. period_ns - * accordingly. - * - * This is good enough for programming the brightness of - * the LCD backlight. - * - * The real implementation would divide PERCLK[0] first by - * both the prescaler (/1 .. /128) and then by CLKSEL - * (/2 .. /16). - */ - u32 max = readl(imx->mmio_base + MX1_PWMP); - u32 p = max * duty_ns / period_ns; - writel(max - p, imx->mmio_base + MX1_PWMS); - - return 0; -} - -static int imx_pwm_enable_v1(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct imx_chip *imx = to_imx_chip(chip); - u32 val; - int ret; - - ret = imx_pwm_clk_prepare_enable(chip); - if (ret < 0) - return ret; - - val = readl(imx->mmio_base + MX1_PWMC); - val |= MX1_PWMC_EN; - writel(val, imx->mmio_base + MX1_PWMC); - - return 0; -} - -static void imx_pwm_disable_v1(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct imx_chip *imx = to_imx_chip(chip); - u32 val; - - val = readl(imx->mmio_base + MX1_PWMC); - val &= ~MX1_PWMC_EN; - writel(val, imx->mmio_base + MX1_PWMC); - - imx_pwm_clk_disable_unprepare(chip); -} - -static void imx_pwm_sw_reset(struct pwm_chip *chip) -{ - struct imx_chip *imx = to_imx_chip(chip); - struct device *dev = chip->dev; - int wait_count = 0; - u32 cr; - - writel(MX3_PWMCR_SWR, imx->mmio_base + MX3_PWMCR); - do { - usleep_range(200, 1000); - cr = readl(imx->mmio_base + MX3_PWMCR); - } while ((cr & MX3_PWMCR_SWR) && - (wait_count++ < MX3_PWM_SWR_LOOP)); - - if (cr & MX3_PWMCR_SWR) - dev_warn(dev, "software reset timeout\n"); -} - -static void imx_pwm_wait_fifo_slot(struct pwm_chip *chip, - struct pwm_device *pwm) -{ - struct imx_chip *imx = to_imx_chip(chip); - struct device *dev = chip->dev; - unsigned int period_ms; - int fifoav; - u32 sr; - - sr = readl(imx->mmio_base + MX3_PWMSR); - 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 == FIELD_GET(MX3_PWMSR_FIFOAV, sr)) - dev_warn(dev, "there is no free FIFO slot\n"); - } -} - -static int imx_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm, - struct pwm_state *state) -{ - unsigned long period_cycles, duty_cycles, prescale; - struct imx_chip *imx = to_imx_chip(chip); - struct pwm_state cstate; - unsigned long long c; - int ret; - u32 cr; - - pwm_get_state(pwm, &cstate); - - if (state->enabled) { - c = clk_get_rate(imx->clk_per); - c *= state->period; - - do_div(c, 1000000000); - period_cycles = c; - - prescale = period_cycles / 0x10000 + 1; - - period_cycles /= prescale; - c = (unsigned long long)period_cycles * state->duty_cycle; - do_div(c, state->period); - duty_cycles = c; - - /* - * according to imx pwm RM, the real period value should be - * PERIOD value in PWMPR plus 2. - */ - if (period_cycles > 2) - period_cycles -= 2; - else - period_cycles = 0; - - /* - * Wait for a free FIFO slot if the PWM is already enabled, and - * flush the FIFO if the PWM was disabled and is about to be - * enabled. - */ - if (cstate.enabled) { - imx_pwm_wait_fifo_slot(chip, pwm); - } else { - ret = imx_pwm_clk_prepare_enable(chip); - if (ret) - return ret; - - imx_pwm_sw_reset(chip); - } - - writel(duty_cycles, imx->mmio_base + MX3_PWMSAR); - writel(period_cycles, imx->mmio_base + MX3_PWMPR); - - 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 |= 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); - - imx_pwm_clk_disable_unprepare(chip); - } - - return 0; -} - -static const struct pwm_ops imx_pwm_ops_v1 = { - .enable = imx_pwm_enable_v1, - .disable = imx_pwm_disable_v1, - .config = imx_pwm_config_v1, - .owner = THIS_MODULE, -}; - -static const struct pwm_ops imx_pwm_ops_v2 = { - .apply = imx_pwm_apply_v2, - .get_state = imx_pwm_get_state, - .owner = THIS_MODULE, -}; - -struct imx_pwm_data { - bool polarity_supported; - const struct pwm_ops *ops; -}; - -static struct imx_pwm_data imx_pwm_data_v1 = { - .ops = &imx_pwm_ops_v1, -}; - -static struct imx_pwm_data imx_pwm_data_v2 = { - .polarity_supported = true, - .ops = &imx_pwm_ops_v2, -}; - -static const struct of_device_id imx_pwm_dt_ids[] = { - { .compatible = "fsl,imx1-pwm", .data = &imx_pwm_data_v1, }, - { .compatible = "fsl,imx27-pwm", .data = &imx_pwm_data_v2, }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, imx_pwm_dt_ids); - -static int imx_pwm_probe(struct platform_device *pdev) -{ - const struct of_device_id *of_id = - of_match_device(imx_pwm_dt_ids, &pdev->dev); - const struct imx_pwm_data *data; - struct imx_chip *imx; - struct resource *r; - - if (!of_id) - return -ENODEV; - - data = of_id->data; - - imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL); - if (imx == NULL) - return -ENOMEM; - - platform_set_drvdata(pdev, imx); - - 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)) { - int ret = PTR_ERR(imx->clk_per); - - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, - "failed to get peripheral clock: %d\n", - ret); - - return ret; - } - - imx->chip.ops = data->ops; - imx->chip.dev = &pdev->dev; - imx->chip.base = -1; - imx->chip.npwm = 1; - - if (data->polarity_supported) { - dev_dbg(&pdev->dev, "PWM supports output inversion\n"); - imx->chip.of_xlate = of_pwm_xlate_with_flags; - imx->chip.of_pwm_n_cells = 3; - } - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - imx->mmio_base = devm_ioremap_resource(&pdev->dev, r); - if (IS_ERR(imx->mmio_base)) - return PTR_ERR(imx->mmio_base); - - return pwmchip_add(&imx->chip); -} - -static int imx_pwm_remove(struct platform_device *pdev) -{ - struct imx_chip *imx; - - imx = platform_get_drvdata(pdev); - - imx_pwm_clk_disable_unprepare(&imx->chip); - - return pwmchip_remove(&imx->chip); -} - -static struct platform_driver imx_pwm_driver = { - .driver = { - .name = "imx-pwm", - .of_match_table = imx_pwm_dt_ids, - }, - .probe = imx_pwm_probe, - .remove = imx_pwm_remove, -}; - -module_platform_driver(imx_pwm_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Sascha Hauer "); diff --git a/drivers/pwm/pwm-imx1.c b/drivers/pwm/pwm-imx1.c new file mode 100644 index 000000000000..f8b2c2e001a7 --- /dev/null +++ b/drivers/pwm/pwm-imx1.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * simple driver for PWM (Pulse Width Modulator) controller + * + * Derived from pxa PWM driver by eric miao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MX1_PWMC 0x00 /* PWM Control Register */ +#define MX1_PWMS 0x04 /* PWM Sample Register */ +#define MX1_PWMP 0x08 /* PWM Period Register */ + +#define MX1_PWMC_EN BIT(4) + +struct pwm_imx1_chip { + struct clk *clk_ipg; + struct clk *clk_per; + void __iomem *mmio_base; + struct pwm_chip chip; +}; + +#define to_pwm_imx1_chip(chip) container_of(chip, struct pwm_imx1_chip, chip) + +static int pwm_imx1_clk_prepare_enable(struct pwm_chip *chip) +{ + struct pwm_imx1_chip *imx = to_pwm_imx1_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 pwm_imx1_clk_disable_unprepare(struct pwm_chip *chip) +{ + struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip); + + clk_disable_unprepare(imx->clk_per); + clk_disable_unprepare(imx->clk_ipg); +} + +static int pwm_imx1_config(struct pwm_chip *chip, + struct pwm_device *pwm, int duty_ns, int period_ns) +{ + struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip); + u32 max, p; + + /* + * The PWM subsystem allows for exact frequencies. However, + * I cannot connect a scope on my device to the PWM line and + * thus cannot provide the program the PWM controller + * exactly. Instead, I'm relying on the fact that the + * Bootloader (u-boot or WinCE+haret) has programmed the PWM + * function group already. So I'll just modify the PWM sample + * register to follow the ratio of duty_ns vs. period_ns + * accordingly. + * + * This is good enough for programming the brightness of + * the LCD backlight. + * + * The real implementation would divide PERCLK[0] first by + * both the prescaler (/1 .. /128) and then by CLKSEL + * (/2 .. /16). + */ + max = readl(imx->mmio_base + MX1_PWMP); + p = max * duty_ns / period_ns; + + writel(max - p, imx->mmio_base + MX1_PWMS); + + return 0; +} + +static int pwm_imx1_enable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip); + u32 value; + int ret; + + ret = pwm_imx1_clk_prepare_enable(chip); + if (ret < 0) + return ret; + + value = readl(imx->mmio_base + MX1_PWMC); + value |= MX1_PWMC_EN; + writel(value, imx->mmio_base + MX1_PWMC); + + return 0; +} + +static void pwm_imx1_disable(struct pwm_chip *chip, struct pwm_device *pwm) +{ + struct pwm_imx1_chip *imx = to_pwm_imx1_chip(chip); + u32 value; + + value = readl(imx->mmio_base + MX1_PWMC); + value &= ~MX1_PWMC_EN; + writel(value, imx->mmio_base + MX1_PWMC); + + pwm_imx1_clk_disable_unprepare(chip); +} + +static const struct pwm_ops pwm_imx1_ops = { + .enable = pwm_imx1_enable, + .disable = pwm_imx1_disable, + .config = pwm_imx1_config, + .owner = THIS_MODULE, +}; + +static const struct of_device_id pwm_imx1_dt_ids[] = { + { .compatible = "fsl,imx1-pwm", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, pwm_imx1_dt_ids); + +static int pwm_imx1_probe(struct platform_device *pdev) +{ + struct pwm_imx1_chip *imx; + struct resource *r; + + imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL); + if (!imx) + return -ENOMEM; + + platform_set_drvdata(pdev, imx); + + 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)) { + int ret = PTR_ERR(imx->clk_per); + + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "failed to get peripheral clock: %d\n", + ret); + + return ret; + } + + imx->chip.ops = &pwm_imx1_ops; + imx->chip.dev = &pdev->dev; + imx->chip.base = -1; + imx->chip.npwm = 1; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + imx->mmio_base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(imx->mmio_base)) + return PTR_ERR(imx->mmio_base); + + return pwmchip_add(&imx->chip); +} + +static int pwm_imx1_remove(struct platform_device *pdev) +{ + struct pwm_imx1_chip *imx = platform_get_drvdata(pdev); + + pwm_imx1_clk_disable_unprepare(&imx->chip); + + return pwmchip_remove(&imx->chip); +} + +static struct platform_driver pwm_imx1_driver = { + .driver = { + .name = "pwm-imx1", + .of_match_table = pwm_imx1_dt_ids, + }, + .probe = pwm_imx1_probe, + .remove = pwm_imx1_remove, +}; +module_platform_driver(pwm_imx1_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Sascha Hauer "); diff --git a/drivers/pwm/pwm-imx27.c b/drivers/pwm/pwm-imx27.c new file mode 100644 index 000000000000..8b8b1c6b7f29 --- /dev/null +++ b/drivers/pwm/pwm-imx27.c @@ -0,0 +1,361 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * simple driver for PWM (Pulse Width Modulator) controller + * + * Derived from pxa PWM driver by eric miao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MX3_PWMCR 0x00 /* PWM Control Register */ +#define MX3_PWMSR 0x04 /* PWM Status Register */ +#define MX3_PWMSAR 0x0C /* PWM Sample Register */ +#define MX3_PWMPR 0x10 /* PWM Period Register */ + +#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 pwm_imx27_chip { + struct clk *clk_ipg; + struct clk *clk_per; + void __iomem *mmio_base; + struct pwm_chip 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) +{ + struct pwm_imx27_chip *imx = to_pwm_imx27_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 pwm_imx27_clk_disable_unprepare(struct pwm_chip *chip) +{ + struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); + + clk_disable_unprepare(imx->clk_per); + clk_disable_unprepare(imx->clk_ipg); +} + +static void pwm_imx27_get_state(struct pwm_chip *chip, + struct pwm_device *pwm, struct pwm_state *state) +{ + struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); + u32 period, prescaler, pwm_clk, ret, val; + u64 tmp; + + ret = pwm_imx27_clk_prepare_enable(chip); + if (ret < 0) + return; + + val = readl(imx->mmio_base + MX3_PWMCR); + + if (val & MX3_PWMCR_EN) { + state->enabled = true; + ret = pwm_imx27_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; + } + + pwm_imx27_clk_disable_unprepare(chip); +} + +static void pwm_imx27_sw_reset(struct pwm_chip *chip) +{ + struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); + struct device *dev = chip->dev; + int wait_count = 0; + u32 cr; + + writel(MX3_PWMCR_SWR, imx->mmio_base + MX3_PWMCR); + do { + usleep_range(200, 1000); + cr = readl(imx->mmio_base + MX3_PWMCR); + } while ((cr & MX3_PWMCR_SWR) && + (wait_count++ < MX3_PWM_SWR_LOOP)); + + if (cr & MX3_PWMCR_SWR) + dev_warn(dev, "software reset timeout\n"); +} + +static void pwm_imx27_wait_fifo_slot(struct pwm_chip *chip, + struct pwm_device *pwm) +{ + struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); + struct device *dev = chip->dev; + unsigned int period_ms; + int fifoav; + u32 sr; + + sr = readl(imx->mmio_base + MX3_PWMSR); + 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 == FIELD_GET(MX3_PWMSR_FIFOAV, sr)) + dev_warn(dev, "there is no free FIFO slot\n"); + } +} + +static int pwm_imx27_apply(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + unsigned long period_cycles, duty_cycles, prescale; + struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); + struct pwm_state cstate; + unsigned long long c; + int ret; + u32 cr; + + pwm_get_state(pwm, &cstate); + + if (state->enabled) { + c = clk_get_rate(imx->clk_per); + c *= state->period; + + do_div(c, 1000000000); + period_cycles = c; + + prescale = period_cycles / 0x10000 + 1; + + period_cycles /= prescale; + c = (unsigned long long)period_cycles * state->duty_cycle; + do_div(c, state->period); + duty_cycles = c; + + /* + * according to imx pwm RM, the real period value should be + * PERIOD value in PWMPR plus 2. + */ + if (period_cycles > 2) + period_cycles -= 2; + else + period_cycles = 0; + + /* + * Wait for a free FIFO slot if the PWM is already enabled, and + * flush the FIFO if the PWM was disabled and is about to be + * enabled. + */ + if (cstate.enabled) { + pwm_imx27_wait_fifo_slot(chip, pwm); + } else { + ret = pwm_imx27_clk_prepare_enable(chip); + if (ret) + return ret; + + pwm_imx27_sw_reset(chip); + } + + writel(duty_cycles, imx->mmio_base + MX3_PWMSAR); + writel(period_cycles, imx->mmio_base + MX3_PWMPR); + + 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 |= 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); + + pwm_imx27_clk_disable_unprepare(chip); + } + + return 0; +} + +static const struct pwm_ops pwm_imx27_ops = { + .apply = pwm_imx27_apply, + .get_state = pwm_imx27_get_state, + .owner = THIS_MODULE, +}; + +static const struct of_device_id pwm_imx27_dt_ids[] = { + { .compatible = "fsl,imx27-pwm", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, pwm_imx27_dt_ids); + +static int pwm_imx27_probe(struct platform_device *pdev) +{ + struct pwm_imx27_chip *imx; + struct resource *r; + + imx = devm_kzalloc(&pdev->dev, sizeof(*imx), GFP_KERNEL); + if (imx == NULL) + return -ENOMEM; + + platform_set_drvdata(pdev, imx); + + 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)) { + int ret = PTR_ERR(imx->clk_per); + + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "failed to get peripheral clock: %d\n", + ret); + + return ret; + } + + imx->chip.ops = &pwm_imx27_ops; + imx->chip.dev = &pdev->dev; + imx->chip.base = -1; + imx->chip.npwm = 1; + + imx->chip.of_xlate = of_pwm_xlate_with_flags; + imx->chip.of_pwm_n_cells = 3; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + imx->mmio_base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(imx->mmio_base)) + return PTR_ERR(imx->mmio_base); + + return pwmchip_add(&imx->chip); +} + +static int pwm_imx27_remove(struct platform_device *pdev) +{ + struct pwm_imx27_chip *imx; + + imx = platform_get_drvdata(pdev); + + pwm_imx27_clk_disable_unprepare(&imx->chip); + + return pwmchip_remove(&imx->chip); +} + +static struct platform_driver imx_pwm_driver = { + .driver = { + .name = "pwm-imx27", + .of_match_table = pwm_imx27_dt_ids, + }, + .probe = pwm_imx27_probe, + .remove = pwm_imx27_remove, +}; +module_platform_driver(imx_pwm_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Sascha Hauer "); -- cgit v1.2.3 From 7ca17b207127240b1e4c8687ba7b9077e655336d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 9 Jan 2019 11:27:47 +0300 Subject: pwm: imx: Signedness bug in imx_pwm_get_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "ret" only holds zero and negative error codes. It needs to be signed for the error handling to work. Fixes: 9f4c8f9607c3 ("pwm: imx: Add ipg clock operation") Signed-off-by: Dan Carpenter Reviewed-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx27.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-imx27.c b/drivers/pwm/pwm-imx27.c index 8b8b1c6b7f29..55666cca4cee 100644 --- a/drivers/pwm/pwm-imx27.c +++ b/drivers/pwm/pwm-imx27.c @@ -115,8 +115,9 @@ static void pwm_imx27_get_state(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state) { struct pwm_imx27_chip *imx = to_pwm_imx27_chip(chip); - u32 period, prescaler, pwm_clk, ret, val; + u32 period, prescaler, pwm_clk, val; u64 tmp; + int ret; ret = pwm_imx27_clk_prepare_enable(chip); if (ret < 0) -- cgit v1.2.3 From 77c3edde4afff2a658f0642546d6b35672f2dc41 Mon Sep 17 00:00:00 2001 From: Mathieu Othacehe Date: Tue, 19 Feb 2019 10:58:06 +0100 Subject: pwm: hibvt: Use individual struct per of-data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split pwm_soc array in one struct per SoC and point to the corresponding one in of-data. Signed-off-by: Mathieu Othacehe Acked-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-hibvt.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-hibvt.c b/drivers/pwm/pwm-hibvt.c index 27c107e78d59..ffc803818c3c 100644 --- a/drivers/pwm/pwm-hibvt.c +++ b/drivers/pwm/pwm-hibvt.c @@ -49,15 +49,19 @@ struct hibvt_pwm_chip { struct clk *clk; void __iomem *base; struct reset_control *rstc; + const struct hibvt_pwm_soc *soc; }; struct hibvt_pwm_soc { u32 num_pwms; }; -static const struct hibvt_pwm_soc pwm_soc[2] = { - { .num_pwms = 4 }, - { .num_pwms = 8 }, +static const struct hibvt_pwm_soc hi3516cv300_soc_info = { + .num_pwms = 4, +}; + +static const struct hibvt_pwm_soc hi3519v100_soc_info = { + .num_pwms = 8, }; static inline struct hibvt_pwm_chip *to_hibvt_pwm_chip(struct pwm_chip *chip) @@ -198,6 +202,7 @@ static int hibvt_pwm_probe(struct platform_device *pdev) pwm_chip->chip.npwm = soc->num_pwms; pwm_chip->chip.of_xlate = of_pwm_xlate_with_flags; pwm_chip->chip.of_pwm_n_cells = 3; + pwm_chip->soc = soc; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); pwm_chip->base = devm_ioremap_resource(&pdev->dev, res); @@ -250,8 +255,10 @@ static int hibvt_pwm_remove(struct platform_device *pdev) } static const struct of_device_id hibvt_pwm_of_match[] = { - { .compatible = "hisilicon,hi3516cv300-pwm", .data = &pwm_soc[0] }, - { .compatible = "hisilicon,hi3519v100-pwm", .data = &pwm_soc[1] }, + { .compatible = "hisilicon,hi3516cv300-pwm", + .data = &hi3516cv300_soc_info }, + { .compatible = "hisilicon,hi3519v100-pwm", + .data = &hi3519v100_soc_info }, { } }; MODULE_DEVICE_TABLE(of, hibvt_pwm_of_match); -- cgit v1.2.3 From 7a58fc5448d186f57d71aac031ade3bf2a302afd Mon Sep 17 00:00:00 2001 From: Mathieu Othacehe Date: Tue, 19 Feb 2019 10:58:08 +0100 Subject: pwm: hibvt: Add hi3559v100 support Add support for the hi3559v100-shub-pwm and hisilicon,hi3559v100-pwm platforms. They require a special quirk: the PWM has to be enabled twice to force a duty_cycle refresh. Signed-off-by: Mathieu Othacehe Signed-off-by: Thierry Reding --- drivers/pwm/pwm-hibvt.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-hibvt.c b/drivers/pwm/pwm-hibvt.c index ffc803818c3c..a0b09603d13d 100644 --- a/drivers/pwm/pwm-hibvt.c +++ b/drivers/pwm/pwm-hibvt.c @@ -54,6 +54,7 @@ struct hibvt_pwm_chip { struct hibvt_pwm_soc { u32 num_pwms; + bool quirk_force_enable; }; static const struct hibvt_pwm_soc hi3516cv300_soc_info = { @@ -64,6 +65,16 @@ static const struct hibvt_pwm_soc hi3519v100_soc_info = { .num_pwms = 8, }; +static const struct hibvt_pwm_soc hi3559v100_shub_soc_info = { + .num_pwms = 8, + .quirk_force_enable = true, +}; + +static const struct hibvt_pwm_soc hi3559v100_soc_info = { + .num_pwms = 2, + .quirk_force_enable = true, +}; + static inline struct hibvt_pwm_chip *to_hibvt_pwm_chip(struct pwm_chip *chip) { return container_of(chip, struct hibvt_pwm_chip, chip); @@ -152,13 +163,23 @@ static void hibvt_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, static int hibvt_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state) { + struct hibvt_pwm_chip *hi_pwm_chip = to_hibvt_pwm_chip(chip); + if (state->polarity != pwm->state.polarity) hibvt_pwm_set_polarity(chip, pwm, state->polarity); if (state->period != pwm->state.period || - state->duty_cycle != pwm->state.duty_cycle) + state->duty_cycle != pwm->state.duty_cycle) { hibvt_pwm_config(chip, pwm, state->duty_cycle, state->period); + /* + * Some implementations require the PWM to be enabled twice + * each time the duty cycle is refreshed. + */ + if (hi_pwm_chip->soc->quirk_force_enable && state->enabled) + hibvt_pwm_enable(chip, pwm); + } + if (state->enabled != pwm->state.enabled) { if (state->enabled) hibvt_pwm_enable(chip, pwm); @@ -259,6 +280,10 @@ static const struct of_device_id hibvt_pwm_of_match[] = { .data = &hi3516cv300_soc_info }, { .compatible = "hisilicon,hi3519v100-pwm", .data = &hi3519v100_soc_info }, + { .compatible = "hisilicon,hi3559v100-shub-pwm", + .data = &hi3559v100_shub_soc_info }, + { .compatible = "hisilicon,hi3559v100-pwm", + .data = &hi3559v100_soc_info }, { } }; MODULE_DEVICE_TABLE(of, hibvt_pwm_of_match); -- cgit v1.2.3 From a87b40615a145f69621bac5dc16360047c51f1d9 Mon Sep 17 00:00:00 2001 From: Jitao Shi Date: Tue, 22 Jan 2019 17:02:43 +0800 Subject: pwm: Add MediaTek MT8183 display PWM driver support Use the mtk_pwm_data struction to define different registers and add MT8183 specific register operations, such as MT8183 doesn't have commit register, needs to disable double buffer before writing register, and needs to select commit mode and use PWM_PERIOD/PWM_HIGH_WIDTH. Signed-off-by: Jitao Shi Signed-off-by: Thierry Reding --- drivers/pwm/pwm-mtk-disp.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/pwm/pwm-mtk-disp.c b/drivers/pwm/pwm-mtk-disp.c index 893940d45f0d..15803c71fe80 100644 --- a/drivers/pwm/pwm-mtk-disp.c +++ b/drivers/pwm/pwm-mtk-disp.c @@ -277,10 +277,21 @@ static const struct mtk_pwm_data mt8173_pwm_data = { .commit_mask = 0x1, }; +static const struct mtk_pwm_data mt8183_pwm_data = { + .enable_mask = BIT(0), + .con0 = 0x18, + .con0_sel = 0x0, + .con1 = 0x1c, + .has_commit = false, + .bls_debug = 0x80, + .bls_debug_mask = 0x3, +}; + static const struct of_device_id mtk_disp_pwm_of_match[] = { { .compatible = "mediatek,mt2701-disp-pwm", .data = &mt2701_pwm_data}, { .compatible = "mediatek,mt6595-disp-pwm", .data = &mt8173_pwm_data}, { .compatible = "mediatek,mt8173-disp-pwm", .data = &mt8173_pwm_data}, + { .compatible = "mediatek,mt8183-disp-pwm", .data = &mt8183_pwm_data}, { } }; MODULE_DEVICE_TABLE(of, mtk_disp_pwm_of_match); -- cgit v1.2.3 From 53784159f6f513dcb5a8f61503312c9c2f57eeb6 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 25 Feb 2019 16:44:33 +0000 Subject: pwm: atmel: Add struct atmel_pwm_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add struct atmel_pwm_data to embed different per controller information. It prepares adding support for another similar controller that needs additional information. At this stage, embed a member of type struct atmel_pwm_registers in it. Signed-off-by: Claudiu Beznea Acked-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-atmel.c | 64 +++++++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 28 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c index 530d7dc5f1b5..7e86a5266eb6 100644 --- a/drivers/pwm/pwm-atmel.c +++ b/drivers/pwm/pwm-atmel.c @@ -65,11 +65,15 @@ struct atmel_pwm_registers { u8 duty_upd; }; +struct atmel_pwm_data { + struct atmel_pwm_registers regs; +}; + struct atmel_pwm_chip { struct pwm_chip chip; struct clk *clk; void __iomem *base; - const struct atmel_pwm_registers *regs; + const struct atmel_pwm_data *data; unsigned int updated_pwms; /* ISR is cleared when read, ensure only one thread does that */ @@ -150,15 +154,15 @@ static void atmel_pwm_update_cdty(struct pwm_chip *chip, struct pwm_device *pwm, struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip); u32 val; - if (atmel_pwm->regs->duty_upd == - atmel_pwm->regs->period_upd) { + if (atmel_pwm->data->regs.duty_upd == + atmel_pwm->data->regs.period_upd) { val = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, PWM_CMR); val &= ~PWM_CMR_UPD_CDTY; atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, PWM_CMR, val); } atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, - atmel_pwm->regs->duty_upd, cdty); + atmel_pwm->data->regs.duty_upd, cdty); } static void atmel_pwm_set_cprd_cdty(struct pwm_chip *chip, @@ -168,9 +172,9 @@ static void atmel_pwm_set_cprd_cdty(struct pwm_chip *chip, struct atmel_pwm_chip *atmel_pwm = to_atmel_pwm_chip(chip); atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, - atmel_pwm->regs->duty, cdty); + atmel_pwm->data->regs.duty, cdty); atmel_pwm_ch_writel(atmel_pwm, pwm->hwpwm, - atmel_pwm->regs->period, cprd); + atmel_pwm->data->regs.period, cprd); } static void atmel_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm, @@ -225,7 +229,7 @@ static int atmel_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, cstate.polarity == state->polarity && cstate.period == state->period) { cprd = atmel_pwm_ch_readl(atmel_pwm, pwm->hwpwm, - atmel_pwm->regs->period); + atmel_pwm->data->regs.period); atmel_pwm_calculate_cdty(state, cprd, &cdty); atmel_pwm_update_cdty(chip, pwm, cdty); return 0; @@ -277,27 +281,31 @@ static const struct pwm_ops atmel_pwm_ops = { .owner = THIS_MODULE, }; -static const struct atmel_pwm_registers atmel_pwm_regs_v1 = { - .period = PWMV1_CPRD, - .period_upd = PWMV1_CUPD, - .duty = PWMV1_CDTY, - .duty_upd = PWMV1_CUPD, +static const struct atmel_pwm_data atmel_pwm_data_v1 = { + .regs = { + .period = PWMV1_CPRD, + .period_upd = PWMV1_CUPD, + .duty = PWMV1_CDTY, + .duty_upd = PWMV1_CUPD, + }, }; -static const struct atmel_pwm_registers atmel_pwm_regs_v2 = { - .period = PWMV2_CPRD, - .period_upd = PWMV2_CPRDUPD, - .duty = PWMV2_CDTY, - .duty_upd = PWMV2_CDTYUPD, +static const struct atmel_pwm_data atmel_pwm_data_v2 = { + .regs = { + .period = PWMV2_CPRD, + .period_upd = PWMV2_CPRDUPD, + .duty = PWMV2_CDTY, + .duty_upd = PWMV2_CDTYUPD, + }, }; static const struct platform_device_id atmel_pwm_devtypes[] = { { .name = "at91sam9rl-pwm", - .driver_data = (kernel_ulong_t)&atmel_pwm_regs_v1, + .driver_data = (kernel_ulong_t)&atmel_pwm_data_v1, }, { .name = "sama5d3-pwm", - .driver_data = (kernel_ulong_t)&atmel_pwm_regs_v2, + .driver_data = (kernel_ulong_t)&atmel_pwm_data_v2, }, { /* sentinel */ }, @@ -307,20 +315,20 @@ MODULE_DEVICE_TABLE(platform, atmel_pwm_devtypes); static const struct of_device_id atmel_pwm_dt_ids[] = { { .compatible = "atmel,at91sam9rl-pwm", - .data = &atmel_pwm_regs_v1, + .data = &atmel_pwm_data_v1, }, { .compatible = "atmel,sama5d3-pwm", - .data = &atmel_pwm_regs_v2, + .data = &atmel_pwm_data_v2, }, { .compatible = "atmel,sama5d2-pwm", - .data = &atmel_pwm_regs_v2, + .data = &atmel_pwm_data_v2, }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, atmel_pwm_dt_ids); -static inline const struct atmel_pwm_registers * +static inline const struct atmel_pwm_data * atmel_pwm_get_driver_data(struct platform_device *pdev) { const struct platform_device_id *id; @@ -330,18 +338,18 @@ atmel_pwm_get_driver_data(struct platform_device *pdev) id = platform_get_device_id(pdev); - return (struct atmel_pwm_registers *)id->driver_data; + return (struct atmel_pwm_data *)id->driver_data; } static int atmel_pwm_probe(struct platform_device *pdev) { - const struct atmel_pwm_registers *regs; + const struct atmel_pwm_data *data; struct atmel_pwm_chip *atmel_pwm; struct resource *res; int ret; - regs = atmel_pwm_get_driver_data(pdev); - if (!regs) + data = atmel_pwm_get_driver_data(pdev); + if (!data) return -ENODEV; atmel_pwm = devm_kzalloc(&pdev->dev, sizeof(*atmel_pwm), GFP_KERNEL); @@ -373,7 +381,7 @@ static int atmel_pwm_probe(struct platform_device *pdev) atmel_pwm->chip.base = -1; atmel_pwm->chip.npwm = 4; - atmel_pwm->regs = regs; + atmel_pwm->data = data; atmel_pwm->updated_pwms = 0; mutex_init(&atmel_pwm->isr_lock); -- cgit v1.2.3 From 0285827d546d9087aadce6d3728dd824e32e3777 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 25 Feb 2019 16:44:37 +0000 Subject: pwm: atmel: Add support for controllers with 32 bit counters SAM9X60's PWM controller uses 32 bits counters thus it could generate signals with higher period and duty cycles than the old ones. Prepare the current driver to be able to work with old controllers (that uses 16 bits counters) and with the new SAM9X60's controller, by providing counters information based on compatible string. Signed-off-by: Claudiu Beznea Signed-off-by: Thierry Reding --- drivers/pwm/pwm-atmel.c | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c index 7e86a5266eb6..647d063562db 100644 --- a/drivers/pwm/pwm-atmel.c +++ b/drivers/pwm/pwm-atmel.c @@ -48,15 +48,11 @@ #define PWMV2_CPRD 0x0C #define PWMV2_CPRDUPD 0x10 -/* - * Max value for duty and period - * - * Although the duty and period register is 32 bit, - * however only the LSB 16 bits are significant. - */ -#define PWM_MAX_DTY 0xFFFF -#define PWM_MAX_PRD 0xFFFF -#define PRD_MAX_PRES 10 +/* Max values for period and prescaler */ + +/* Only the LSB 16 bits are significant. */ +#define PWM_MAXV1_PRD 0xFFFF +#define PRD_MAXV1_PRES 10 struct atmel_pwm_registers { u8 period; @@ -65,8 +61,14 @@ struct atmel_pwm_registers { u8 duty_upd; }; +struct atmel_pwm_config { + u32 max_period; + u32 max_pres; +}; + struct atmel_pwm_data { struct atmel_pwm_registers regs; + struct atmel_pwm_config cfg; }; struct atmel_pwm_chip { @@ -125,10 +127,10 @@ static int atmel_pwm_calculate_cprd_and_pres(struct pwm_chip *chip, cycles *= clk_get_rate(atmel_pwm->clk); do_div(cycles, NSEC_PER_SEC); - for (*pres = 0; cycles > PWM_MAX_PRD; cycles >>= 1) + for (*pres = 0; cycles > atmel_pwm->data->cfg.max_period; cycles >>= 1) (*pres)++; - if (*pres > PRD_MAX_PRES) { + if (*pres > atmel_pwm->data->cfg.max_pres) { dev_err(chip->dev, "pres exceeds the maximum value\n"); return -EINVAL; } @@ -288,6 +290,11 @@ static const struct atmel_pwm_data atmel_pwm_data_v1 = { .duty = PWMV1_CDTY, .duty_upd = PWMV1_CUPD, }, + .cfg = { + /* 16 bits to keep period and duty. */ + .max_period = PWM_MAXV1_PRD, + .max_pres = PRD_MAXV1_PRES, + }, }; static const struct atmel_pwm_data atmel_pwm_data_v2 = { @@ -297,6 +304,11 @@ static const struct atmel_pwm_data atmel_pwm_data_v2 = { .duty = PWMV2_CDTY, .duty_upd = PWMV2_CDTYUPD, }, + .cfg = { + /* 16 bits to keep period and duty. */ + .max_period = PWM_MAXV1_PRD, + .max_pres = PRD_MAXV1_PRES, + }, }; static const struct platform_device_id atmel_pwm_devtypes[] = { -- cgit v1.2.3 From abcbe3733e50aacc16fadce11536ac3c70ba55d2 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 25 Feb 2019 16:44:41 +0000 Subject: pwm: atmel: Rename objects of type atmel_pwm_data Rename objects of type atmel_pwm_data to contain chip name instead of version number. Signed-off-by: Claudiu Beznea Signed-off-by: Thierry Reding --- drivers/pwm/pwm-atmel.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c index 647d063562db..4ac899d8008c 100644 --- a/drivers/pwm/pwm-atmel.c +++ b/drivers/pwm/pwm-atmel.c @@ -283,7 +283,7 @@ static const struct pwm_ops atmel_pwm_ops = { .owner = THIS_MODULE, }; -static const struct atmel_pwm_data atmel_pwm_data_v1 = { +static const struct atmel_pwm_data atmel_sam9rl_pwm_data = { .regs = { .period = PWMV1_CPRD, .period_upd = PWMV1_CUPD, @@ -297,7 +297,7 @@ static const struct atmel_pwm_data atmel_pwm_data_v1 = { }, }; -static const struct atmel_pwm_data atmel_pwm_data_v2 = { +static const struct atmel_pwm_data atmel_sama5_pwm_data = { .regs = { .period = PWMV2_CPRD, .period_upd = PWMV2_CPRDUPD, @@ -314,10 +314,10 @@ static const struct atmel_pwm_data atmel_pwm_data_v2 = { static const struct platform_device_id atmel_pwm_devtypes[] = { { .name = "at91sam9rl-pwm", - .driver_data = (kernel_ulong_t)&atmel_pwm_data_v1, + .driver_data = (kernel_ulong_t)&atmel_sam9rl_pwm_data, }, { .name = "sama5d3-pwm", - .driver_data = (kernel_ulong_t)&atmel_pwm_data_v2, + .driver_data = (kernel_ulong_t)&atmel_sama5_pwm_data, }, { /* sentinel */ }, @@ -327,13 +327,13 @@ MODULE_DEVICE_TABLE(platform, atmel_pwm_devtypes); static const struct of_device_id atmel_pwm_dt_ids[] = { { .compatible = "atmel,at91sam9rl-pwm", - .data = &atmel_pwm_data_v1, + .data = &atmel_sam9rl_pwm_data, }, { .compatible = "atmel,sama5d3-pwm", - .data = &atmel_pwm_data_v2, + .data = &atmel_sama5_pwm_data, }, { .compatible = "atmel,sama5d2-pwm", - .data = &atmel_pwm_data_v2, + .data = &atmel_sama5_pwm_data, }, { /* sentinel */ }, -- cgit v1.2.3 From 74d0c3b2050927f364e3320091f234c108bd845d Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Mon, 25 Feb 2019 16:44:45 +0000 Subject: pwm: atmel: Add support for SAM9X60's PWM controller Add support for SAM9X60's PWM controller. Signed-off-by: Claudiu Beznea Signed-off-by: Thierry Reding --- drivers/pwm/pwm-atmel.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers') diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c index 4ac899d8008c..b1473ed55110 100644 --- a/drivers/pwm/pwm-atmel.c +++ b/drivers/pwm/pwm-atmel.c @@ -52,6 +52,8 @@ /* Only the LSB 16 bits are significant. */ #define PWM_MAXV1_PRD 0xFFFF +/* All 32 bits are significant. */ +#define PWM_MAXV2_PRD 0xFFFFFFFF #define PRD_MAXV1_PRES 10 struct atmel_pwm_registers { @@ -311,6 +313,20 @@ static const struct atmel_pwm_data atmel_sama5_pwm_data = { }, }; +static const struct atmel_pwm_data mchp_sam9x60_pwm_data = { + .regs = { + .period = PWMV1_CPRD, + .period_upd = PWMV1_CUPD, + .duty = PWMV1_CDTY, + .duty_upd = PWMV1_CUPD, + }, + .cfg = { + /* 32 bits to keep period and duty. */ + .max_period = PWM_MAXV2_PRD, + .max_pres = PRD_MAXV1_PRES, + }, +}; + static const struct platform_device_id atmel_pwm_devtypes[] = { { .name = "at91sam9rl-pwm", @@ -334,6 +350,9 @@ static const struct of_device_id atmel_pwm_dt_ids[] = { }, { .compatible = "atmel,sama5d2-pwm", .data = &atmel_sama5_pwm_data, + }, { + .compatible = "microchip,sam9x60-pwm", + .data = &mchp_sam9x60_pwm_data, }, { /* sentinel */ }, -- cgit v1.2.3 From 7f68ce8287d3b25a70455aec18ff678a908d49ee Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 9 Jan 2019 17:19:05 +0900 Subject: pwm: rcar: Add support "atomic" API This patch adds support for "atomic" API. This behavior differs with legacy APIs a little. Legacy APIs: The PWMCNT register will be updated in rcar_pwm_config() even if the PWM state is disabled. Atomic API: The PWMCNT register will be updated in rcar_pwm_apply() only if the PWM state is enabled. Otherwize, if a PWM runs with 30% duty cycles and the pwm_apply_state() is called with state->enabled = 0, ->duty_cycle = 60 and ->period = 100, this is possible to output a 60% duty cycle. Signed-off-by: Yoshihiro Shimoda Signed-off-by: Thierry Reding --- drivers/pwm/pwm-rcar.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) (limited to 'drivers') diff --git a/drivers/pwm/pwm-rcar.c b/drivers/pwm/pwm-rcar.c index a41812fc6f95..e3ab663ff3a7 100644 --- a/drivers/pwm/pwm-rcar.c +++ b/drivers/pwm/pwm-rcar.c @@ -192,12 +192,49 @@ static void rcar_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) rcar_pwm_update(rp, RCAR_PWMCR_EN0, 0, RCAR_PWMCR); } +static int rcar_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + struct rcar_pwm_chip *rp = to_rcar_pwm_chip(chip); + struct pwm_state cur_state; + int div, ret; + + /* This HW/driver only supports normal polarity */ + pwm_get_state(pwm, &cur_state); + if (state->polarity != PWM_POLARITY_NORMAL) + return -ENOTSUPP; + + if (!state->enabled) { + rcar_pwm_disable(chip, pwm); + return 0; + } + + div = rcar_pwm_get_clock_division(rp, state->period); + if (div < 0) + return div; + + rcar_pwm_update(rp, RCAR_PWMCR_SYNC, RCAR_PWMCR_SYNC, RCAR_PWMCR); + + ret = rcar_pwm_set_counter(rp, div, state->duty_cycle, state->period); + if (!ret) + rcar_pwm_set_clock_control(rp, div); + + /* The SYNC should be set to 0 even if rcar_pwm_set_counter failed */ + rcar_pwm_update(rp, RCAR_PWMCR_SYNC, 0, RCAR_PWMCR); + + if (!ret && state->enabled) + ret = rcar_pwm_enable(chip, pwm); + + return ret; +} + static const struct pwm_ops rcar_pwm_ops = { .request = rcar_pwm_request, .free = rcar_pwm_free, .config = rcar_pwm_config, .enable = rcar_pwm_enable, .disable = rcar_pwm_disable, + .apply = rcar_pwm_apply, .owner = THIS_MODULE, }; -- cgit v1.2.3 From 87f50ce981b8ac33734e3b5847f7f0bbfcad856b Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 9 Jan 2019 17:19:06 +0900 Subject: pwm: rcar: Use "atomic" API on rcar_pwm_resume() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To remove legacy API related functions in the future, this patch uses "atomic" related function instead. No change in behavior. Signed-off-by: Yoshihiro Shimoda Acked-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-rcar.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-rcar.c b/drivers/pwm/pwm-rcar.c index e3ab663ff3a7..652a937ef8b0 100644 --- a/drivers/pwm/pwm-rcar.c +++ b/drivers/pwm/pwm-rcar.c @@ -316,18 +316,16 @@ static int rcar_pwm_suspend(struct device *dev) static int rcar_pwm_resume(struct device *dev) { struct pwm_device *pwm = rcar_pwm_dev_to_pwm_dev(dev); + struct pwm_state state; if (!test_bit(PWMF_REQUESTED, &pwm->flags)) return 0; pm_runtime_get_sync(dev); - rcar_pwm_config(pwm->chip, pwm, pwm->state.duty_cycle, - pwm->state.period); - if (pwm_is_enabled(pwm)) - rcar_pwm_enable(pwm->chip, pwm); + pwm_get_state(pwm, &state); - return 0; + return rcar_pwm_apply(pwm->chip, pwm, &state); } #endif /* CONFIG_PM_SLEEP */ static SIMPLE_DEV_PM_OPS(rcar_pwm_pm_ops, rcar_pwm_suspend, rcar_pwm_resume); -- cgit v1.2.3 From 8cc2b970397c59b25ffb22e0e4a86753fa162619 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 9 Jan 2019 17:19:07 +0900 Subject: pwm: rcar: Remove legacy APIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch removes legacy APIs. Since rcar_pwm_{en,dis}able() functions are reused on "atomic" API, this patch changes the arguments of these functions. No change in behavior. Signed-off-by: Yoshihiro Shimoda Acked-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-rcar.c | 44 ++++---------------------------------------- 1 file changed, 4 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-rcar.c b/drivers/pwm/pwm-rcar.c index 652a937ef8b0..1e13fbb41fd1 100644 --- a/drivers/pwm/pwm-rcar.c +++ b/drivers/pwm/pwm-rcar.c @@ -139,39 +139,8 @@ static void rcar_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) pm_runtime_put(chip->dev); } -static int rcar_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, - int duty_ns, int period_ns) +static int rcar_pwm_enable(struct rcar_pwm_chip *rp) { - struct rcar_pwm_chip *rp = to_rcar_pwm_chip(chip); - int div, ret; - - div = rcar_pwm_get_clock_division(rp, period_ns); - if (div < 0) - return div; - - /* - * Let the core driver set pwm->period if disabled and duty_ns == 0. - * But, this driver should prevent to set the new duty_ns if current - * duty_cycle is not set - */ - if (!pwm_is_enabled(pwm) && !duty_ns && !pwm->state.duty_cycle) - return 0; - - rcar_pwm_update(rp, RCAR_PWMCR_SYNC, RCAR_PWMCR_SYNC, RCAR_PWMCR); - - ret = rcar_pwm_set_counter(rp, div, duty_ns, period_ns); - if (!ret) - rcar_pwm_set_clock_control(rp, div); - - /* The SYNC should be set to 0 even if rcar_pwm_set_counter failed */ - rcar_pwm_update(rp, RCAR_PWMCR_SYNC, 0, RCAR_PWMCR); - - return ret; -} - -static int rcar_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) -{ - struct rcar_pwm_chip *rp = to_rcar_pwm_chip(chip); u32 value; /* Don't enable the PWM device if CYC0 or PH0 is 0 */ @@ -185,10 +154,8 @@ static int rcar_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) return 0; } -static void rcar_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) +static void rcar_pwm_disable(struct rcar_pwm_chip *rp) { - struct rcar_pwm_chip *rp = to_rcar_pwm_chip(chip); - rcar_pwm_update(rp, RCAR_PWMCR_EN0, 0, RCAR_PWMCR); } @@ -205,7 +172,7 @@ static int rcar_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, return -ENOTSUPP; if (!state->enabled) { - rcar_pwm_disable(chip, pwm); + rcar_pwm_disable(rp); return 0; } @@ -223,7 +190,7 @@ static int rcar_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, rcar_pwm_update(rp, RCAR_PWMCR_SYNC, 0, RCAR_PWMCR); if (!ret && state->enabled) - ret = rcar_pwm_enable(chip, pwm); + ret = rcar_pwm_enable(rp); return ret; } @@ -231,9 +198,6 @@ static int rcar_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, static const struct pwm_ops rcar_pwm_ops = { .request = rcar_pwm_request, .free = rcar_pwm_free, - .config = rcar_pwm_config, - .enable = rcar_pwm_enable, - .disable = rcar_pwm_disable, .apply = rcar_pwm_apply, .owner = THIS_MODULE, }; -- cgit v1.2.3 From b4f9a7268dbe9d55a6bc8c3123cb518657743b76 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 9 Jan 2019 17:19:08 +0900 Subject: pwm: rcar: Improve calculation of divider MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rcar_pwm_get_clock_division() has a loop to calculate the divider, but the value of div should be calculatable without a loop. So, this patch improves it. This algorithm is suggested by Uwe Kleine-König and Laurent Pinchart. Signed-off-by: Yoshihiro Shimoda Acked-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-rcar.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-rcar.c b/drivers/pwm/pwm-rcar.c index 1e13fbb41fd1..cfe7dd1b448e 100644 --- a/drivers/pwm/pwm-rcar.c +++ b/drivers/pwm/pwm-rcar.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include #include @@ -68,19 +70,15 @@ static void rcar_pwm_update(struct rcar_pwm_chip *rp, u32 mask, u32 data, static int rcar_pwm_get_clock_division(struct rcar_pwm_chip *rp, int period_ns) { unsigned long clk_rate = clk_get_rate(rp->clk); - unsigned long long max; /* max cycle / nanoseconds */ - unsigned int div; + u64 div, tmp; if (clk_rate == 0) return -EINVAL; - for (div = 0; div <= RCAR_PWM_MAX_DIVISION; div++) { - max = (unsigned long long)NSEC_PER_SEC * RCAR_PWM_MAX_CYCLE * - (1 << div); - do_div(max, clk_rate); - if (period_ns <= max) - break; - } + div = (u64)NSEC_PER_SEC * RCAR_PWM_MAX_CYCLE; + tmp = (u64)period_ns * clk_rate + div - 1; + tmp = div64_u64(tmp, div); + div = ilog2(tmp - 1) + 1; return (div <= RCAR_PWM_MAX_DIVISION) ? div : -ERANGE; } -- cgit v1.2.3 From 519ef9b5f23c16dcb3a3a8a890baf7c084974730 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 10 Jan 2019 20:33:53 +0100 Subject: pwm: imx27: Only enable the clocks once in .get_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the function pwm_imx27_get_state() of enables the clocks once unconditionally at the start, a second time if the PWM is enabled and disables unconditionally at the end. Simplify that to enable once at the start and disable conditionally at the end. Signed-off-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx27.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-imx27.c b/drivers/pwm/pwm-imx27.c index 55666cca4cee..806130654211 100644 --- a/drivers/pwm/pwm-imx27.c +++ b/drivers/pwm/pwm-imx27.c @@ -125,14 +125,10 @@ static void pwm_imx27_get_state(struct pwm_chip *chip, val = readl(imx->mmio_base + MX3_PWMCR); - if (val & MX3_PWMCR_EN) { + if (val & MX3_PWMCR_EN) state->enabled = true; - ret = pwm_imx27_clk_prepare_enable(chip); - if (ret) - return; - } else { + else state->enabled = false; - } switch (FIELD_GET(MX3_PWMCR_POUTC, val)) { case MX3_PWMCR_POUTC_NORMAL: @@ -164,7 +160,8 @@ static void pwm_imx27_get_state(struct pwm_chip *chip, state->duty_cycle = 0; } - pwm_imx27_clk_disable_unprepare(chip); + if (!state->enabled) + pwm_imx27_clk_disable_unprepare(chip); } static void pwm_imx27_sw_reset(struct pwm_chip *chip) -- cgit v1.2.3 From 6571d13e449d2d577ff88471b7b8a83dfe17c12c Mon Sep 17 00:00:00 2001 From: Sheetal Tigadoli Date: Thu, 17 Jan 2019 01:11:22 +0530 Subject: pwm: bcm-kona: Update macros to remove braces around numbers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parentheses are not needed around integer literals in macros. Remove them. Signed-off-by: Sheetal Tigadoli Acked-by: Uwe Kleine-König Signed-off-by: Thierry Reding --- drivers/pwm/pwm-bcm-kona.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-bcm-kona.c b/drivers/pwm/pwm-bcm-kona.c index 09a95aeb3a70..81da91df2529 100644 --- a/drivers/pwm/pwm-bcm-kona.c +++ b/drivers/pwm/pwm-bcm-kona.c @@ -45,25 +45,25 @@ * high or low depending on its state at that exact instant. */ -#define PWM_CONTROL_OFFSET (0x00000000) +#define PWM_CONTROL_OFFSET 0x00000000 #define PWM_CONTROL_SMOOTH_SHIFT(chan) (24 + (chan)) #define PWM_CONTROL_TYPE_SHIFT(chan) (16 + (chan)) #define PWM_CONTROL_POLARITY_SHIFT(chan) (8 + (chan)) #define PWM_CONTROL_TRIGGER_SHIFT(chan) (chan) -#define PRESCALE_OFFSET (0x00000004) +#define PRESCALE_OFFSET 0x00000004 #define PRESCALE_SHIFT(chan) ((chan) << 2) #define PRESCALE_MASK(chan) (0x7 << PRESCALE_SHIFT(chan)) -#define PRESCALE_MIN (0x00000000) -#define PRESCALE_MAX (0x00000007) +#define PRESCALE_MIN 0x00000000 +#define PRESCALE_MAX 0x00000007 #define PERIOD_COUNT_OFFSET(chan) (0x00000008 + ((chan) << 3)) -#define PERIOD_COUNT_MIN (0x00000002) -#define PERIOD_COUNT_MAX (0x00ffffff) +#define PERIOD_COUNT_MIN 0x00000002 +#define PERIOD_COUNT_MAX 0x00ffffff #define DUTY_CYCLE_HIGH_OFFSET(chan) (0x0000000c + ((chan) << 3)) -#define DUTY_CYCLE_HIGH_MIN (0x00000000) -#define DUTY_CYCLE_HIGH_MAX (0x00ffffff) +#define DUTY_CYCLE_HIGH_MIN 0x00000000 +#define DUTY_CYCLE_HIGH_MAX 0x00ffffff struct kona_pwmc { struct pwm_chip chip; -- cgit v1.2.3 From d7d96312fe108d0df50898d212770b0a5b2d491e Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 4 Mar 2019 12:10:29 +0100 Subject: pwm: atmel: Remove useless symbolic definitions The values that these symbols define are only assigned to the per-SoC structure where the context is clear, so there's no need for the extra symbolic name. Acked-by: Claudiu Beznea Signed-off-by: Thierry Reding --- drivers/pwm/pwm-atmel.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/pwm/pwm-atmel.c b/drivers/pwm/pwm-atmel.c index b1473ed55110..a9fd6f0d408c 100644 --- a/drivers/pwm/pwm-atmel.c +++ b/drivers/pwm/pwm-atmel.c @@ -48,14 +48,6 @@ #define PWMV2_CPRD 0x0C #define PWMV2_CPRDUPD 0x10 -/* Max values for period and prescaler */ - -/* Only the LSB 16 bits are significant. */ -#define PWM_MAXV1_PRD 0xFFFF -/* All 32 bits are significant. */ -#define PWM_MAXV2_PRD 0xFFFFFFFF -#define PRD_MAXV1_PRES 10 - struct atmel_pwm_registers { u8 period; u8 period_upd; @@ -294,8 +286,8 @@ static const struct atmel_pwm_data atmel_sam9rl_pwm_data = { }, .cfg = { /* 16 bits to keep period and duty. */ - .max_period = PWM_MAXV1_PRD, - .max_pres = PRD_MAXV1_PRES, + .max_period = 0xffff, + .max_pres = 10, }, }; @@ -308,8 +300,8 @@ static const struct atmel_pwm_data atmel_sama5_pwm_data = { }, .cfg = { /* 16 bits to keep period and duty. */ - .max_period = PWM_MAXV1_PRD, - .max_pres = PRD_MAXV1_PRES, + .max_period = 0xffff, + .max_pres = 10, }, }; @@ -322,8 +314,8 @@ static const struct atmel_pwm_data mchp_sam9x60_pwm_data = { }, .cfg = { /* 32 bits to keep period and duty. */ - .max_period = PWM_MAXV2_PRD, - .max_pres = PRD_MAXV1_PRES, + .max_period = 0xffffffff, + .max_pres = 10, }, }; -- cgit v1.2.3