drivers/pwm/pwm-stm32.c

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

File Facts

System
Linux kernel
Corpus path
drivers/pwm/pwm-stm32.c
Extension
.c
Size
25483 bytes
Lines
940
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_breakinput {
	u32 index;
	u32 level;
	u32 filter;
};

struct stm32_pwm {
	struct mutex lock; /* protect pwm config/enable */
	struct clk *clk;
	struct regmap *regmap;
	u32 max_arr;
	bool have_complementary_output;
	struct stm32_breakinput breakinputs[MAX_BREAKINPUT];
	unsigned int num_breakinputs;
	u32 capture[4] ____cacheline_aligned; /* DMA'able buffer */
};

static inline struct stm32_pwm *to_stm32_pwm_dev(struct pwm_chip *chip)
{
	return pwmchip_get_drvdata(chip);
}

static u32 active_channels(struct stm32_pwm *dev)
{
	u32 ccer;

	regmap_read(dev->regmap, TIM_CCER, &ccer);

	return ccer & TIM_CCER_CCXE;
}

struct stm32_pwm_waveform {
	u32 ccer;
	u32 psc;
	u32 arr;
	u32 ccr;
};

static int stm32_pwm_round_waveform_tohw(struct pwm_chip *chip,
					 struct pwm_device *pwm,
					 const struct pwm_waveform *wf,
					 void *_wfhw)
{
	struct stm32_pwm_waveform *wfhw = _wfhw;
	struct stm32_pwm *priv = to_stm32_pwm_dev(chip);
	unsigned int ch = pwm->hwpwm;
	unsigned long rate;
	u64 duty_ticks, offset_ticks;
	int ret;

	if (wf->period_length_ns == 0) {
		*wfhw = (struct stm32_pwm_waveform){
			.ccer = 0,
		};

		return 0;
	}

	ret = clk_enable(priv->clk);
	if (ret)
		return ret;

	wfhw->ccer = TIM_CCER_CCxE(ch + 1);
	if (priv->have_complementary_output)
		wfhw->ccer |= TIM_CCER_CCxNE(ch + 1);

	rate = clk_get_rate(priv->clk);

	if (active_channels(priv) & ~TIM_CCER_CCxE(ch + 1)) {
		u64 arr;

		/*
		 * Other channels are already enabled, so the configured PSC and
		 * ARR must be used for this channel, too.
		 */
		ret = regmap_read(priv->regmap, TIM_PSC, &wfhw->psc);
		if (ret)
			goto out;

		ret = regmap_read(priv->regmap, TIM_ARR, &wfhw->arr);
		if (ret)
			goto out;

		arr = mul_u64_u64_div_u64(wf->period_length_ns, rate,
					  (u64)NSEC_PER_SEC * (wfhw->psc + 1));
		if (arr <= wfhw->arr) {
			/*
			 * requested period is smaller than the currently
			 * configured and unchangable period, report back the smallest
			 * possible period, i.e. the current state and return 1

Annotation

Implementation Notes