From 2196ad1ae9fdcf75e73072458b6b137fa814610b Mon Sep 17 00:00:00 2001 From: Damon Ding Date: Wed, 24 May 2023 09:52:21 +0800 Subject: [PATCH] pwm: rockchip: disable the pclk dynamic switch if oneshot mode enabled If the pclk of pwm is off, the interrupt will not be generated. Signed-off-by: Damon Ding Change-Id: I1c68a22e875712fd7260ba8f24bd8f53cb8aa679 --- drivers/pwm/pwm-rockchip.c | 73 +++++++++++++++++++++++++++++--------- 1 file changed, 56 insertions(+), 17 deletions(-) diff --git a/drivers/pwm/pwm-rockchip.c b/drivers/pwm/pwm-rockchip.c index e604c5dd8aa5..e2837c45f0d7 100644 --- a/drivers/pwm/pwm-rockchip.c +++ b/drivers/pwm/pwm-rockchip.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -25,7 +26,11 @@ #define PWM_CTRL_OUTPUT_EN (1 << 3) #define PWM_ENABLE (1 << 0) -#define PWM_CONTINUOUS (1 << 1) +#define PWM_MODE_SHIFT 1 +#define PWM_MODE_MASK (0x3 << PWM_MODE_SHIFT) +#define PWM_ONESHOT (0 << PWM_MODE_SHIFT) +#define PWM_CONTINUOUS (1 << PWM_MODE_SHIFT) +#define PWM_CAPTURE (2 << PWM_MODE_SHIFT) #define PWM_DUTY_POSITIVE (1 << 3) #define PWM_DUTY_NEGATIVE (0 << 3) #define PWM_INACTIVE_NEGATIVE (0 << 4) @@ -56,7 +61,7 @@ struct rockchip_pwm_chip { unsigned long clk_rate; bool vop_pwm_en; /* indicate voppwm mirror register state */ bool center_aligned; - bool oneshot; + bool oneshot_en; int channel_id; int irq; }; @@ -93,9 +98,11 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip, u32 val; int ret; - ret = clk_enable(pc->pclk); - if (ret) - return; + if (!pc->oneshot_en) { + ret = clk_enable(pc->pclk); + if (ret) + return; + } tmp = readl_relaxed(pc->base + pc->data->regs.period); tmp *= pc->data->prescaler * NSEC_PER_SEC; @@ -106,6 +113,8 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip, state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, pc->clk_rate); val = readl_relaxed(pc->base + pc->data->regs.ctrl); + if (pc->oneshot_en) + enable_conf &= ~PWM_CONTINUOUS; state->enabled = (val & enable_conf) == enable_conf; if (pc->data->supports_polarity && !(val & PWM_DUTY_POSITIVE)) @@ -113,7 +122,8 @@ static void rockchip_pwm_get_state(struct pwm_chip *chip, else state->polarity = PWM_POLARITY_NORMAL; - clk_disable(pc->pclk); + if (!pc->oneshot_en) + clk_disable(pc->pclk); } static irqreturn_t rockchip_pwm_oneshot_irq(int irq, void *data) @@ -179,13 +189,13 @@ static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, } #ifdef CONFIG_PWM_ROCKCHIP_ONESHOT - if (state->oneshot_count > PWM_ONESHOT_COUNT_MAX) { - pc->oneshot = false; - dev_err(chip->dev, "Oneshot_count value overflow.\n"); - } else if (state->oneshot_count > 0) { + if (state->oneshot_count > 0 && state->oneshot_count <= PWM_ONESHOT_COUNT_MAX) { u32 int_ctrl; - pc->oneshot = true; + pc->oneshot_en = true; + ctrl &= ~PWM_MODE_MASK; + ctrl |= PWM_ONESHOT; + ctrl &= ~PWM_ONESHOT_COUNT_MASK; ctrl |= (state->oneshot_count - 1) << PWM_ONESHOT_COUNT_SHIFT; @@ -195,9 +205,15 @@ static void rockchip_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, } else { u32 int_ctrl; - pc->oneshot = false; + if (state->oneshot_count) + dev_err(chip->dev, "Oneshot_count must be between 1 and 256.\n"); + + pc->oneshot_en = false; + ctrl &= ~PWM_MODE_MASK; ctrl |= PWM_CONTINUOUS; + ctrl &= ~PWM_ONESHOT_COUNT_MASK; + int_ctrl = readl_relaxed(pc->base + PWM_REG_INT_EN(pc->channel_id)); int_ctrl &= ~PWM_CH_INT(pc->channel_id); writel_relaxed(int_ctrl, pc->base + PWM_REG_INT_EN(pc->channel_id)); @@ -257,7 +273,7 @@ static int rockchip_pwm_enable(struct pwm_chip *chip, if (enable) { val |= enable_conf; - if (pc->oneshot) + if (pc->oneshot_en) val &= ~PWM_CONTINUOUS; } else { val &= ~enable_conf; @@ -281,9 +297,11 @@ static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, bool enabled; int ret = 0; - ret = clk_enable(pc->pclk); - if (ret) - return ret; + if (!pc->oneshot_en) { + ret = clk_enable(pc->pclk); + if (ret) + return ret; + } pwm_get_state(pwm, &curstate); enabled = curstate.enabled; @@ -306,7 +324,8 @@ static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, if (state->enabled) ret = pinctrl_select_state(pc->pinctrl, pc->active_state); out: - clk_disable(pc->pclk); + if (!pc->oneshot_en) + clk_disable(pc->pclk); return ret; } @@ -537,7 +556,27 @@ err_clk: static int rockchip_pwm_remove(struct platform_device *pdev) { struct rockchip_pwm_chip *pc = platform_get_drvdata(pdev); + struct pwm_state state; + u32 val; + /* + * For oneshot mode, it is needed to wait for bit PWM_ENABLE + * to 0, which is automatic if all periods have been sent. + */ + pwm_get_state(&pc->chip.pwms[0], &state); + if (state.enabled) { + if (pc->oneshot_en) { + if (readl_poll_timeout(pc->base + pc->data->regs.ctrl, + val, !(val & PWM_ENABLE), 1000, 10 * 1000)) + dev_err(&pdev->dev, "Wait for oneshot to complete failed\n"); + } else { + state.enabled = false; + pwm_apply_state(&pc->chip.pwms[0], &state); + } + } + + if (pc->oneshot_en) + clk_disable(pc->pclk); clk_unprepare(pc->pclk); clk_unprepare(pc->clk);