drivers/pwm/pwm-imx-tpm.c

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

File Facts

System
Linux kernel
Corpus path
drivers/pwm/pwm-imx-tpm.c
Extension
.c
Size
12442 bytes
Lines
463
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 imx_tpm_pwm_chip {
	struct clk *clk;
	void __iomem *base;
	struct mutex lock;
	u32 user_count;
	u32 enable_count;
	u32 real_period;
};

struct imx_tpm_pwm_param {
	u8 prescale;
	u32 mod;
	u32 val;
};

static inline struct imx_tpm_pwm_chip *
to_imx_tpm_pwm_chip(struct pwm_chip *chip)
{
	return pwmchip_get_drvdata(chip);
}

/*
 * This function determines for a given pwm_state *state that a consumer
 * might request the pwm_state *real_state that eventually is implemented
 * by the hardware and the necessary register values (in *p) to achieve
 * this.
 */
static int pwm_imx_tpm_round_state(struct pwm_chip *chip,
				   struct imx_tpm_pwm_param *p,
				   struct pwm_state *real_state,
				   const struct pwm_state *state)
{
	struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip);
	u32 rate, prescale, period_count, clock_unit;
	u64 tmp;

	rate = clk_get_rate(tpm->clk);
	tmp = (u64)state->period * rate;
	clock_unit = DIV_ROUND_CLOSEST_ULL(tmp, NSEC_PER_SEC);
	if (clock_unit <= PWM_IMX_TPM_MOD_MOD)
		prescale = 0;
	else
		prescale = ilog2(clock_unit) + 1 - PWM_IMX_TPM_MOD_WIDTH;

	if ((!FIELD_FIT(PWM_IMX_TPM_SC_PS, prescale)))
		return -ERANGE;
	p->prescale = prescale;

	period_count = (clock_unit + ((1 << prescale) >> 1)) >> prescale;
	if (period_count == 0)
		return -EINVAL;
	p->mod = period_count - 1;

	/* calculate real period HW can support */
	tmp = (u64)period_count << prescale;
	tmp *= NSEC_PER_SEC;
	real_state->period = DIV_ROUND_CLOSEST_ULL(tmp, rate);

	/*
	 * if eventually the PWM output is inactive, either
	 * duty cycle is 0 or status is disabled, need to
	 * make sure the output pin is inactive.
	 */
	if (!state->enabled)
		real_state->duty_cycle = 0;
	else
		real_state->duty_cycle = state->duty_cycle;

	tmp = (u64)p->mod * real_state->duty_cycle;
	p->val = DIV64_U64_ROUND_CLOSEST(tmp, real_state->period);

	real_state->polarity = state->polarity;
	real_state->enabled = state->enabled;

	return 0;
}

static int pwm_imx_tpm_get_state(struct pwm_chip *chip,
				 struct pwm_device *pwm,
				 struct pwm_state *state)
{
	struct imx_tpm_pwm_chip *tpm = to_imx_tpm_pwm_chip(chip);
	u32 rate, val, prescale;
	u64 tmp;

	/* get period */
	state->period = tpm->real_period;

	/* get duty cycle */
	rate = clk_get_rate(tpm->clk);

Annotation

Implementation Notes