/* STM32 Low-Power Timer is preceded by a configurable power-of-2 prescaler */ #define STM32_LPTIM_MAX_PRESCALER 128
staticint stm32_pwm_lp_update_allowed(struct stm32_pwm_lp *priv, int channel)
{ int ret;
u32 ccmr1; unsignedlong ccmr;
/* Only one PWM on this LPTIMER: enable, prescaler and reload value can be changed */ if (!priv->num_cc_chans) returntrue;
ret = regmap_read(priv->regmap, STM32_LPTIM_CCMR1, &ccmr1); if (ret) return ret;
ccmr = ccmr1 & (STM32_LPTIM_CC1E | STM32_LPTIM_CC2E);
/* More than one channel enabled: enable, prescaler or ARR value can't be changed */ if (bitmap_weight(&ccmr, sizeof(u32) * BITS_PER_BYTE) > 1) returnfalse;
/* * Only one channel is enabled (or none): check status on the other channel, to * report if enable, prescaler or ARR value can be changed.
*/ if (channel) return !(ccmr1 & STM32_LPTIM_CC1E); else return !(ccmr1 & STM32_LPTIM_CC2E);
}
staticint stm32_pwm_lp_compare_channel_apply(struct stm32_pwm_lp *priv, int channel, bool enable, enum pwm_polarity polarity)
{
u32 ccmr1, val, mask; bool reenable; int ret;
/* No dedicated CC channel: nothing to do */ if (!priv->num_cc_chans) return 0;
ret = regmap_read(priv->regmap, STM32_LPTIM_CCMR1, &ccmr1); if (ret) return ret;
if (channel) { /* Must disable CC channel (CCxE) to modify polarity (CCxP), then re-enable */
reenable = (enable && FIELD_GET(STM32_LPTIM_CC2E, ccmr1)) &&
(polarity != FIELD_GET(STM32_LPTIM_CC2P, ccmr1));
mask = STM32_LPTIM_CC1SEL | STM32_LPTIM_CC1E | STM32_LPTIM_CC1P;
val = FIELD_PREP(STM32_LPTIM_CC1P, polarity);
val |= FIELD_PREP(STM32_LPTIM_CC1E, enable);
}
if (reenable) {
u32 cfgr, presc; unsignedlong rate; unsignedint delay_us;
ret = regmap_update_bits(priv->regmap, STM32_LPTIM_CCMR1,
channel ? STM32_LPTIM_CC2E : STM32_LPTIM_CC1E, 0); if (ret) return ret; /* * After a write to the LPTIM_CCMRx register, a new write operation can only be * performed after a delay of at least (PRESC × 3) clock cycles
*/
ret = regmap_read(priv->regmap, STM32_LPTIM_CFGR, &cfgr); if (ret) return ret;
presc = FIELD_GET(STM32_LPTIM_PRESC, cfgr);
rate = clk_get_rate(priv->clk) >> presc; if (!rate) return -EINVAL;
delay_us = 3 * DIV_ROUND_UP(USEC_PER_SEC, rate);
usleep_range(delay_us, delay_us * 2);
}
if (!state->enabled) { if (cstate.enabled) { /* Disable CC channel if any */
ret = stm32_pwm_lp_compare_channel_apply(priv, pwm->hwpwm, false,
state->polarity); if (ret) return ret;
ret = regmap_write(priv->regmap, pwm->hwpwm ?
STM32_LPTIM_CCR2 : STM32_LPTIM_CMP, 0); if (ret) return ret;
/* Check if the timer can be disabled */
ret = stm32_pwm_lp_update_allowed(priv, pwm->hwpwm); if (ret < 0) return ret;
if (ret) { /* Disable LP timer */
ret = regmap_write(priv->regmap, STM32_LPTIM_CR, 0); if (ret) return ret;
}
/* Calculate the period and prescaler value */
div = (unsignedlonglong)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(pwmchip_parent(chip), "Can't reach %llu ns\n", state->period); return -EINVAL;
}
ret = regmap_read(priv->regmap, STM32_LPTIM_CFGR, &cfgr); if (ret) return ret;
/* * When there are several channels, they share the same prescaler and reload value. * Check if this can be changed, or the values are the same for all channels.
*/ if (!stm32_pwm_lp_update_allowed(priv, pwm->hwpwm)) {
ret = regmap_read(priv->regmap, STM32_LPTIM_ARR, &arr); if (ret) return ret;
if (!cstate.enabled) { /* enable clock to drive PWM counter */
ret = clk_enable(priv->clk); if (ret) return ret;
}
if ((FIELD_GET(STM32_LPTIM_PRESC, cfgr) != presc) ||
((FIELD_GET(STM32_LPTIM_WAVPOL, cfgr) != state->polarity) && !priv->num_cc_chans)) {
val = FIELD_PREP(STM32_LPTIM_PRESC, presc);
mask = STM32_LPTIM_PRESC;
if (!priv->num_cc_chans) { /* * WAVPOL bit is only available when no capature compare channel is used, * e.g. on LPTIMER instances that have only one output channel. CCMR1 is * used otherwise.
*/
val |= FIELD_PREP(STM32_LPTIM_WAVPOL, state->polarity);
mask |= 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;
/* Write CMP/CCRx register and ensure it's been properly written */
ret = regmap_write(priv->regmap, pwm->hwpwm ? STM32_LPTIM_CCR2 : STM32_LPTIM_CMP,
prd - (1 + dty)); if (ret) goto err;
/* ensure ARR and CMP/CCRx registers are properly written */
ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val, pwm->hwpwm ?
(val & STM32_LPTIM_CMP2_ARROK) == STM32_LPTIM_CMP2_ARROK :
(val & STM32_LPTIM_CMPOK_ARROK) == STM32_LPTIM_CMPOK_ARROK,
100, 1000); if (ret) {
dev_err(pwmchip_parent(chip), "ARR/CMP registers write issue\n"); goto err;
}
ret = regmap_write(priv->regmap, STM32_LPTIM_ICR, pwm->hwpwm ?
STM32_LPTIM_CMP2OKCF_ARROKCF : STM32_LPTIM_CMPOKCF_ARROKCF); if (ret) goto err;
ret = stm32_pwm_lp_compare_channel_apply(priv, pwm->hwpwm, true, state->polarity); if (ret) goto err;
if (reenable) { /* Start LP timer in continuous mode */
ret = regmap_set_bits(priv->regmap, STM32_LPTIM_CR,
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);
if (!ddata->num_cc_chans) { /* No dedicated CC channel, so there's only one PWM channel */
npwm = 1;
} else { /* There are dedicated CC channels, each with one PWM output */
npwm = ddata->num_cc_chans;
}
for (i = 0; i < chip->npwm; i++) {
pwm_get_state(&chip->pwms[i], &state); if (state.enabled) {
dev_err(dev, "The consumer didn't stop us (%s)\n",
chip->pwms[i].label); return -EBUSY;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.