mirror of https://github.com/torvalds/linux.git
pwm: stm32-lp: Add support for stm32mp25
Add support for STM32MP25 SoC. A new compatible has been added to the dt-bindings. It represents handle new features, registers and bits diversity. It isn't used currently in the driver, as matching is done by retrieving MFD parent data. New dedicated capture/compare channels has been added: e.g. a new compare register for channel 2. Some controls (polarity / cc channel enable) are handled in CCMR register on this new variant (instead of wavepol bit). So, Low-power timer can now have up to two PWM outputs. Use device data from the MFD parent to configure the number of PWM channels e.g. 'npwm'. Update current get_state() and apply() ops to support either: - one PWM channel (as on older revision, or LPTIM5 on STM32MP25) - two PWM channels (e.g. LPTIM1/2/3/4 on STM32MP25 that has the full feature set) Introduce new routines to manage common prescaler, reload register and global enable bit. Signed-off-by: Fabrice Gasnier <fabrice.gasnier@foss.st.com> Acked-by: Uwe Kleine-König <ukleinek@kernel.org> Link: https://lore.kernel.org/r/20250429125133.1574167-5-fabrice.gasnier@foss.st.com Signed-off-by: Lee Jones <lee@kernel.org>
This commit is contained in:
parent
5414bc8c57
commit
3f51b232c1
|
|
@ -20,6 +20,7 @@
|
|||
struct stm32_pwm_lp {
|
||||
struct clk *clk;
|
||||
struct regmap *regmap;
|
||||
unsigned int num_cc_chans;
|
||||
};
|
||||
|
||||
static inline struct stm32_pwm_lp *to_stm32_pwm_lp(struct pwm_chip *chip)
|
||||
|
|
@ -30,13 +31,101 @@ static inline struct stm32_pwm_lp *to_stm32_pwm_lp(struct pwm_chip *chip)
|
|||
/* STM32 Low-Power Timer is preceded by a configurable power-of-2 prescaler */
|
||||
#define STM32_LPTIM_MAX_PRESCALER 128
|
||||
|
||||
static int stm32_pwm_lp_update_allowed(struct stm32_pwm_lp *priv, int channel)
|
||||
{
|
||||
int ret;
|
||||
u32 ccmr1;
|
||||
unsigned long ccmr;
|
||||
|
||||
/* Only one PWM on this LPTIMER: enable, prescaler and reload value can be changed */
|
||||
if (!priv->num_cc_chans)
|
||||
return true;
|
||||
|
||||
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)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
static int 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_CC2SEL | STM32_LPTIM_CC2E | STM32_LPTIM_CC2P;
|
||||
val = FIELD_PREP(STM32_LPTIM_CC2P, polarity);
|
||||
val |= FIELD_PREP(STM32_LPTIM_CC2E, enable);
|
||||
} else {
|
||||
reenable = (enable && FIELD_GET(STM32_LPTIM_CC1E, ccmr1)) &&
|
||||
(polarity != FIELD_GET(STM32_LPTIM_CC1P, 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;
|
||||
unsigned long rate;
|
||||
unsigned int 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);
|
||||
}
|
||||
|
||||
return regmap_update_bits(priv->regmap, STM32_LPTIM_CCMR1, mask, val);
|
||||
}
|
||||
|
||||
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;
|
||||
u32 arr, val, mask, cfgr, presc = 0;
|
||||
bool reenable;
|
||||
int ret;
|
||||
|
||||
|
|
@ -45,10 +134,28 @@ static int stm32_pwm_lp_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
/* disable clock to PWM counter */
|
||||
clk_disable(priv->clk);
|
||||
}
|
||||
|
|
@ -79,6 +186,23 @@ static int stm32_pwm_lp_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
dty = prd * state->duty_cycle;
|
||||
do_div(dty, state->period);
|
||||
|
||||
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 ((FIELD_GET(STM32_LPTIM_PRESC, cfgr) != presc) || (arr != prd - 1))
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (!cstate.enabled) {
|
||||
/* enable clock to drive PWM counter */
|
||||
ret = clk_enable(priv->clk);
|
||||
|
|
@ -86,15 +210,20 @@ static int stm32_pwm_lp_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
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)) {
|
||||
((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_PRESC | STM32_LPTIM_WAVPOL;
|
||||
mask |= STM32_LPTIM_WAVPOL;
|
||||
}
|
||||
|
||||
/* Must disable LP timer to modify CFGR */
|
||||
reenable = true;
|
||||
|
|
@ -120,20 +249,27 @@ static int stm32_pwm_lp_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = regmap_write(priv->regmap, STM32_LPTIM_CMP, prd - (1 + dty));
|
||||
/* 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 CMP & ARR registers are properly written */
|
||||
ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val,
|
||||
/* 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,
|
||||
STM32_LPTIM_CMPOKCF_ARROKCF);
|
||||
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;
|
||||
|
||||
|
|
@ -161,11 +297,22 @@ static int stm32_pwm_lp_get_state(struct pwm_chip *chip,
|
|||
{
|
||||
struct stm32_pwm_lp *priv = to_stm32_pwm_lp(chip);
|
||||
unsigned long rate = clk_get_rate(priv->clk);
|
||||
u32 val, presc, prd;
|
||||
u32 val, presc, prd, ccmr1;
|
||||
bool enabled;
|
||||
u64 tmp;
|
||||
|
||||
regmap_read(priv->regmap, STM32_LPTIM_CR, &val);
|
||||
state->enabled = !!FIELD_GET(STM32_LPTIM_ENABLE, val);
|
||||
enabled = !!FIELD_GET(STM32_LPTIM_ENABLE, val);
|
||||
if (priv->num_cc_chans) {
|
||||
/* There's a CC chan, need to also check if it's enabled */
|
||||
regmap_read(priv->regmap, STM32_LPTIM_CCMR1, &ccmr1);
|
||||
if (pwm->hwpwm)
|
||||
enabled &= !!FIELD_GET(STM32_LPTIM_CC2E, ccmr1);
|
||||
else
|
||||
enabled &= !!FIELD_GET(STM32_LPTIM_CC1E, ccmr1);
|
||||
}
|
||||
state->enabled = enabled;
|
||||
|
||||
/* Keep PWM counter clock refcount in sync with PWM initial state */
|
||||
if (state->enabled) {
|
||||
int ret = clk_enable(priv->clk);
|
||||
|
|
@ -176,14 +323,21 @@ static int stm32_pwm_lp_get_state(struct pwm_chip *chip,
|
|||
|
||||
regmap_read(priv->regmap, STM32_LPTIM_CFGR, &val);
|
||||
presc = FIELD_GET(STM32_LPTIM_PRESC, val);
|
||||
if (priv->num_cc_chans) {
|
||||
if (pwm->hwpwm)
|
||||
state->polarity = FIELD_GET(STM32_LPTIM_CC2P, ccmr1);
|
||||
else
|
||||
state->polarity = FIELD_GET(STM32_LPTIM_CC1P, ccmr1);
|
||||
} else {
|
||||
state->polarity = FIELD_GET(STM32_LPTIM_WAVPOL, val);
|
||||
}
|
||||
|
||||
regmap_read(priv->regmap, STM32_LPTIM_ARR, &prd);
|
||||
tmp = prd + 1;
|
||||
tmp = (tmp << presc) * NSEC_PER_SEC;
|
||||
state->period = DIV_ROUND_CLOSEST_ULL(tmp, rate);
|
||||
|
||||
regmap_read(priv->regmap, STM32_LPTIM_CMP, &val);
|
||||
regmap_read(priv->regmap, pwm->hwpwm ? STM32_LPTIM_CCR2 : STM32_LPTIM_CMP, &val);
|
||||
tmp = prd - val;
|
||||
tmp = (tmp << presc) * NSEC_PER_SEC;
|
||||
state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, rate);
|
||||
|
|
@ -201,15 +355,25 @@ static int stm32_pwm_lp_probe(struct platform_device *pdev)
|
|||
struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
|
||||
struct stm32_pwm_lp *priv;
|
||||
struct pwm_chip *chip;
|
||||
unsigned int npwm;
|
||||
int ret;
|
||||
|
||||
chip = devm_pwmchip_alloc(&pdev->dev, 1, sizeof(*priv));
|
||||
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;
|
||||
}
|
||||
|
||||
chip = devm_pwmchip_alloc(&pdev->dev, npwm, sizeof(*priv));
|
||||
if (IS_ERR(chip))
|
||||
return PTR_ERR(chip);
|
||||
priv = to_stm32_pwm_lp(chip);
|
||||
|
||||
priv->regmap = ddata->regmap;
|
||||
priv->clk = ddata->clk;
|
||||
priv->num_cc_chans = ddata->num_cc_chans;
|
||||
chip->ops = &stm32_pwm_lp_ops;
|
||||
|
||||
ret = devm_pwmchip_add(&pdev->dev, chip);
|
||||
|
|
@ -225,13 +389,16 @@ static int stm32_pwm_lp_suspend(struct device *dev)
|
|||
{
|
||||
struct pwm_chip *chip = dev_get_drvdata(dev);
|
||||
struct pwm_state state;
|
||||
unsigned int i;
|
||||
|
||||
pwm_get_state(&chip->pwms[0], &state);
|
||||
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[0].label);
|
||||
chip->pwms[i].label);
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
|
||||
return pinctrl_pm_select_sleep_state(dev);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue