drivers/clk/clk-sparx5.c

Source file repositories/reference/linux-study-clean/drivers/clk/clk-sparx5.c

File Facts

System
Linux kernel
Corpus path
drivers/clk/clk-sparx5.c
Extension
.c
Size
6551 bytes
Lines
298
Domain
Driver Families
Bucket
drivers/clk
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 s5_hw_clk {
	struct clk_hw hw;
	void __iomem *reg;
};

struct s5_clk_data {
	void __iomem *base;
	struct s5_hw_clk s5_hw[N_CLOCKS];
};

struct s5_pll_conf {
	unsigned long freq;
	u8 div;
	bool rot_ena;
	u8 rot_sel;
	u8 rot_dir;
	u8 pre_div;
};

#define to_s5_pll(hw) container_of(hw, struct s5_hw_clk, hw)

static unsigned long s5_calc_freq(unsigned long parent_rate,
				  const struct s5_pll_conf *conf)
{
	unsigned long rate = parent_rate / conf->div;

	if (conf->rot_ena) {
		int sign = conf->rot_dir ? -1 : 1;
		int divt = sel_rates[conf->rot_sel] * (1 + conf->pre_div);
		int divb = divt + sign;

		rate = mult_frac(rate, divt, divb);
		rate = roundup(rate, 1000);
	}

	return rate;
}

static void s5_search_fractional(unsigned long rate,
				 unsigned long parent_rate,
				 int div,
				 struct s5_pll_conf *conf)
{
	struct s5_pll_conf best;
	ulong cur_offset, best_offset = rate;
	int d, i, j;

	memset(conf, 0, sizeof(*conf));
	conf->div = div;
	conf->rot_ena = 1;	/* Fractional rate */

	for (d = 0; best_offset > 0 && d <= 1 ; d++) {
		conf->rot_dir = !!d;
		for (i = 0; best_offset > 0 && i < MAX_PRE; i++) {
			conf->pre_div = i;
			for (j = 1; best_offset > 0 && j < MAX_SEL; j++) {
				conf->rot_sel = j;
				conf->freq = s5_calc_freq(parent_rate, conf);
				cur_offset = abs(rate - conf->freq);
				if (cur_offset < best_offset) {
					best_offset = cur_offset;
					best = *conf;
				}
			}
		}
	}

	/* Best match */
	*conf = best;
}

static unsigned long s5_calc_params(unsigned long rate,
				    unsigned long parent_rate,
				    struct s5_pll_conf *conf)
{
	if (parent_rate % rate) {
		struct s5_pll_conf alt1, alt2;
		int div;

		div = DIV_ROUND_CLOSEST_ULL(parent_rate, rate);
		s5_search_fractional(rate, parent_rate, div, &alt1);

		/* Straight match? */
		if (alt1.freq == rate) {
			*conf = alt1;
		} else {
			/* Try without rounding divider */
			div = parent_rate / rate;
			if (div != alt1.div) {
				s5_search_fractional(rate, parent_rate, div,

Annotation

Implementation Notes