drivers/pwm/pwm-meson.c

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

File Facts

System
Linux kernel
Corpus path
drivers/pwm/pwm-meson.c
Extension
.c
Size
19247 bytes
Lines
693
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 meson_pwm_channel {
	unsigned long rate;
	unsigned int hi;
	unsigned int lo;
	bool constant;
	bool inverted;

	struct clk_mux mux;
	struct clk_divider div;
	struct clk_gate gate;
	struct clk *clk;
};

struct meson_pwm_data {
	const char *const parent_names[MESON_NUM_MUX_PARENTS];
	int (*channels_init)(struct pwm_chip *chip);
	bool has_constant;
	bool has_polarity;
};

struct meson_pwm {
	const struct meson_pwm_data *data;
	struct meson_pwm_channel channels[MESON_NUM_PWMS];
	void __iomem *base;
	/*
	 * Protects register (write) access to the REG_MISC_AB register
	 * that is shared between the two PWMs.
	 */
	spinlock_t lock;
};

static inline struct meson_pwm *to_meson_pwm(struct pwm_chip *chip)
{
	return pwmchip_get_drvdata(chip);
}

static int meson_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
{
	struct meson_pwm *meson = to_meson_pwm(chip);
	struct meson_pwm_channel *channel = &meson->channels[pwm->hwpwm];
	struct device *dev = pwmchip_parent(chip);
	int err;

	err = clk_prepare_enable(channel->clk);
	if (err < 0) {
		dev_err(dev, "failed to enable clock %s: %d\n",
			__clk_get_name(channel->clk), err);
		return err;
	}

	return 0;
}

static void meson_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
{
	struct meson_pwm *meson = to_meson_pwm(chip);
	struct meson_pwm_channel *channel = &meson->channels[pwm->hwpwm];

	clk_disable_unprepare(channel->clk);
}

static int meson_pwm_calc(struct pwm_chip *chip, struct pwm_device *pwm,
			  const struct pwm_state *state)
{
	struct meson_pwm *meson = to_meson_pwm(chip);
	struct meson_pwm_channel *channel = &meson->channels[pwm->hwpwm];
	unsigned int cnt, duty_cnt;
	long fin_freq;
	u64 duty, period, freq;

	duty = state->duty_cycle;
	period = state->period;

	/*
	 * Note this is wrong. The result is an output wave that isn't really
	 * inverted and so is wrongly identified by .get_state as normal.
	 * Fixing this needs some care however as some machines might rely on
	 * this.
	 */
	if (state->polarity == PWM_POLARITY_INVERSED && !meson->data->has_polarity)
		duty = period - duty;

	freq = div64_u64(NSEC_PER_SEC * 0xffffULL, period);
	if (freq > ULONG_MAX)
		freq = ULONG_MAX;

	fin_freq = clk_round_rate(channel->clk, freq);
	if (fin_freq <= 0) {
		dev_err(pwmchip_parent(chip),
			"invalid source clock frequency %llu\n", freq);

Annotation

Implementation Notes