drivers/pwm/pwm-sprd.c

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

File Facts

System
Linux kernel
Corpus path
drivers/pwm/pwm-sprd.c
Extension
.c
Size
7899 bytes
Lines
297
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 sprd_pwm_chn {
	struct clk_bulk_data clks[SPRD_PWM_CHN_CLKS_NUM];
	u32 clk_rate;
};

struct sprd_pwm_chip {
	void __iomem *base;
	struct sprd_pwm_chn chn[SPRD_PWM_CHN_NUM];
};

static inline struct sprd_pwm_chip* sprd_pwm_from_chip(struct pwm_chip *chip)
{
	return pwmchip_get_drvdata(chip);
}

/*
 * The list of clocks required by PWM channels, and each channel has 2 clocks:
 * enable clock and pwm clock.
 */
static const char * const sprd_pwm_clks[] = {
	"enable0", "pwm0",
	"enable1", "pwm1",
	"enable2", "pwm2",
	"enable3", "pwm3",
};

static u32 sprd_pwm_read(struct sprd_pwm_chip *spc, u32 hwid, u32 reg)
{
	u32 offset = reg + (hwid << SPRD_PWM_REGS_SHIFT);

	return readl_relaxed(spc->base + offset);
}

static void sprd_pwm_write(struct sprd_pwm_chip *spc, u32 hwid,
			   u32 reg, u32 val)
{
	u32 offset = reg + (hwid << SPRD_PWM_REGS_SHIFT);

	writel_relaxed(val, spc->base + offset);
}

static int sprd_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
			      struct pwm_state *state)
{
	struct sprd_pwm_chip *spc = sprd_pwm_from_chip(chip);
	struct sprd_pwm_chn *chn = &spc->chn[pwm->hwpwm];
	u32 val, duty, prescale;
	u64 tmp;
	int ret;

	/*
	 * The clocks to PWM channel has to be enabled first before
	 * reading to the registers.
	 */
	ret = clk_bulk_prepare_enable(SPRD_PWM_CHN_CLKS_NUM, chn->clks);
	if (ret) {
		dev_err(pwmchip_parent(chip), "failed to enable pwm%u clocks\n",
			pwm->hwpwm);
		return ret;
	}

	val = sprd_pwm_read(spc, pwm->hwpwm, SPRD_PWM_ENABLE);
	if (val & SPRD_PWM_ENABLE_BIT)
		state->enabled = true;
	else
		state->enabled = false;

	/*
	 * The hardware provides a counter that is feed by the source clock.
	 * The period length is (PRESCALE + 1) * MOD counter steps.
	 * The duty cycle length is (PRESCALE + 1) * DUTY counter steps.
	 * Thus the period_ns and duty_ns calculation formula should be:
	 * period_ns = NSEC_PER_SEC * (prescale + 1) * mod / clk_rate
	 * duty_ns = NSEC_PER_SEC * (prescale + 1) * duty / clk_rate
	 */
	val = sprd_pwm_read(spc, pwm->hwpwm, SPRD_PWM_PRESCALE);
	prescale = val & SPRD_PWM_PRESCALE_MSK;
	tmp = (prescale + 1) * NSEC_PER_SEC * SPRD_PWM_MOD_MAX;
	state->period = DIV_ROUND_CLOSEST_ULL(tmp, chn->clk_rate);

	val = sprd_pwm_read(spc, pwm->hwpwm, SPRD_PWM_DUTY);
	duty = val & SPRD_PWM_DUTY_MSK;
	tmp = (prescale + 1) * NSEC_PER_SEC * duty;
	state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, chn->clk_rate);
	state->polarity = PWM_POLARITY_NORMAL;

	/* Disable PWM clocks if the PWM channel is not in enable state. */
	if (!state->enabled)
		clk_bulk_disable_unprepare(SPRD_PWM_CHN_CLKS_NUM, chn->clks);

Annotation

Implementation Notes