mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-10 11:40:19 -04:00
Merge tag 'pwm/for-6.17-rc1-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/ukleinek/linux
Pull pwm fixes from Uwe Kleine-König: "Two fixes for the mediatek and the imx-tpm driver. Both are old (v4.12-rc1 and v5.2-rc1 respectively). The mediatek issue is that both period and duty_cycle were configured to higher values than requested. For most applications the period part is no tragedy, but a PWM that is configured for duty_cycle = 0 should really emit a constant inactive signal. That was noticed by an LED not being completely off in this case (two commits for one fix: a preparatory one and the actual fix in the second one). For the imx-tpm PWM driver the fixed issue is that the first period is quite a bit too long under some circumstances. So it might take up to UINT32_MAX << 7 clock ticks until the PWM starts toggling. With an assumed input clock rate of 166 MHz (completely made up) that's 55 minutes" * tag 'pwm/for-6.17-rc1-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/ukleinek/linux: pwm: imx-tpm: Reset counter if CMOD is 0 pwm: mediatek: Fix duty and period setting pwm: mediatek: Handle hardware enable and clock enable separately
This commit is contained in:
@@ -204,6 +204,15 @@ static int pwm_imx_tpm_apply_hw(struct pwm_chip *chip,
|
||||
val |= FIELD_PREP(PWM_IMX_TPM_SC_PS, p->prescale);
|
||||
writel(val, tpm->base + PWM_IMX_TPM_SC);
|
||||
|
||||
/*
|
||||
* if the counter is disabled (CMOD == 0), programming the new
|
||||
* period length (MOD) will not reset the counter (CNT). If
|
||||
* CNT.COUNT happens to be bigger than the new MOD value then
|
||||
* the counter will end up being reset way too late. Therefore,
|
||||
* manually reset it to 0.
|
||||
*/
|
||||
if (!cmod)
|
||||
writel(0x0, tpm->base + PWM_IMX_TPM_CNT);
|
||||
/*
|
||||
* set period count:
|
||||
* if the PWM is disabled (CMOD[1:0] = 2b00), then MOD register
|
||||
|
||||
@@ -121,6 +121,26 @@ static inline void pwm_mediatek_writel(struct pwm_mediatek_chip *chip,
|
||||
writel(value, chip->regs + chip->soc->reg_offset[num] + offset);
|
||||
}
|
||||
|
||||
static void pwm_mediatek_enable(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
{
|
||||
struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
|
||||
u32 value;
|
||||
|
||||
value = readl(pc->regs);
|
||||
value |= BIT(pwm->hwpwm);
|
||||
writel(value, pc->regs);
|
||||
}
|
||||
|
||||
static void pwm_mediatek_disable(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
{
|
||||
struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
|
||||
u32 value;
|
||||
|
||||
value = readl(pc->regs);
|
||||
value &= ~BIT(pwm->hwpwm);
|
||||
writel(value, pc->regs);
|
||||
}
|
||||
|
||||
static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
int duty_ns, int period_ns)
|
||||
{
|
||||
@@ -150,7 +170,10 @@ static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
do_div(resolution, clk_rate);
|
||||
|
||||
cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000, resolution);
|
||||
while (cnt_period > 8191) {
|
||||
if (!cnt_period)
|
||||
return -EINVAL;
|
||||
|
||||
while (cnt_period > 8192) {
|
||||
resolution *= 2;
|
||||
clkdiv++;
|
||||
cnt_period = DIV_ROUND_CLOSEST_ULL((u64)period_ns * 1000,
|
||||
@@ -173,9 +196,16 @@ static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
}
|
||||
|
||||
cnt_duty = DIV_ROUND_CLOSEST_ULL((u64)duty_ns * 1000, resolution);
|
||||
|
||||
pwm_mediatek_writel(pc, pwm->hwpwm, PWMCON, BIT(15) | clkdiv);
|
||||
pwm_mediatek_writel(pc, pwm->hwpwm, reg_width, cnt_period);
|
||||
pwm_mediatek_writel(pc, pwm->hwpwm, reg_thres, cnt_duty);
|
||||
pwm_mediatek_writel(pc, pwm->hwpwm, reg_width, cnt_period - 1);
|
||||
|
||||
if (cnt_duty) {
|
||||
pwm_mediatek_writel(pc, pwm->hwpwm, reg_thres, cnt_duty - 1);
|
||||
pwm_mediatek_enable(chip, pwm);
|
||||
} else {
|
||||
pwm_mediatek_disable(chip, pwm);
|
||||
}
|
||||
|
||||
out:
|
||||
pwm_mediatek_clk_disable(chip, pwm);
|
||||
@@ -183,35 +213,6 @@ static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pwm_mediatek_enable(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
{
|
||||
struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
|
||||
u32 value;
|
||||
int ret;
|
||||
|
||||
ret = pwm_mediatek_clk_enable(chip, pwm);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
value = readl(pc->regs);
|
||||
value |= BIT(pwm->hwpwm);
|
||||
writel(value, pc->regs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pwm_mediatek_disable(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
{
|
||||
struct pwm_mediatek_chip *pc = to_pwm_mediatek_chip(chip);
|
||||
u32 value;
|
||||
|
||||
value = readl(pc->regs);
|
||||
value &= ~BIT(pwm->hwpwm);
|
||||
writel(value, pc->regs);
|
||||
|
||||
pwm_mediatek_clk_disable(chip, pwm);
|
||||
}
|
||||
|
||||
static int pwm_mediatek_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
const struct pwm_state *state)
|
||||
{
|
||||
@@ -221,8 +222,10 @@ static int pwm_mediatek_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
return -EINVAL;
|
||||
|
||||
if (!state->enabled) {
|
||||
if (pwm->state.enabled)
|
||||
if (pwm->state.enabled) {
|
||||
pwm_mediatek_disable(chip, pwm);
|
||||
pwm_mediatek_clk_disable(chip, pwm);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -232,7 +235,7 @@ static int pwm_mediatek_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
return err;
|
||||
|
||||
if (!pwm->state.enabled)
|
||||
err = pwm_mediatek_enable(chip, pwm);
|
||||
err = pwm_mediatek_clk_enable(chip, pwm);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user