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 <damon.ding@rock-chips.com>
Change-Id: I1c68a22e875712fd7260ba8f24bd8f53cb8aa679
This commit is contained in:
Damon Ding
2023-05-24 09:52:21 +08:00
committed by Tao Huang
parent a8efd060aa
commit 2196ad1ae9

View File

@@ -9,6 +9,7 @@
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -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);