drivers/pwm/pwm-lpc18xx-sct.c

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

File Facts

System
Linux kernel
Corpus path
drivers/pwm/pwm-lpc18xx-sct.c
Extension
.c
Size
13310 bytes
Lines
442
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 lpc18xx_pwm_data {
	unsigned int duty_event;
};

struct lpc18xx_pwm_chip {
	void __iomem *base;
	struct clk *pwm_clk;
	unsigned long clk_rate;
	unsigned int period_ns;
	unsigned int min_period_ns;
	u64 max_period_ns;
	unsigned int period_event;
	unsigned long event_map;
	struct lpc18xx_pwm_data channeldata[LPC18XX_NUM_PWMS];
};

static inline struct lpc18xx_pwm_chip *
to_lpc18xx_pwm_chip(struct pwm_chip *chip)
{
	return pwmchip_get_drvdata(chip);
}

static inline void lpc18xx_pwm_writel(struct lpc18xx_pwm_chip *lpc18xx_pwm,
				      u32 reg, u32 val)
{
	writel(val, lpc18xx_pwm->base + reg);
}

static inline u32 lpc18xx_pwm_readl(struct lpc18xx_pwm_chip *lpc18xx_pwm,
				    u32 reg)
{
	return readl(lpc18xx_pwm->base + reg);
}

static void lpc18xx_pwm_set_conflict_res(struct lpc18xx_pwm_chip *lpc18xx_pwm,
					 struct pwm_device *pwm,
					 enum lpc18xx_pwm_res_action action)
{
	u32 val;

	/*
	 * Simultaneous set and clear may happen on an output, that is the case
	 * when duty_ns == period_ns. LPC18xx SCT allows to set a conflict
	 * resolution action to be taken in such a case.
	 */
	val = lpc18xx_pwm_readl(lpc18xx_pwm, LPC18XX_PWM_RES_BASE);
	val &= ~LPC18XX_PWM_RES_MASK(pwm->hwpwm);
	val |= LPC18XX_PWM_RES(pwm->hwpwm, action);
	lpc18xx_pwm_writel(lpc18xx_pwm, LPC18XX_PWM_RES_BASE, val);
}

static void lpc18xx_pwm_config_period(struct pwm_chip *chip, u64 period_ns)
{
	struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip);
	u32 val;

	/*
	 * With clk_rate < NSEC_PER_SEC this cannot overflow.
	 * With period_ns < max_period_ns this also fits into an u32.
	 * As period_ns >= min_period_ns = DIV_ROUND_UP(NSEC_PER_SEC, lpc18xx_pwm->clk_rate);
	 * we have val >= 1.
	 */
	val = mul_u64_u64_div_u64(period_ns, lpc18xx_pwm->clk_rate, NSEC_PER_SEC);

	lpc18xx_pwm_writel(lpc18xx_pwm,
			   LPC18XX_PWM_MATCH(lpc18xx_pwm->period_event),
			   val - 1);

	lpc18xx_pwm_writel(lpc18xx_pwm,
			   LPC18XX_PWM_MATCHREL(lpc18xx_pwm->period_event),
			   val - 1);
}

static void lpc18xx_pwm_config_duty(struct pwm_chip *chip,
				    struct pwm_device *pwm, u64 duty_ns)
{
	struct lpc18xx_pwm_chip *lpc18xx_pwm = to_lpc18xx_pwm_chip(chip);
	struct lpc18xx_pwm_data *lpc18xx_data = &lpc18xx_pwm->channeldata[pwm->hwpwm];
	u32 val;

	/*
	 * With clk_rate <= NSEC_PER_SEC this cannot overflow.
	 * With duty_ns <= period_ns < max_period_ns this also fits into an u32.
	 */
	val = mul_u64_u64_div_u64(duty_ns, lpc18xx_pwm->clk_rate, NSEC_PER_SEC);

	lpc18xx_pwm_writel(lpc18xx_pwm,
			   LPC18XX_PWM_MATCH(lpc18xx_data->duty_event),
			   val);

Annotation

Implementation Notes