drivers/pwm/pwm-stm32-lp.c

Source file repositories/reference/linux-study-clean/drivers/pwm/pwm-stm32-lp.c

File Facts

System
Linux kernel
Corpus path
drivers/pwm/pwm-stm32-lp.c
Extension
.c
Size
11484 bytes
Lines
433
Domain
Driver Families
Bucket
drivers/pwm
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

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)

Annotation

Implementation Notes