From e3adc7efe678ba907f99791f5adfee81faea10e6 Mon Sep 17 00:00:00 2001 From: Michal Vokáč Date: Mon, 1 Oct 2018 16:19:46 +0200 Subject: pwm: imx: Sort include files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sort included header files alphabetically. Signed-off-by: Michal Vokáč Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/pwm/pwm-imx.c') diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index 1d5242c9cde0..bcbcac405718 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -5,17 +5,17 @@ * Derived from pxa PWM driver by eric miao */ -#include -#include -#include -#include -#include #include #include +#include #include -#include +#include +#include #include #include +#include +#include +#include /* i.MX1 and i.MX21 share the same PWM function block: */ -- cgit v1.2.3 From 9f617ada9f823dff1944ebcf92ef4a05f5f322b7 Mon Sep 17 00:00:00 2001 From: Michal Vokáč Date: Mon, 1 Oct 2018 16:19:47 +0200 Subject: pwm: imx: Use bitops and bitfield macros to define register values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use existing macros to define register fields instead of manually shifting the bit masks. Also define some more register bits. Signed-off-by: Michal Vokáč Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx.c | 78 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 58 insertions(+), 20 deletions(-) (limited to 'drivers/pwm/pwm-imx.c') diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index bcbcac405718..7a4907b73d7c 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -5,6 +5,8 @@ * Derived from pxa PWM driver by eric miao */ +#include +#include #include #include #include @@ -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,18 +33,53 @@ #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 @@ -142,14 +179,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"); } } @@ -207,13 +244,14 @@ 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) { -- cgit v1.2.3 From bf9b0b1b0b6cd51797ce79b6fa5fc2d1baa2719e Mon Sep 17 00:00:00 2001 From: Michal Vokáč Date: Mon, 1 Oct 2018 16:19:48 +0200 Subject: pwm: imx: Implement get_state() function for hardware readout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement the get_state() function and set the initial state to reflect real state of the hardware. This allows to keep the PWM running if it was enabled in bootloader. It is very similar to the GPIO behavior. GPIO pin set as output in bootloader keep the same setting in Linux unless it is reconfigured. If we find the PWM block enabled we need to prepare and enable its source clock otherwise the clock will be disabled late in the boot as unused. That will leave the PWM in enabled state but with disabled clock. That has a side effect that the PWM output is left at its current level at which the clock was disabled. It is totally non-deterministic and it may be LOW or HIGH. Signed-off-by: Michal Vokáč Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) (limited to 'drivers/pwm/pwm-imx.c') diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index 7a4907b73d7c..6cd3b72fbbc1 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -83,6 +83,9 @@ #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_per; @@ -93,6 +96,55 @@ struct imx_chip { #define to_imx_chip(chip) container_of(chip, struct imx_chip, chip) +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; + + val = readl(imx->mmio_base + MX3_PWMCR); + + if (val & MX3_PWMCR_EN) { + state->enabled = true; + ret = clk_prepare_enable(imx->clk_per); + 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; + } +} + static int imx_pwm_config_v1(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns) { @@ -272,6 +324,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, }; -- cgit v1.2.3 From 9f4c8f9607c3147d291b70c13dd01c738ed41faf Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Wed, 19 Dec 2018 05:24:58 +0000 Subject: pwm: imx: Add ipg clock operation i.MX PWM module's ipg_clk_s is for PWM register access, on most of i.MX SoCs, this ipg_clk_s is from system ipg clock or perclk which is always enabled, but on i.MX7D, the ipg_clk_s is from PWM1_CLK_ROOT which is controlled by CCGR132, that means the CCGR132 MUST be enabled first before accessing PWM registers on i.MX7D. This patch adds ipg clock operation to make sure register access successfully on i.MX7D and it fixes Linux kernel boot up hang during PWM driver probe. Fixes: 4a23e6ee9f69 ("ARM: dts: imx7d-sdb: Restore pwm backlight support") Signed-off-by: Anson Huang Signed-off-by: Thierry Reding --- drivers/pwm/pwm-imx.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 5 deletions(-) (limited to 'drivers/pwm/pwm-imx.c') diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index 6cd3b72fbbc1..55a3a363d5be 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c @@ -87,6 +87,8 @@ #define MX3_PWMPR_MAX 0xfffe struct imx_chip { + struct clk *clk_ipg; + struct clk *clk_per; void __iomem *mmio_base; @@ -96,6 +98,32 @@ 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) { @@ -103,11 +131,15 @@ static void imx_pwm_get_state(struct pwm_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 = clk_prepare_enable(imx->clk_per); + ret = imx_pwm_clk_prepare_enable(chip); if (ret) return; } else { @@ -143,6 +175,8 @@ static void imx_pwm_get_state(struct pwm_chip *chip, } else { state->duty_cycle = 0; } + + imx_pwm_clk_disable_unprepare(chip); } static int imx_pwm_config_v1(struct pwm_chip *chip, @@ -180,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; @@ -200,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) @@ -286,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; @@ -309,7 +343,7 @@ static int imx_pwm_apply_v2(struct pwm_chip *chip, struct pwm_device *pwm, } 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; @@ -367,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", @@ -406,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); } -- cgit v1.2.3