drivers/pwm/pwm-keembay.c

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

File Facts

System
Linux kernel
Corpus path
drivers/pwm/pwm-keembay.c
Extension
.c
Size
6124 bytes
Lines
235
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 keembay_pwm {
	struct device *dev;
	struct clk *clk;
	void __iomem *base;
};

static inline struct keembay_pwm *to_keembay_pwm_dev(struct pwm_chip *chip)
{
	return pwmchip_get_drvdata(chip);
}

static void keembay_clk_unprepare(void *data)
{
	clk_disable_unprepare(data);
}

static int keembay_clk_enable(struct device *dev, struct clk *clk)
{
	int ret;

	ret = clk_prepare_enable(clk);
	if (ret)
		return ret;

	return devm_add_action_or_reset(dev, keembay_clk_unprepare, clk);
}

/*
 * With gcc 10, CONFIG_CC_OPTIMIZE_FOR_SIZE and only "inline" instead of
 * "__always_inline" this fails to compile because the compiler doesn't notice
 * for all valid masks (e.g. KMB_PWM_LEADIN_MASK) that they are ok.
 */
static __always_inline void keembay_pwm_update_bits(struct keembay_pwm *priv, u32 mask,
					   u32 val, u32 offset)
{
	u32 buff = readl(priv->base + offset);

	buff = u32_replace_bits(buff, val, mask);
	writel(buff, priv->base + offset);
}

static void keembay_pwm_enable(struct keembay_pwm *priv, int ch)
{
	keembay_pwm_update_bits(priv, KMB_PWM_EN_BIT, 1,
				KMB_PWM_LEADIN_OFFSET(ch));
}

static void keembay_pwm_disable(struct keembay_pwm *priv, int ch)
{
	keembay_pwm_update_bits(priv, KMB_PWM_EN_BIT, 0,
				KMB_PWM_LEADIN_OFFSET(ch));
}

static int keembay_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
				 struct pwm_state *state)
{
	struct keembay_pwm *priv = to_keembay_pwm_dev(chip);
	unsigned long long high, low;
	unsigned long clk_rate;
	u32 highlow;

	clk_rate = clk_get_rate(priv->clk);

	/* Read channel enabled status */
	highlow = readl(priv->base + KMB_PWM_LEADIN_OFFSET(pwm->hwpwm));
	if (highlow & KMB_PWM_EN_BIT)
		state->enabled = true;
	else
		state->enabled = false;

	/* Read period and duty cycle */
	highlow = readl(priv->base + KMB_PWM_HIGHLOW_OFFSET(pwm->hwpwm));
	low = FIELD_GET(KMB_PWM_LOW_MASK, highlow) * NSEC_PER_SEC;
	high = FIELD_GET(KMB_PWM_HIGH_MASK, highlow) * NSEC_PER_SEC;
	state->duty_cycle = DIV_ROUND_UP_ULL(high, clk_rate);
	state->period = DIV_ROUND_UP_ULL(high + low, clk_rate);
	state->polarity = PWM_POLARITY_NORMAL;

	return 0;
}

static int keembay_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
			     const struct pwm_state *state)
{
	struct keembay_pwm *priv = to_keembay_pwm_dev(chip);
	struct pwm_state current_state;
	unsigned long long div;
	unsigned long clk_rate;
	u32 pwm_count = 0;
	u16 high, low;

Annotation

Implementation Notes