mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2025-08-18 01:27:40 +00:00

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>
433 lines
11 KiB
C
433 lines
11 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
||
/*
|
||
* STM32 Low-Power Timer PWM driver
|
||
*
|
||
* Copyright (C) STMicroelectronics 2017
|
||
*
|
||
* Author: Gerald Baeza <gerald.baeza@st.com>
|
||
*
|
||
* Inspired by Gerald Baeza's pwm-stm32 driver
|
||
*/
|
||
|
||
#include <linux/bitfield.h>
|
||
#include <linux/mfd/stm32-lptimer.h>
|
||
#include <linux/module.h>
|
||
#include <linux/of.h>
|
||
#include <linux/pinctrl/consumer.h>
|
||
#include <linux/platform_device.h>
|
||
#include <linux/pwm.h>
|
||
|
||
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)
|
||
{
|
||
return pwmchip_get_drvdata(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 arr, val, mask, cfgr, presc = 0;
|
||
bool reenable;
|
||
int ret;
|
||
|
||
pwm_get_state(pwm, &cstate);
|
||
reenable = !cstate.enabled;
|
||
|
||
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);
|
||
}
|
||
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(pwmchip_parent(chip), "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(pwmchip_parent(chip), "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);
|
||
|
||
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);
|
||
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);
|
||
|
||
return ret;
|
||
}
|
||
|
||
static int stm32_pwm_lp_get_state(struct pwm_chip *chip,
|
||
struct pwm_device *pwm,
|
||
struct pwm_state *state)
|
||
{
|
||
struct stm32_pwm_lp *priv = to_stm32_pwm_lp(chip);
|
||
unsigned long rate = clk_get_rate(priv->clk);
|
||
u32 val, presc, prd, ccmr1;
|
||
bool enabled;
|
||
u64 tmp;
|
||
|
||
regmap_read(priv->regmap, STM32_LPTIM_CR, &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);
|
||
|
||
if (ret)
|
||
return ret;
|
||
}
|
||
|
||
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, 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);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static const struct pwm_ops stm32_pwm_lp_ops = {
|
||
.apply = stm32_pwm_lp_apply,
|
||
.get_state = stm32_pwm_lp_get_state,
|
||
};
|
||
|
||
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;
|
||
|
||
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);
|
||
if (ret < 0)
|
||
return ret;
|
||
|
||
platform_set_drvdata(pdev, chip);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int stm32_pwm_lp_suspend(struct device *dev)
|
||
{
|
||
struct pwm_chip *chip = dev_get_drvdata(dev);
|
||
struct pwm_state state;
|
||
unsigned int i;
|
||
|
||
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;
|
||
}
|
||
}
|
||
|
||
return pinctrl_pm_select_sleep_state(dev);
|
||
}
|
||
|
||
static int stm32_pwm_lp_resume(struct device *dev)
|
||
{
|
||
return pinctrl_pm_select_default_state(dev);
|
||
}
|
||
|
||
static DEFINE_SIMPLE_DEV_PM_OPS(stm32_pwm_lp_pm_ops, stm32_pwm_lp_suspend,
|
||
stm32_pwm_lp_resume);
|
||
|
||
static const struct of_device_id stm32_pwm_lp_of_match[] = {
|
||
{ .compatible = "st,stm32-pwm-lp", },
|
||
{},
|
||
};
|
||
MODULE_DEVICE_TABLE(of, stm32_pwm_lp_of_match);
|
||
|
||
static struct platform_driver stm32_pwm_lp_driver = {
|
||
.probe = stm32_pwm_lp_probe,
|
||
.driver = {
|
||
.name = "stm32-pwm-lp",
|
||
.of_match_table = stm32_pwm_lp_of_match,
|
||
.pm = pm_ptr(&stm32_pwm_lp_pm_ops),
|
||
},
|
||
};
|
||
module_platform_driver(stm32_pwm_lp_driver);
|
||
|
||
MODULE_ALIAS("platform:stm32-pwm-lp");
|
||
MODULE_DESCRIPTION("STMicroelectronics STM32 PWM LP driver");
|
||
MODULE_LICENSE("GPL v2");
|