in pwm-stm32-lp.c [34:158]
static int stm32_pwm_lp_apply(struct pwm_chip *chip, struct pwm_device *pwm,
const struct pwm_state *state)
{
struct stm32_pwm_lp *priv = to_stm32_pwm_lp(chip);
unsigned long long prd, div, dty;
struct pwm_state cstate;
u32 val, mask, cfgr, presc = 0;
bool reenable;
int ret;
pwm_get_state(pwm, &cstate);
reenable = !cstate.enabled;
if (!state->enabled) {
if (cstate.enabled) {
/* Disable LP timer */
ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
if (ret)
return ret;
/* disable clock to PWM counter */
clk_disable(priv->clk);
}
return 0;
}
/* Calculate the period and prescaler value */
div = (unsigned long long)clk_get_rate(priv->clk) * state->period;
do_div(div, NSEC_PER_SEC);
if (!div) {
/* Clock is too slow to achieve requested period. */
dev_dbg(priv->chip.dev, "Can't reach %llu ns\n", state->period);
return -EINVAL;
}
prd = div;
while (div > STM32_LPTIM_MAX_ARR) {
presc++;
if ((1 << presc) > STM32_LPTIM_MAX_PRESCALER) {
dev_err(priv->chip.dev, "max prescaler exceeded\n");
return -EINVAL;
}
div = prd >> presc;
}
prd = div;
/* Calculate the duty cycle */
dty = prd * state->duty_cycle;
do_div(dty, state->period);
if (!cstate.enabled) {
/* enable clock to drive PWM counter */
ret = clk_enable(priv->clk);
if (ret)
return ret;
}
ret = regmap_read(priv->regmap, STM32_LPTIM_CFGR, &cfgr);
if (ret)
goto err;
if ((FIELD_GET(STM32_LPTIM_PRESC, cfgr) != presc) ||
(FIELD_GET(STM32_LPTIM_WAVPOL, cfgr) != state->polarity)) {
val = FIELD_PREP(STM32_LPTIM_PRESC, presc);
val |= FIELD_PREP(STM32_LPTIM_WAVPOL, state->polarity);
mask = STM32_LPTIM_PRESC | STM32_LPTIM_WAVPOL;
/* Must disable LP timer to modify CFGR */
reenable = true;
ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
if (ret)
goto err;
ret = regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask,
val);
if (ret)
goto err;
}
if (reenable) {
/* Must (re)enable LP timer to modify CMP & ARR */
ret = regmap_write(priv->regmap, STM32_LPTIM_CR,
STM32_LPTIM_ENABLE);
if (ret)
goto err;
}
ret = regmap_write(priv->regmap, STM32_LPTIM_ARR, prd - 1);
if (ret)
goto err;
ret = regmap_write(priv->regmap, STM32_LPTIM_CMP, prd - (1 + dty));
if (ret)
goto err;
/* ensure CMP & ARR registers are properly written */
ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val,
(val & STM32_LPTIM_CMPOK_ARROK),
100, 1000);
if (ret) {
dev_err(priv->chip.dev, "ARR/CMP registers write issue\n");
goto err;
}
ret = regmap_write(priv->regmap, STM32_LPTIM_ICR,
STM32_LPTIM_CMPOKCF_ARROKCF);
if (ret)
goto err;
if (reenable) {
/* Start LP timer in continuous mode */
ret = regmap_update_bits(priv->regmap, STM32_LPTIM_CR,
STM32_LPTIM_CNTSTRT,
STM32_LPTIM_CNTSTRT);
if (ret) {
regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
goto err;
}
}
return 0;
err:
if (!cstate.enabled)
clk_disable(priv->clk);
return ret;
}