xref: /openbmc/linux/drivers/clk/at91/clk-master.c (revision e26b3006)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2e442d234SBoris BREZILLON /*
3e442d234SBoris BREZILLON  *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
4e442d234SBoris BREZILLON  */
5e442d234SBoris BREZILLON 
6e442d234SBoris BREZILLON #include <linux/clk-provider.h>
7e442d234SBoris BREZILLON #include <linux/clkdev.h>
8e442d234SBoris BREZILLON #include <linux/clk/at91_pmc.h>
9e442d234SBoris BREZILLON #include <linux/of.h>
101bdf0232SBoris Brezillon #include <linux/mfd/syscon.h>
111bdf0232SBoris Brezillon #include <linux/regmap.h>
12e442d234SBoris BREZILLON 
13e442d234SBoris BREZILLON #include "pmc.h"
14e442d234SBoris BREZILLON 
15e442d234SBoris BREZILLON #define MASTER_PRES_MASK	0x7
16e442d234SBoris BREZILLON #define MASTER_PRES_MAX		MASTER_PRES_MASK
17e442d234SBoris BREZILLON #define MASTER_DIV_SHIFT	8
18*e26b3006SEugen Hristev #define MASTER_DIV_MASK		0x7
19e442d234SBoris BREZILLON 
2075c88143SClaudiu Beznea #define PMC_MCR			0x30
2175c88143SClaudiu Beznea #define PMC_MCR_ID_MSK		GENMASK(3, 0)
2275c88143SClaudiu Beznea #define PMC_MCR_CMD		BIT(7)
2375c88143SClaudiu Beznea #define PMC_MCR_DIV		GENMASK(10, 8)
2475c88143SClaudiu Beznea #define PMC_MCR_CSS		GENMASK(20, 16)
2575c88143SClaudiu Beznea #define PMC_MCR_CSS_SHIFT	(16)
2675c88143SClaudiu Beznea #define PMC_MCR_EN		BIT(28)
2775c88143SClaudiu Beznea 
2875c88143SClaudiu Beznea #define PMC_MCR_ID(x)		((x) & PMC_MCR_ID_MSK)
2975c88143SClaudiu Beznea 
3075c88143SClaudiu Beznea #define MASTER_MAX_ID		4
3175c88143SClaudiu Beznea 
32e442d234SBoris BREZILLON #define to_clk_master(hw) container_of(hw, struct clk_master, hw)
33e442d234SBoris BREZILLON 
34e442d234SBoris BREZILLON struct clk_master {
35e442d234SBoris BREZILLON 	struct clk_hw hw;
361bdf0232SBoris Brezillon 	struct regmap *regmap;
3775c88143SClaudiu Beznea 	spinlock_t *lock;
38e442d234SBoris BREZILLON 	const struct clk_master_layout *layout;
39e442d234SBoris BREZILLON 	const struct clk_master_characteristics *characteristics;
4075c88143SClaudiu Beznea 	u32 *mux_table;
41e5be5370SAlexandre Belloni 	u32 mckr;
4275c88143SClaudiu Beznea 	int chg_pid;
4375c88143SClaudiu Beznea 	u8 id;
4475c88143SClaudiu Beznea 	u8 parent;
4575c88143SClaudiu Beznea 	u8 div;
46e442d234SBoris BREZILLON };
47e442d234SBoris BREZILLON 
4875c88143SClaudiu Beznea static inline bool clk_master_ready(struct clk_master *master)
491bdf0232SBoris Brezillon {
5075c88143SClaudiu Beznea 	unsigned int bit = master->id ? AT91_PMC_MCKXRDY : AT91_PMC_MCKRDY;
511bdf0232SBoris Brezillon 	unsigned int status;
521bdf0232SBoris Brezillon 
5375c88143SClaudiu Beznea 	regmap_read(master->regmap, AT91_PMC_SR, &status);
541bdf0232SBoris Brezillon 
5575c88143SClaudiu Beznea 	return !!(status & bit);
561bdf0232SBoris Brezillon }
571bdf0232SBoris Brezillon 
58e442d234SBoris BREZILLON static int clk_master_prepare(struct clk_hw *hw)
59e442d234SBoris BREZILLON {
60e442d234SBoris BREZILLON 	struct clk_master *master = to_clk_master(hw);
61e442d234SBoris BREZILLON 
6275c88143SClaudiu Beznea 	while (!clk_master_ready(master))
6399a81706SAlexandre Belloni 		cpu_relax();
64e442d234SBoris BREZILLON 
65e442d234SBoris BREZILLON 	return 0;
66e442d234SBoris BREZILLON }
67e442d234SBoris BREZILLON 
68e442d234SBoris BREZILLON static int clk_master_is_prepared(struct clk_hw *hw)
69e442d234SBoris BREZILLON {
70e442d234SBoris BREZILLON 	struct clk_master *master = to_clk_master(hw);
71e442d234SBoris BREZILLON 
7275c88143SClaudiu Beznea 	return clk_master_ready(master);
73e442d234SBoris BREZILLON }
74e442d234SBoris BREZILLON 
75e442d234SBoris BREZILLON static unsigned long clk_master_recalc_rate(struct clk_hw *hw,
76e442d234SBoris BREZILLON 					    unsigned long parent_rate)
77e442d234SBoris BREZILLON {
78e442d234SBoris BREZILLON 	u8 pres;
79e442d234SBoris BREZILLON 	u8 div;
80e442d234SBoris BREZILLON 	unsigned long rate = parent_rate;
81e442d234SBoris BREZILLON 	struct clk_master *master = to_clk_master(hw);
82e442d234SBoris BREZILLON 	const struct clk_master_layout *layout = master->layout;
83e442d234SBoris BREZILLON 	const struct clk_master_characteristics *characteristics =
84e442d234SBoris BREZILLON 						master->characteristics;
851bdf0232SBoris Brezillon 	unsigned int mckr;
86e442d234SBoris BREZILLON 
87e5be5370SAlexandre Belloni 	regmap_read(master->regmap, master->layout->offset, &mckr);
881bdf0232SBoris Brezillon 	mckr &= layout->mask;
89e442d234SBoris BREZILLON 
901bdf0232SBoris Brezillon 	pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK;
911bdf0232SBoris Brezillon 	div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
92e442d234SBoris BREZILLON 
93e442d234SBoris BREZILLON 	if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX)
94e442d234SBoris BREZILLON 		rate /= 3;
95e442d234SBoris BREZILLON 	else
96e442d234SBoris BREZILLON 		rate >>= pres;
97e442d234SBoris BREZILLON 
98e442d234SBoris BREZILLON 	rate /= characteristics->divisors[div];
99e442d234SBoris BREZILLON 
100e442d234SBoris BREZILLON 	if (rate < characteristics->output.min)
101e442d234SBoris BREZILLON 		pr_warn("master clk is underclocked");
102e442d234SBoris BREZILLON 	else if (rate > characteristics->output.max)
103e442d234SBoris BREZILLON 		pr_warn("master clk is overclocked");
104e442d234SBoris BREZILLON 
105e442d234SBoris BREZILLON 	return rate;
106e442d234SBoris BREZILLON }
107e442d234SBoris BREZILLON 
108e442d234SBoris BREZILLON static u8 clk_master_get_parent(struct clk_hw *hw)
109e442d234SBoris BREZILLON {
110e442d234SBoris BREZILLON 	struct clk_master *master = to_clk_master(hw);
1111bdf0232SBoris Brezillon 	unsigned int mckr;
112e442d234SBoris BREZILLON 
113e5be5370SAlexandre Belloni 	regmap_read(master->regmap, master->layout->offset, &mckr);
1141bdf0232SBoris Brezillon 
1151bdf0232SBoris Brezillon 	return mckr & AT91_PMC_CSS;
116e442d234SBoris BREZILLON }
117e442d234SBoris BREZILLON 
118e442d234SBoris BREZILLON static const struct clk_ops master_ops = {
119e442d234SBoris BREZILLON 	.prepare = clk_master_prepare,
120e442d234SBoris BREZILLON 	.is_prepared = clk_master_is_prepared,
121e442d234SBoris BREZILLON 	.recalc_rate = clk_master_recalc_rate,
122e442d234SBoris BREZILLON 	.get_parent = clk_master_get_parent,
123e442d234SBoris BREZILLON };
124e442d234SBoris BREZILLON 
125b2e39dc0SAlexandre Belloni struct clk_hw * __init
12699a81706SAlexandre Belloni at91_clk_register_master(struct regmap *regmap,
127e442d234SBoris BREZILLON 		const char *name, int num_parents,
128e442d234SBoris BREZILLON 		const char **parent_names,
129e442d234SBoris BREZILLON 		const struct clk_master_layout *layout,
130e442d234SBoris BREZILLON 		const struct clk_master_characteristics *characteristics)
131e442d234SBoris BREZILLON {
132e442d234SBoris BREZILLON 	struct clk_master *master;
133e442d234SBoris BREZILLON 	struct clk_init_data init;
134f5644f10SStephen Boyd 	struct clk_hw *hw;
135f5644f10SStephen Boyd 	int ret;
136e442d234SBoris BREZILLON 
1371bdf0232SBoris Brezillon 	if (!name || !num_parents || !parent_names)
138e442d234SBoris BREZILLON 		return ERR_PTR(-EINVAL);
139e442d234SBoris BREZILLON 
140e442d234SBoris BREZILLON 	master = kzalloc(sizeof(*master), GFP_KERNEL);
141e442d234SBoris BREZILLON 	if (!master)
142e442d234SBoris BREZILLON 		return ERR_PTR(-ENOMEM);
143e442d234SBoris BREZILLON 
144e442d234SBoris BREZILLON 	init.name = name;
145e442d234SBoris BREZILLON 	init.ops = &master_ops;
146e442d234SBoris BREZILLON 	init.parent_names = parent_names;
147e442d234SBoris BREZILLON 	init.num_parents = num_parents;
148e442d234SBoris BREZILLON 	init.flags = 0;
149e442d234SBoris BREZILLON 
150e442d234SBoris BREZILLON 	master->hw.init = &init;
151e442d234SBoris BREZILLON 	master->layout = layout;
152e442d234SBoris BREZILLON 	master->characteristics = characteristics;
1531bdf0232SBoris Brezillon 	master->regmap = regmap;
154e442d234SBoris BREZILLON 
155f5644f10SStephen Boyd 	hw = &master->hw;
156f5644f10SStephen Boyd 	ret = clk_hw_register(NULL, &master->hw);
157f5644f10SStephen Boyd 	if (ret) {
158e442d234SBoris BREZILLON 		kfree(master);
159f5644f10SStephen Boyd 		hw = ERR_PTR(ret);
160c76a024eSDavid Dueck 	}
161e442d234SBoris BREZILLON 
162f5644f10SStephen Boyd 	return hw;
163e442d234SBoris BREZILLON }
164e442d234SBoris BREZILLON 
16575c88143SClaudiu Beznea static unsigned long
16675c88143SClaudiu Beznea clk_sama7g5_master_recalc_rate(struct clk_hw *hw,
16775c88143SClaudiu Beznea 			       unsigned long parent_rate)
16875c88143SClaudiu Beznea {
16975c88143SClaudiu Beznea 	struct clk_master *master = to_clk_master(hw);
17075c88143SClaudiu Beznea 
17175c88143SClaudiu Beznea 	return DIV_ROUND_CLOSEST_ULL(parent_rate, (1 << master->div));
17275c88143SClaudiu Beznea }
17375c88143SClaudiu Beznea 
17475c88143SClaudiu Beznea static void clk_sama7g5_master_best_diff(struct clk_rate_request *req,
17575c88143SClaudiu Beznea 					 struct clk_hw *parent,
17675c88143SClaudiu Beznea 					 unsigned long parent_rate,
17775c88143SClaudiu Beznea 					 long *best_rate,
17875c88143SClaudiu Beznea 					 long *best_diff,
17975c88143SClaudiu Beznea 					 u32 div)
18075c88143SClaudiu Beznea {
18175c88143SClaudiu Beznea 	unsigned long tmp_rate, tmp_diff;
18275c88143SClaudiu Beznea 
18375c88143SClaudiu Beznea 	if (div == MASTER_PRES_MAX)
18475c88143SClaudiu Beznea 		tmp_rate = parent_rate / 3;
18575c88143SClaudiu Beznea 	else
18675c88143SClaudiu Beznea 		tmp_rate = parent_rate >> div;
18775c88143SClaudiu Beznea 
18875c88143SClaudiu Beznea 	tmp_diff = abs(req->rate - tmp_rate);
18975c88143SClaudiu Beznea 
19075c88143SClaudiu Beznea 	if (*best_diff < 0 || *best_diff >= tmp_diff) {
19175c88143SClaudiu Beznea 		*best_rate = tmp_rate;
19275c88143SClaudiu Beznea 		*best_diff = tmp_diff;
19375c88143SClaudiu Beznea 		req->best_parent_rate = parent_rate;
19475c88143SClaudiu Beznea 		req->best_parent_hw = parent;
19575c88143SClaudiu Beznea 	}
19675c88143SClaudiu Beznea }
19775c88143SClaudiu Beznea 
19875c88143SClaudiu Beznea static int clk_sama7g5_master_determine_rate(struct clk_hw *hw,
19975c88143SClaudiu Beznea 					     struct clk_rate_request *req)
20075c88143SClaudiu Beznea {
20175c88143SClaudiu Beznea 	struct clk_master *master = to_clk_master(hw);
20275c88143SClaudiu Beznea 	struct clk_rate_request req_parent = *req;
20375c88143SClaudiu Beznea 	struct clk_hw *parent;
20475c88143SClaudiu Beznea 	long best_rate = LONG_MIN, best_diff = LONG_MIN;
20575c88143SClaudiu Beznea 	unsigned long parent_rate;
20675c88143SClaudiu Beznea 	unsigned int div, i;
20775c88143SClaudiu Beznea 
20875c88143SClaudiu Beznea 	/* First: check the dividers of MCR. */
20975c88143SClaudiu Beznea 	for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
21075c88143SClaudiu Beznea 		parent = clk_hw_get_parent_by_index(hw, i);
21175c88143SClaudiu Beznea 		if (!parent)
21275c88143SClaudiu Beznea 			continue;
21375c88143SClaudiu Beznea 
21475c88143SClaudiu Beznea 		parent_rate = clk_hw_get_rate(parent);
21575c88143SClaudiu Beznea 		if (!parent_rate)
21675c88143SClaudiu Beznea 			continue;
21775c88143SClaudiu Beznea 
21875c88143SClaudiu Beznea 		for (div = 0; div < MASTER_PRES_MAX + 1; div++) {
21975c88143SClaudiu Beznea 			clk_sama7g5_master_best_diff(req, parent, parent_rate,
22075c88143SClaudiu Beznea 						     &best_rate, &best_diff,
22175c88143SClaudiu Beznea 						     div);
22275c88143SClaudiu Beznea 			if (!best_diff)
22375c88143SClaudiu Beznea 				break;
22475c88143SClaudiu Beznea 		}
22575c88143SClaudiu Beznea 
22675c88143SClaudiu Beznea 		if (!best_diff)
22775c88143SClaudiu Beznea 			break;
22875c88143SClaudiu Beznea 	}
22975c88143SClaudiu Beznea 
23075c88143SClaudiu Beznea 	/* Second: try to request rate form changeable parent. */
23175c88143SClaudiu Beznea 	if (master->chg_pid < 0)
23275c88143SClaudiu Beznea 		goto end;
23375c88143SClaudiu Beznea 
23475c88143SClaudiu Beznea 	parent = clk_hw_get_parent_by_index(hw, master->chg_pid);
23575c88143SClaudiu Beznea 	if (!parent)
23675c88143SClaudiu Beznea 		goto end;
23775c88143SClaudiu Beznea 
23875c88143SClaudiu Beznea 	for (div = 0; div < MASTER_PRES_MAX + 1; div++) {
23975c88143SClaudiu Beznea 		if (div == MASTER_PRES_MAX)
24075c88143SClaudiu Beznea 			req_parent.rate = req->rate * 3;
24175c88143SClaudiu Beznea 		else
24275c88143SClaudiu Beznea 			req_parent.rate = req->rate << div;
24375c88143SClaudiu Beznea 
24475c88143SClaudiu Beznea 		if (__clk_determine_rate(parent, &req_parent))
24575c88143SClaudiu Beznea 			continue;
24675c88143SClaudiu Beznea 
24775c88143SClaudiu Beznea 		clk_sama7g5_master_best_diff(req, parent, req_parent.rate,
24875c88143SClaudiu Beznea 					     &best_rate, &best_diff, div);
24975c88143SClaudiu Beznea 
25075c88143SClaudiu Beznea 		if (!best_diff)
25175c88143SClaudiu Beznea 			break;
25275c88143SClaudiu Beznea 	}
25375c88143SClaudiu Beznea 
25475c88143SClaudiu Beznea end:
25575c88143SClaudiu Beznea 	pr_debug("MCK: %s, best_rate = %ld, parent clk: %s @ %ld\n",
25675c88143SClaudiu Beznea 		 __func__, best_rate,
25775c88143SClaudiu Beznea 		 __clk_get_name((req->best_parent_hw)->clk),
25875c88143SClaudiu Beznea 		req->best_parent_rate);
25975c88143SClaudiu Beznea 
26075c88143SClaudiu Beznea 	if (best_rate < 0)
26175c88143SClaudiu Beznea 		return -EINVAL;
26275c88143SClaudiu Beznea 
26375c88143SClaudiu Beznea 	req->rate = best_rate;
26475c88143SClaudiu Beznea 
26575c88143SClaudiu Beznea 	return 0;
26675c88143SClaudiu Beznea }
26775c88143SClaudiu Beznea 
26875c88143SClaudiu Beznea static u8 clk_sama7g5_master_get_parent(struct clk_hw *hw)
26975c88143SClaudiu Beznea {
27075c88143SClaudiu Beznea 	struct clk_master *master = to_clk_master(hw);
27175c88143SClaudiu Beznea 	unsigned long flags;
27275c88143SClaudiu Beznea 	u8 index;
27375c88143SClaudiu Beznea 
27475c88143SClaudiu Beznea 	spin_lock_irqsave(master->lock, flags);
27575c88143SClaudiu Beznea 	index = clk_mux_val_to_index(&master->hw, master->mux_table, 0,
27675c88143SClaudiu Beznea 				     master->parent);
27775c88143SClaudiu Beznea 	spin_unlock_irqrestore(master->lock, flags);
27875c88143SClaudiu Beznea 
27975c88143SClaudiu Beznea 	return index;
28075c88143SClaudiu Beznea }
28175c88143SClaudiu Beznea 
28275c88143SClaudiu Beznea static int clk_sama7g5_master_set_parent(struct clk_hw *hw, u8 index)
28375c88143SClaudiu Beznea {
28475c88143SClaudiu Beznea 	struct clk_master *master = to_clk_master(hw);
28575c88143SClaudiu Beznea 	unsigned long flags;
28675c88143SClaudiu Beznea 
28775c88143SClaudiu Beznea 	if (index >= clk_hw_get_num_parents(hw))
28875c88143SClaudiu Beznea 		return -EINVAL;
28975c88143SClaudiu Beznea 
29075c88143SClaudiu Beznea 	spin_lock_irqsave(master->lock, flags);
29175c88143SClaudiu Beznea 	master->parent = clk_mux_index_to_val(master->mux_table, 0, index);
29275c88143SClaudiu Beznea 	spin_unlock_irqrestore(master->lock, flags);
29375c88143SClaudiu Beznea 
29475c88143SClaudiu Beznea 	return 0;
29575c88143SClaudiu Beznea }
29675c88143SClaudiu Beznea 
29775c88143SClaudiu Beznea static int clk_sama7g5_master_enable(struct clk_hw *hw)
29875c88143SClaudiu Beznea {
29975c88143SClaudiu Beznea 	struct clk_master *master = to_clk_master(hw);
30075c88143SClaudiu Beznea 	unsigned long flags;
30175c88143SClaudiu Beznea 	unsigned int val, cparent;
30275c88143SClaudiu Beznea 
30375c88143SClaudiu Beznea 	spin_lock_irqsave(master->lock, flags);
30475c88143SClaudiu Beznea 
30575c88143SClaudiu Beznea 	regmap_write(master->regmap, PMC_MCR, PMC_MCR_ID(master->id));
30675c88143SClaudiu Beznea 	regmap_read(master->regmap, PMC_MCR, &val);
30775c88143SClaudiu Beznea 	regmap_update_bits(master->regmap, PMC_MCR,
30875c88143SClaudiu Beznea 			   PMC_MCR_EN | PMC_MCR_CSS | PMC_MCR_DIV |
30975c88143SClaudiu Beznea 			   PMC_MCR_CMD | PMC_MCR_ID_MSK,
31075c88143SClaudiu Beznea 			   PMC_MCR_EN | (master->parent << PMC_MCR_CSS_SHIFT) |
31175c88143SClaudiu Beznea 			   (master->div << MASTER_DIV_SHIFT) |
31275c88143SClaudiu Beznea 			   PMC_MCR_CMD | PMC_MCR_ID(master->id));
31375c88143SClaudiu Beznea 
31475c88143SClaudiu Beznea 	cparent = (val & PMC_MCR_CSS) >> PMC_MCR_CSS_SHIFT;
31575c88143SClaudiu Beznea 
31675c88143SClaudiu Beznea 	/* Wait here only if parent is being changed. */
31775c88143SClaudiu Beznea 	while ((cparent != master->parent) && !clk_master_ready(master))
31875c88143SClaudiu Beznea 		cpu_relax();
31975c88143SClaudiu Beznea 
32075c88143SClaudiu Beznea 	spin_unlock_irqrestore(master->lock, flags);
32175c88143SClaudiu Beznea 
32275c88143SClaudiu Beznea 	return 0;
32375c88143SClaudiu Beznea }
32475c88143SClaudiu Beznea 
32575c88143SClaudiu Beznea static void clk_sama7g5_master_disable(struct clk_hw *hw)
32675c88143SClaudiu Beznea {
32775c88143SClaudiu Beznea 	struct clk_master *master = to_clk_master(hw);
32875c88143SClaudiu Beznea 	unsigned long flags;
32975c88143SClaudiu Beznea 
33075c88143SClaudiu Beznea 	spin_lock_irqsave(master->lock, flags);
33175c88143SClaudiu Beznea 
33275c88143SClaudiu Beznea 	regmap_write(master->regmap, PMC_MCR, master->id);
33375c88143SClaudiu Beznea 	regmap_update_bits(master->regmap, PMC_MCR,
33475c88143SClaudiu Beznea 			   PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID_MSK,
33575c88143SClaudiu Beznea 			   PMC_MCR_CMD | PMC_MCR_ID(master->id));
33675c88143SClaudiu Beznea 
33775c88143SClaudiu Beznea 	spin_unlock_irqrestore(master->lock, flags);
33875c88143SClaudiu Beznea }
33975c88143SClaudiu Beznea 
34075c88143SClaudiu Beznea static int clk_sama7g5_master_is_enabled(struct clk_hw *hw)
34175c88143SClaudiu Beznea {
34275c88143SClaudiu Beznea 	struct clk_master *master = to_clk_master(hw);
34375c88143SClaudiu Beznea 	unsigned long flags;
34475c88143SClaudiu Beznea 	unsigned int val;
34575c88143SClaudiu Beznea 
34675c88143SClaudiu Beznea 	spin_lock_irqsave(master->lock, flags);
34775c88143SClaudiu Beznea 
34875c88143SClaudiu Beznea 	regmap_write(master->regmap, PMC_MCR, master->id);
34975c88143SClaudiu Beznea 	regmap_read(master->regmap, PMC_MCR, &val);
35075c88143SClaudiu Beznea 
35175c88143SClaudiu Beznea 	spin_unlock_irqrestore(master->lock, flags);
35275c88143SClaudiu Beznea 
35375c88143SClaudiu Beznea 	return !!(val & PMC_MCR_EN);
35475c88143SClaudiu Beznea }
35575c88143SClaudiu Beznea 
35675c88143SClaudiu Beznea static int clk_sama7g5_master_set_rate(struct clk_hw *hw, unsigned long rate,
35775c88143SClaudiu Beznea 				       unsigned long parent_rate)
35875c88143SClaudiu Beznea {
35975c88143SClaudiu Beznea 	struct clk_master *master = to_clk_master(hw);
36075c88143SClaudiu Beznea 	unsigned long div, flags;
36175c88143SClaudiu Beznea 
36275c88143SClaudiu Beznea 	div = DIV_ROUND_CLOSEST(parent_rate, rate);
36375c88143SClaudiu Beznea 	if ((div > (1 << (MASTER_PRES_MAX - 1))) || (div & (div - 1)))
36475c88143SClaudiu Beznea 		return -EINVAL;
36575c88143SClaudiu Beznea 
36675c88143SClaudiu Beznea 	if (div == 3)
36775c88143SClaudiu Beznea 		div = MASTER_PRES_MAX;
36875c88143SClaudiu Beznea 	else
36975c88143SClaudiu Beznea 		div = ffs(div) - 1;
37075c88143SClaudiu Beznea 
37175c88143SClaudiu Beznea 	spin_lock_irqsave(master->lock, flags);
37275c88143SClaudiu Beznea 	master->div = div;
37375c88143SClaudiu Beznea 	spin_unlock_irqrestore(master->lock, flags);
37475c88143SClaudiu Beznea 
37575c88143SClaudiu Beznea 	return 0;
37675c88143SClaudiu Beznea }
37775c88143SClaudiu Beznea 
37875c88143SClaudiu Beznea static const struct clk_ops sama7g5_master_ops = {
37975c88143SClaudiu Beznea 	.enable = clk_sama7g5_master_enable,
38075c88143SClaudiu Beznea 	.disable = clk_sama7g5_master_disable,
38175c88143SClaudiu Beznea 	.is_enabled = clk_sama7g5_master_is_enabled,
38275c88143SClaudiu Beznea 	.recalc_rate = clk_sama7g5_master_recalc_rate,
38375c88143SClaudiu Beznea 	.determine_rate = clk_sama7g5_master_determine_rate,
38475c88143SClaudiu Beznea 	.set_rate = clk_sama7g5_master_set_rate,
38575c88143SClaudiu Beznea 	.get_parent = clk_sama7g5_master_get_parent,
38675c88143SClaudiu Beznea 	.set_parent = clk_sama7g5_master_set_parent,
38775c88143SClaudiu Beznea };
38875c88143SClaudiu Beznea 
38975c88143SClaudiu Beznea struct clk_hw * __init
39075c88143SClaudiu Beznea at91_clk_sama7g5_register_master(struct regmap *regmap,
39175c88143SClaudiu Beznea 				 const char *name, int num_parents,
39275c88143SClaudiu Beznea 				 const char **parent_names,
39375c88143SClaudiu Beznea 				 u32 *mux_table,
39475c88143SClaudiu Beznea 				 spinlock_t *lock, u8 id,
39575c88143SClaudiu Beznea 				 bool critical, int chg_pid)
39675c88143SClaudiu Beznea {
39775c88143SClaudiu Beznea 	struct clk_master *master;
39875c88143SClaudiu Beznea 	struct clk_hw *hw;
39975c88143SClaudiu Beznea 	struct clk_init_data init;
40075c88143SClaudiu Beznea 	unsigned long flags;
40175c88143SClaudiu Beznea 	unsigned int val;
40275c88143SClaudiu Beznea 	int ret;
40375c88143SClaudiu Beznea 
40475c88143SClaudiu Beznea 	if (!name || !num_parents || !parent_names || !mux_table ||
40575c88143SClaudiu Beznea 	    !lock || id > MASTER_MAX_ID)
40675c88143SClaudiu Beznea 		return ERR_PTR(-EINVAL);
40775c88143SClaudiu Beznea 
40875c88143SClaudiu Beznea 	master = kzalloc(sizeof(*master), GFP_KERNEL);
40975c88143SClaudiu Beznea 	if (!master)
41075c88143SClaudiu Beznea 		return ERR_PTR(-ENOMEM);
41175c88143SClaudiu Beznea 
41275c88143SClaudiu Beznea 	init.name = name;
41375c88143SClaudiu Beznea 	init.ops = &sama7g5_master_ops;
41475c88143SClaudiu Beznea 	init.parent_names = parent_names;
41575c88143SClaudiu Beznea 	init.num_parents = num_parents;
41675c88143SClaudiu Beznea 	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
41775c88143SClaudiu Beznea 	if (chg_pid >= 0)
41875c88143SClaudiu Beznea 		init.flags |= CLK_SET_RATE_PARENT;
41975c88143SClaudiu Beznea 	if (critical)
42075c88143SClaudiu Beznea 		init.flags |= CLK_IS_CRITICAL;
42175c88143SClaudiu Beznea 
42275c88143SClaudiu Beznea 	master->hw.init = &init;
42375c88143SClaudiu Beznea 	master->regmap = regmap;
42475c88143SClaudiu Beznea 	master->id = id;
42575c88143SClaudiu Beznea 	master->chg_pid = chg_pid;
42675c88143SClaudiu Beznea 	master->lock = lock;
42775c88143SClaudiu Beznea 	master->mux_table = mux_table;
42875c88143SClaudiu Beznea 
42975c88143SClaudiu Beznea 	spin_lock_irqsave(master->lock, flags);
43075c88143SClaudiu Beznea 	regmap_write(master->regmap, PMC_MCR, master->id);
43175c88143SClaudiu Beznea 	regmap_read(master->regmap, PMC_MCR, &val);
43275c88143SClaudiu Beznea 	master->parent = (val & PMC_MCR_CSS) >> PMC_MCR_CSS_SHIFT;
43375c88143SClaudiu Beznea 	master->div = (val & PMC_MCR_DIV) >> MASTER_DIV_SHIFT;
43475c88143SClaudiu Beznea 	spin_unlock_irqrestore(master->lock, flags);
43575c88143SClaudiu Beznea 
43675c88143SClaudiu Beznea 	hw = &master->hw;
43775c88143SClaudiu Beznea 	ret = clk_hw_register(NULL, &master->hw);
43875c88143SClaudiu Beznea 	if (ret) {
43975c88143SClaudiu Beznea 		kfree(master);
44075c88143SClaudiu Beznea 		hw = ERR_PTR(ret);
44175c88143SClaudiu Beznea 	}
44275c88143SClaudiu Beznea 
44375c88143SClaudiu Beznea 	return hw;
44475c88143SClaudiu Beznea }
44575c88143SClaudiu Beznea 
446b2e39dc0SAlexandre Belloni const struct clk_master_layout at91rm9200_master_layout = {
447e442d234SBoris BREZILLON 	.mask = 0x31F,
448e442d234SBoris BREZILLON 	.pres_shift = 2,
449e5be5370SAlexandre Belloni 	.offset = AT91_PMC_MCKR,
450e442d234SBoris BREZILLON };
451e442d234SBoris BREZILLON 
452b2e39dc0SAlexandre Belloni const struct clk_master_layout at91sam9x5_master_layout = {
453e442d234SBoris BREZILLON 	.mask = 0x373,
454e442d234SBoris BREZILLON 	.pres_shift = 4,
455e5be5370SAlexandre Belloni 	.offset = AT91_PMC_MCKR,
456e442d234SBoris BREZILLON };
457