xref: /openbmc/linux/drivers/clk/at91/clk-master.c (revision 171e502c)
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>
87029db09SClaudiu Beznea #include <linux/clk.h>
9e442d234SBoris BREZILLON #include <linux/clk/at91_pmc.h>
10e442d234SBoris BREZILLON #include <linux/of.h>
111bdf0232SBoris Brezillon #include <linux/mfd/syscon.h>
121bdf0232SBoris Brezillon #include <linux/regmap.h>
13e442d234SBoris BREZILLON 
14e442d234SBoris BREZILLON #include "pmc.h"
15e442d234SBoris BREZILLON 
16e442d234SBoris BREZILLON #define MASTER_PRES_MASK	0x7
17e442d234SBoris BREZILLON #define MASTER_PRES_MAX		MASTER_PRES_MASK
18e442d234SBoris BREZILLON #define MASTER_DIV_SHIFT	8
19e26b3006SEugen Hristev #define MASTER_DIV_MASK		0x7
20e442d234SBoris BREZILLON 
2175c88143SClaudiu Beznea #define PMC_MCR_CSS_SHIFT	(16)
2275c88143SClaudiu Beznea 
2375c88143SClaudiu Beznea #define MASTER_MAX_ID		4
2475c88143SClaudiu Beznea 
25e442d234SBoris BREZILLON #define to_clk_master(hw) container_of(hw, struct clk_master, hw)
26e442d234SBoris BREZILLON 
27e442d234SBoris BREZILLON struct clk_master {
28e442d234SBoris BREZILLON 	struct clk_hw hw;
291bdf0232SBoris Brezillon 	struct regmap *regmap;
3075c88143SClaudiu Beznea 	spinlock_t *lock;
31e442d234SBoris BREZILLON 	const struct clk_master_layout *layout;
32e442d234SBoris BREZILLON 	const struct clk_master_characteristics *characteristics;
3336971566SClaudiu Beznea 	struct at91_clk_pms pms;
3475c88143SClaudiu Beznea 	u32 *mux_table;
35e5be5370SAlexandre Belloni 	u32 mckr;
3675c88143SClaudiu Beznea 	int chg_pid;
3775c88143SClaudiu Beznea 	u8 id;
3875c88143SClaudiu Beznea 	u8 parent;
3975c88143SClaudiu Beznea 	u8 div;
407029db09SClaudiu Beznea 	u32 safe_div;
41e442d234SBoris BREZILLON };
42e442d234SBoris BREZILLON 
437029db09SClaudiu Beznea /* MCK div reference to be used by notifier. */
447029db09SClaudiu Beznea static struct clk_master *master_div;
457029db09SClaudiu Beznea 
clk_master_ready(struct clk_master * master)4675c88143SClaudiu Beznea static inline bool clk_master_ready(struct clk_master *master)
471bdf0232SBoris Brezillon {
4875c88143SClaudiu Beznea 	unsigned int bit = master->id ? AT91_PMC_MCKXRDY : AT91_PMC_MCKRDY;
491bdf0232SBoris Brezillon 	unsigned int status;
501bdf0232SBoris Brezillon 
5175c88143SClaudiu Beznea 	regmap_read(master->regmap, AT91_PMC_SR, &status);
521bdf0232SBoris Brezillon 
5375c88143SClaudiu Beznea 	return !!(status & bit);
541bdf0232SBoris Brezillon }
551bdf0232SBoris Brezillon 
clk_master_prepare(struct clk_hw * hw)56e442d234SBoris BREZILLON static int clk_master_prepare(struct clk_hw *hw)
57e442d234SBoris BREZILLON {
58e442d234SBoris BREZILLON 	struct clk_master *master = to_clk_master(hw);
597a110b91SClaudiu Beznea 	unsigned long flags;
607a110b91SClaudiu Beznea 
617a110b91SClaudiu Beznea 	spin_lock_irqsave(master->lock, flags);
62e442d234SBoris BREZILLON 
6375c88143SClaudiu Beznea 	while (!clk_master_ready(master))
6499a81706SAlexandre Belloni 		cpu_relax();
65e442d234SBoris BREZILLON 
667a110b91SClaudiu Beznea 	spin_unlock_irqrestore(master->lock, flags);
677a110b91SClaudiu Beznea 
68e442d234SBoris BREZILLON 	return 0;
69e442d234SBoris BREZILLON }
70e442d234SBoris BREZILLON 
clk_master_is_prepared(struct clk_hw * hw)71e442d234SBoris BREZILLON static int clk_master_is_prepared(struct clk_hw *hw)
72e442d234SBoris BREZILLON {
73e442d234SBoris BREZILLON 	struct clk_master *master = to_clk_master(hw);
747a110b91SClaudiu Beznea 	unsigned long flags;
757a110b91SClaudiu Beznea 	bool status;
76e442d234SBoris BREZILLON 
777a110b91SClaudiu Beznea 	spin_lock_irqsave(master->lock, flags);
787a110b91SClaudiu Beznea 	status = clk_master_ready(master);
797a110b91SClaudiu Beznea 	spin_unlock_irqrestore(master->lock, flags);
807a110b91SClaudiu Beznea 
817a110b91SClaudiu Beznea 	return status;
82e442d234SBoris BREZILLON }
83e442d234SBoris BREZILLON 
clk_master_div_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)847a110b91SClaudiu Beznea static unsigned long clk_master_div_recalc_rate(struct clk_hw *hw,
85e442d234SBoris BREZILLON 						unsigned long parent_rate)
86e442d234SBoris BREZILLON {
87e442d234SBoris BREZILLON 	u8 div;
887a110b91SClaudiu Beznea 	unsigned long flags, rate = parent_rate;
89e442d234SBoris BREZILLON 	struct clk_master *master = to_clk_master(hw);
90e442d234SBoris BREZILLON 	const struct clk_master_layout *layout = master->layout;
91e442d234SBoris BREZILLON 	const struct clk_master_characteristics *characteristics =
92e442d234SBoris BREZILLON 						master->characteristics;
931bdf0232SBoris Brezillon 	unsigned int mckr;
94e442d234SBoris BREZILLON 
957a110b91SClaudiu Beznea 	spin_lock_irqsave(master->lock, flags);
96e5be5370SAlexandre Belloni 	regmap_read(master->regmap, master->layout->offset, &mckr);
977a110b91SClaudiu Beznea 	spin_unlock_irqrestore(master->lock, flags);
987a110b91SClaudiu Beznea 
991bdf0232SBoris Brezillon 	mckr &= layout->mask;
100e442d234SBoris BREZILLON 
1011bdf0232SBoris Brezillon 	div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
102e442d234SBoris BREZILLON 
103e442d234SBoris BREZILLON 	rate /= characteristics->divisors[div];
104e442d234SBoris BREZILLON 
105e442d234SBoris BREZILLON 	if (rate < characteristics->output.min)
1067a110b91SClaudiu Beznea 		pr_warn("master clk div is underclocked");
107e442d234SBoris BREZILLON 	else if (rate > characteristics->output.max)
1087a110b91SClaudiu Beznea 		pr_warn("master clk div is overclocked");
109e442d234SBoris BREZILLON 
110e442d234SBoris BREZILLON 	return rate;
111e442d234SBoris BREZILLON }
112e442d234SBoris BREZILLON 
clk_master_div_save_context(struct clk_hw * hw)11336971566SClaudiu Beznea static int clk_master_div_save_context(struct clk_hw *hw)
11436971566SClaudiu Beznea {
11536971566SClaudiu Beznea 	struct clk_master *master = to_clk_master(hw);
11636971566SClaudiu Beznea 	struct clk_hw *parent_hw = clk_hw_get_parent(hw);
11736971566SClaudiu Beznea 	unsigned long flags;
11836971566SClaudiu Beznea 	unsigned int mckr, div;
11936971566SClaudiu Beznea 
12036971566SClaudiu Beznea 	spin_lock_irqsave(master->lock, flags);
12136971566SClaudiu Beznea 	regmap_read(master->regmap, master->layout->offset, &mckr);
12236971566SClaudiu Beznea 	spin_unlock_irqrestore(master->lock, flags);
12336971566SClaudiu Beznea 
12436971566SClaudiu Beznea 	mckr &= master->layout->mask;
12536971566SClaudiu Beznea 	div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
12636971566SClaudiu Beznea 	div = master->characteristics->divisors[div];
12736971566SClaudiu Beznea 
12836971566SClaudiu Beznea 	master->pms.parent_rate = clk_hw_get_rate(parent_hw);
12936971566SClaudiu Beznea 	master->pms.rate = DIV_ROUND_CLOSEST(master->pms.parent_rate, div);
13036971566SClaudiu Beznea 
13136971566SClaudiu Beznea 	return 0;
13236971566SClaudiu Beznea }
13336971566SClaudiu Beznea 
clk_master_div_restore_context(struct clk_hw * hw)13436971566SClaudiu Beznea static void clk_master_div_restore_context(struct clk_hw *hw)
13536971566SClaudiu Beznea {
13636971566SClaudiu Beznea 	struct clk_master *master = to_clk_master(hw);
13736971566SClaudiu Beznea 	unsigned long flags;
13836971566SClaudiu Beznea 	unsigned int mckr;
13936971566SClaudiu Beznea 	u8 div;
14036971566SClaudiu Beznea 
14136971566SClaudiu Beznea 	spin_lock_irqsave(master->lock, flags);
14236971566SClaudiu Beznea 	regmap_read(master->regmap, master->layout->offset, &mckr);
14336971566SClaudiu Beznea 	spin_unlock_irqrestore(master->lock, flags);
14436971566SClaudiu Beznea 
14536971566SClaudiu Beznea 	mckr &= master->layout->mask;
14636971566SClaudiu Beznea 	div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
14736971566SClaudiu Beznea 	div = master->characteristics->divisors[div];
14836971566SClaudiu Beznea 
14936971566SClaudiu Beznea 	if (div != DIV_ROUND_CLOSEST(master->pms.parent_rate, master->pms.rate))
15036971566SClaudiu Beznea 		pr_warn("MCKR DIV not configured properly by firmware!\n");
15136971566SClaudiu Beznea }
15236971566SClaudiu Beznea 
1537a110b91SClaudiu Beznea static const struct clk_ops master_div_ops = {
154e442d234SBoris BREZILLON 	.prepare = clk_master_prepare,
155e442d234SBoris BREZILLON 	.is_prepared = clk_master_is_prepared,
1567a110b91SClaudiu Beznea 	.recalc_rate = clk_master_div_recalc_rate,
15736971566SClaudiu Beznea 	.save_context = clk_master_div_save_context,
15836971566SClaudiu Beznea 	.restore_context = clk_master_div_restore_context,
159e442d234SBoris BREZILLON };
160e442d234SBoris BREZILLON 
1617029db09SClaudiu Beznea /* This function must be called with lock acquired. */
clk_master_div_set(struct clk_master * master,unsigned long parent_rate,int div)1627029db09SClaudiu Beznea static int clk_master_div_set(struct clk_master *master,
1637029db09SClaudiu Beznea 			      unsigned long parent_rate, int div)
16475c88143SClaudiu Beznea {
1657a110b91SClaudiu Beznea 	const struct clk_master_characteristics *characteristics =
1667a110b91SClaudiu Beznea 						master->characteristics;
1677029db09SClaudiu Beznea 	unsigned long rate = parent_rate;
1687029db09SClaudiu Beznea 	unsigned int max_div = 0, div_index = 0, max_div_index = 0;
1697029db09SClaudiu Beznea 	unsigned int i, mckr, tmp;
17036971566SClaudiu Beznea 	int ret;
17175c88143SClaudiu Beznea 
1727a110b91SClaudiu Beznea 	for (i = 0; i < ARRAY_SIZE(characteristics->divisors); i++) {
1737a110b91SClaudiu Beznea 		if (!characteristics->divisors[i])
1747a110b91SClaudiu Beznea 			break;
1757a110b91SClaudiu Beznea 
1767029db09SClaudiu Beznea 		if (div == characteristics->divisors[i])
1777029db09SClaudiu Beznea 			div_index = i;
1787029db09SClaudiu Beznea 
1797029db09SClaudiu Beznea 		if (max_div < characteristics->divisors[i]) {
1807029db09SClaudiu Beznea 			max_div = characteristics->divisors[i];
1817029db09SClaudiu Beznea 			max_div_index = i;
18275c88143SClaudiu Beznea 		}
1837a110b91SClaudiu Beznea 	}
1847a110b91SClaudiu Beznea 
1857029db09SClaudiu Beznea 	if (div > max_div)
1867029db09SClaudiu Beznea 		div_index = max_div_index;
1877a110b91SClaudiu Beznea 
18836971566SClaudiu Beznea 	ret = regmap_read(master->regmap, master->layout->offset, &mckr);
18936971566SClaudiu Beznea 	if (ret)
1907029db09SClaudiu Beznea 		return ret;
19136971566SClaudiu Beznea 
192a27748adSClaudiu Beznea 	mckr &= master->layout->mask;
193a27748adSClaudiu Beznea 	tmp = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
1947029db09SClaudiu Beznea 	if (tmp == div_index)
1957029db09SClaudiu Beznea 		return 0;
1967029db09SClaudiu Beznea 
1977029db09SClaudiu Beznea 	rate /= characteristics->divisors[div_index];
1987029db09SClaudiu Beznea 	if (rate < characteristics->output.min)
1997029db09SClaudiu Beznea 		pr_warn("master clk div is underclocked");
2007029db09SClaudiu Beznea 	else if (rate > characteristics->output.max)
2017029db09SClaudiu Beznea 		pr_warn("master clk div is overclocked");
20236971566SClaudiu Beznea 
20336971566SClaudiu Beznea 	mckr &= ~(MASTER_DIV_MASK << MASTER_DIV_SHIFT);
2047029db09SClaudiu Beznea 	mckr |= (div_index << MASTER_DIV_SHIFT);
20536971566SClaudiu Beznea 	ret = regmap_write(master->regmap, master->layout->offset, mckr);
20636971566SClaudiu Beznea 	if (ret)
2077029db09SClaudiu Beznea 		return ret;
20836971566SClaudiu Beznea 
2097a110b91SClaudiu Beznea 	while (!clk_master_ready(master))
2107a110b91SClaudiu Beznea 		cpu_relax();
2117029db09SClaudiu Beznea 
2127029db09SClaudiu Beznea 	master->div = characteristics->divisors[div_index];
2137a110b91SClaudiu Beznea 
2147a110b91SClaudiu Beznea 	return 0;
2157a110b91SClaudiu Beznea }
2167a110b91SClaudiu Beznea 
clk_master_div_recalc_rate_chg(struct clk_hw * hw,unsigned long parent_rate)2177029db09SClaudiu Beznea static unsigned long clk_master_div_recalc_rate_chg(struct clk_hw *hw,
2187029db09SClaudiu Beznea 						    unsigned long parent_rate)
2197a110b91SClaudiu Beznea {
2207a110b91SClaudiu Beznea 	struct clk_master *master = to_clk_master(hw);
2217a110b91SClaudiu Beznea 
2227029db09SClaudiu Beznea 	return DIV_ROUND_CLOSEST_ULL(parent_rate, master->div);
2237a110b91SClaudiu Beznea }
2247a110b91SClaudiu Beznea 
clk_master_div_restore_context_chg(struct clk_hw * hw)22536971566SClaudiu Beznea static void clk_master_div_restore_context_chg(struct clk_hw *hw)
22636971566SClaudiu Beznea {
22736971566SClaudiu Beznea 	struct clk_master *master = to_clk_master(hw);
2287029db09SClaudiu Beznea 	unsigned long flags;
22936971566SClaudiu Beznea 	int ret;
23036971566SClaudiu Beznea 
2317029db09SClaudiu Beznea 	spin_lock_irqsave(master->lock, flags);
2327029db09SClaudiu Beznea 	ret = clk_master_div_set(master, master->pms.parent_rate,
2337029db09SClaudiu Beznea 				 DIV_ROUND_CLOSEST(master->pms.parent_rate,
2347029db09SClaudiu Beznea 						   master->pms.rate));
2357029db09SClaudiu Beznea 	spin_unlock_irqrestore(master->lock, flags);
23636971566SClaudiu Beznea 	if (ret)
23736971566SClaudiu Beznea 		pr_warn("Failed to restore MCK DIV clock\n");
23836971566SClaudiu Beznea }
23936971566SClaudiu Beznea 
2407a110b91SClaudiu Beznea static const struct clk_ops master_div_ops_chg = {
2417a110b91SClaudiu Beznea 	.prepare = clk_master_prepare,
2427a110b91SClaudiu Beznea 	.is_prepared = clk_master_is_prepared,
2437029db09SClaudiu Beznea 	.recalc_rate = clk_master_div_recalc_rate_chg,
24436971566SClaudiu Beznea 	.save_context = clk_master_div_save_context,
24536971566SClaudiu Beznea 	.restore_context = clk_master_div_restore_context_chg,
2467a110b91SClaudiu Beznea };
24775c88143SClaudiu Beznea 
clk_master_div_notifier_fn(struct notifier_block * notifier,unsigned long code,void * data)2487029db09SClaudiu Beznea static int clk_master_div_notifier_fn(struct notifier_block *notifier,
2497029db09SClaudiu Beznea 				      unsigned long code, void *data)
2507029db09SClaudiu Beznea {
2517029db09SClaudiu Beznea 	const struct clk_master_characteristics *characteristics =
2527029db09SClaudiu Beznea 						master_div->characteristics;
2537029db09SClaudiu Beznea 	struct clk_notifier_data *cnd = data;
2547029db09SClaudiu Beznea 	unsigned long flags, new_parent_rate, new_rate;
2557029db09SClaudiu Beznea 	unsigned int mckr, div, new_div = 0;
2567029db09SClaudiu Beznea 	int ret, i;
2577029db09SClaudiu Beznea 	long tmp_diff;
2587029db09SClaudiu Beznea 	long best_diff = -1;
2597029db09SClaudiu Beznea 
2607029db09SClaudiu Beznea 	spin_lock_irqsave(master_div->lock, flags);
2617029db09SClaudiu Beznea 	switch (code) {
2627029db09SClaudiu Beznea 	case PRE_RATE_CHANGE:
2637029db09SClaudiu Beznea 		/*
2647029db09SClaudiu Beznea 		 * We want to avoid any overclocking of MCK DIV domain. To do
2657029db09SClaudiu Beznea 		 * this we set a safe divider (the underclocking is not of
2667029db09SClaudiu Beznea 		 * interest as we can go as low as 32KHz). The relation
2677029db09SClaudiu Beznea 		 * b/w this clock and its parents are as follows:
2687029db09SClaudiu Beznea 		 *
2697029db09SClaudiu Beznea 		 * FRAC PLL -> DIV PLL -> MCK DIV
2707029db09SClaudiu Beznea 		 *
2717029db09SClaudiu Beznea 		 * With the proper safe divider we should be good even with FRAC
2727029db09SClaudiu Beznea 		 * PLL at its maximum value.
2737029db09SClaudiu Beznea 		 */
2747029db09SClaudiu Beznea 		ret = regmap_read(master_div->regmap, master_div->layout->offset,
2757029db09SClaudiu Beznea 				  &mckr);
2767029db09SClaudiu Beznea 		if (ret) {
2777029db09SClaudiu Beznea 			ret = NOTIFY_STOP_MASK;
2787029db09SClaudiu Beznea 			goto unlock;
2797029db09SClaudiu Beznea 		}
2807029db09SClaudiu Beznea 
2817029db09SClaudiu Beznea 		mckr &= master_div->layout->mask;
2827029db09SClaudiu Beznea 		div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
2837029db09SClaudiu Beznea 
2847029db09SClaudiu Beznea 		/* Switch to safe divider. */
2857029db09SClaudiu Beznea 		clk_master_div_set(master_div,
2867029db09SClaudiu Beznea 				   cnd->old_rate * characteristics->divisors[div],
2877029db09SClaudiu Beznea 				   master_div->safe_div);
2887029db09SClaudiu Beznea 		break;
2897029db09SClaudiu Beznea 
2907029db09SClaudiu Beznea 	case POST_RATE_CHANGE:
2917029db09SClaudiu Beznea 		/*
2927029db09SClaudiu Beznea 		 * At this point we want to restore MCK DIV domain to its maximum
2937029db09SClaudiu Beznea 		 * allowed rate.
2947029db09SClaudiu Beznea 		 */
2957029db09SClaudiu Beznea 		ret = regmap_read(master_div->regmap, master_div->layout->offset,
2967029db09SClaudiu Beznea 				  &mckr);
2977029db09SClaudiu Beznea 		if (ret) {
2987029db09SClaudiu Beznea 			ret = NOTIFY_STOP_MASK;
2997029db09SClaudiu Beznea 			goto unlock;
3007029db09SClaudiu Beznea 		}
3017029db09SClaudiu Beznea 
3027029db09SClaudiu Beznea 		mckr &= master_div->layout->mask;
3037029db09SClaudiu Beznea 		div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
3047029db09SClaudiu Beznea 		new_parent_rate = cnd->new_rate * characteristics->divisors[div];
3057029db09SClaudiu Beznea 
3067029db09SClaudiu Beznea 		for (i = 0; i < ARRAY_SIZE(characteristics->divisors); i++) {
3077029db09SClaudiu Beznea 			if (!characteristics->divisors[i])
3087029db09SClaudiu Beznea 				break;
3097029db09SClaudiu Beznea 
3107029db09SClaudiu Beznea 			new_rate = DIV_ROUND_CLOSEST_ULL(new_parent_rate,
3117029db09SClaudiu Beznea 							 characteristics->divisors[i]);
3127029db09SClaudiu Beznea 
3137029db09SClaudiu Beznea 			tmp_diff = characteristics->output.max - new_rate;
3147029db09SClaudiu Beznea 			if (tmp_diff < 0)
3157029db09SClaudiu Beznea 				continue;
3167029db09SClaudiu Beznea 
3177029db09SClaudiu Beznea 			if (best_diff < 0 || best_diff > tmp_diff) {
3187029db09SClaudiu Beznea 				new_div = characteristics->divisors[i];
3197029db09SClaudiu Beznea 				best_diff = tmp_diff;
3207029db09SClaudiu Beznea 			}
3217029db09SClaudiu Beznea 
3227029db09SClaudiu Beznea 			if (!tmp_diff)
3237029db09SClaudiu Beznea 				break;
3247029db09SClaudiu Beznea 		}
3257029db09SClaudiu Beznea 
3267029db09SClaudiu Beznea 		if (!new_div) {
3277029db09SClaudiu Beznea 			ret = NOTIFY_STOP_MASK;
3287029db09SClaudiu Beznea 			goto unlock;
3297029db09SClaudiu Beznea 		}
3307029db09SClaudiu Beznea 
3317029db09SClaudiu Beznea 		/* Update the div to preserve MCK DIV clock rate. */
3327029db09SClaudiu Beznea 		clk_master_div_set(master_div, new_parent_rate,
3337029db09SClaudiu Beznea 				   new_div);
3347029db09SClaudiu Beznea 
3357029db09SClaudiu Beznea 		ret = NOTIFY_OK;
3367029db09SClaudiu Beznea 		break;
3377029db09SClaudiu Beznea 
3387029db09SClaudiu Beznea 	default:
3397029db09SClaudiu Beznea 		ret = NOTIFY_DONE;
3407029db09SClaudiu Beznea 		break;
3417029db09SClaudiu Beznea 	}
3427029db09SClaudiu Beznea 
3437029db09SClaudiu Beznea unlock:
3447029db09SClaudiu Beznea 	spin_unlock_irqrestore(master_div->lock, flags);
3457029db09SClaudiu Beznea 
3467029db09SClaudiu Beznea 	return ret;
3477029db09SClaudiu Beznea }
3487029db09SClaudiu Beznea 
3497029db09SClaudiu Beznea static struct notifier_block clk_master_div_notifier = {
3507029db09SClaudiu Beznea 	.notifier_call = clk_master_div_notifier_fn,
3517029db09SClaudiu Beznea };
3527029db09SClaudiu Beznea 
clk_sama7g5_master_best_diff(struct clk_rate_request * req,struct clk_hw * parent,unsigned long parent_rate,long * best_rate,long * best_diff,u32 div)35375c88143SClaudiu Beznea static void clk_sama7g5_master_best_diff(struct clk_rate_request *req,
35475c88143SClaudiu Beznea 					 struct clk_hw *parent,
35575c88143SClaudiu Beznea 					 unsigned long parent_rate,
35675c88143SClaudiu Beznea 					 long *best_rate,
35775c88143SClaudiu Beznea 					 long *best_diff,
35875c88143SClaudiu Beznea 					 u32 div)
35975c88143SClaudiu Beznea {
36075c88143SClaudiu Beznea 	unsigned long tmp_rate, tmp_diff;
36175c88143SClaudiu Beznea 
36275c88143SClaudiu Beznea 	if (div == MASTER_PRES_MAX)
36375c88143SClaudiu Beznea 		tmp_rate = parent_rate / 3;
36475c88143SClaudiu Beznea 	else
36575c88143SClaudiu Beznea 		tmp_rate = parent_rate >> div;
36675c88143SClaudiu Beznea 
36775c88143SClaudiu Beznea 	tmp_diff = abs(req->rate - tmp_rate);
36875c88143SClaudiu Beznea 
36975c88143SClaudiu Beznea 	if (*best_diff < 0 || *best_diff >= tmp_diff) {
37075c88143SClaudiu Beznea 		*best_rate = tmp_rate;
37175c88143SClaudiu Beznea 		*best_diff = tmp_diff;
37275c88143SClaudiu Beznea 		req->best_parent_rate = parent_rate;
37375c88143SClaudiu Beznea 		req->best_parent_hw = parent;
37475c88143SClaudiu Beznea 	}
37575c88143SClaudiu Beznea }
37675c88143SClaudiu Beznea 
clk_master_pres_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)3777a110b91SClaudiu Beznea static unsigned long clk_master_pres_recalc_rate(struct clk_hw *hw,
3787a110b91SClaudiu Beznea 						 unsigned long parent_rate)
3797a110b91SClaudiu Beznea {
3807a110b91SClaudiu Beznea 	struct clk_master *master = to_clk_master(hw);
3817a110b91SClaudiu Beznea 	const struct clk_master_characteristics *characteristics =
3827a110b91SClaudiu Beznea 						master->characteristics;
3837a110b91SClaudiu Beznea 	unsigned long flags;
3847a110b91SClaudiu Beznea 	unsigned int val, pres;
3857a110b91SClaudiu Beznea 
3867a110b91SClaudiu Beznea 	spin_lock_irqsave(master->lock, flags);
3877a110b91SClaudiu Beznea 	regmap_read(master->regmap, master->layout->offset, &val);
3887a110b91SClaudiu Beznea 	spin_unlock_irqrestore(master->lock, flags);
3897a110b91SClaudiu Beznea 
390a27748adSClaudiu Beznea 	val &= master->layout->mask;
3917a110b91SClaudiu Beznea 	pres = (val >> master->layout->pres_shift) & MASTER_PRES_MASK;
3920ef99f82SClaudiu Beznea 	if (pres == MASTER_PRES_MAX && characteristics->have_div3_pres)
3937a110b91SClaudiu Beznea 		pres = 3;
3947a110b91SClaudiu Beznea 	else
3957a110b91SClaudiu Beznea 		pres = (1 << pres);
3967a110b91SClaudiu Beznea 
3977a110b91SClaudiu Beznea 	return DIV_ROUND_CLOSEST_ULL(parent_rate, pres);
3987a110b91SClaudiu Beznea }
3997a110b91SClaudiu Beznea 
clk_master_pres_get_parent(struct clk_hw * hw)4007a110b91SClaudiu Beznea static u8 clk_master_pres_get_parent(struct clk_hw *hw)
4017a110b91SClaudiu Beznea {
4027a110b91SClaudiu Beznea 	struct clk_master *master = to_clk_master(hw);
4037a110b91SClaudiu Beznea 	unsigned long flags;
4047a110b91SClaudiu Beznea 	unsigned int mckr;
4057a110b91SClaudiu Beznea 
4067a110b91SClaudiu Beznea 	spin_lock_irqsave(master->lock, flags);
4077a110b91SClaudiu Beznea 	regmap_read(master->regmap, master->layout->offset, &mckr);
4087a110b91SClaudiu Beznea 	spin_unlock_irqrestore(master->lock, flags);
4097a110b91SClaudiu Beznea 
410a27748adSClaudiu Beznea 	mckr &= master->layout->mask;
411a27748adSClaudiu Beznea 
4127a110b91SClaudiu Beznea 	return mckr & AT91_PMC_CSS;
4137a110b91SClaudiu Beznea }
4147a110b91SClaudiu Beznea 
clk_master_pres_save_context(struct clk_hw * hw)41536971566SClaudiu Beznea static int clk_master_pres_save_context(struct clk_hw *hw)
41636971566SClaudiu Beznea {
41736971566SClaudiu Beznea 	struct clk_master *master = to_clk_master(hw);
41836971566SClaudiu Beznea 	struct clk_hw *parent_hw = clk_hw_get_parent(hw);
41936971566SClaudiu Beznea 	unsigned long flags;
42036971566SClaudiu Beznea 	unsigned int val, pres;
42136971566SClaudiu Beznea 
42236971566SClaudiu Beznea 	spin_lock_irqsave(master->lock, flags);
42336971566SClaudiu Beznea 	regmap_read(master->regmap, master->layout->offset, &val);
42436971566SClaudiu Beznea 	spin_unlock_irqrestore(master->lock, flags);
42536971566SClaudiu Beznea 
42636971566SClaudiu Beznea 	val &= master->layout->mask;
42736971566SClaudiu Beznea 	pres = (val >> master->layout->pres_shift) & MASTER_PRES_MASK;
42836971566SClaudiu Beznea 	if (pres == MASTER_PRES_MAX && master->characteristics->have_div3_pres)
42936971566SClaudiu Beznea 		pres = 3;
43036971566SClaudiu Beznea 	else
43136971566SClaudiu Beznea 		pres = (1 << pres);
43236971566SClaudiu Beznea 
43336971566SClaudiu Beznea 	master->pms.parent = val & AT91_PMC_CSS;
43436971566SClaudiu Beznea 	master->pms.parent_rate = clk_hw_get_rate(parent_hw);
43536971566SClaudiu Beznea 	master->pms.rate = DIV_ROUND_CLOSEST_ULL(master->pms.parent_rate, pres);
43636971566SClaudiu Beznea 
43736971566SClaudiu Beznea 	return 0;
43836971566SClaudiu Beznea }
43936971566SClaudiu Beznea 
clk_master_pres_restore_context(struct clk_hw * hw)44036971566SClaudiu Beznea static void clk_master_pres_restore_context(struct clk_hw *hw)
44136971566SClaudiu Beznea {
44236971566SClaudiu Beznea 	struct clk_master *master = to_clk_master(hw);
44336971566SClaudiu Beznea 	unsigned long flags;
44436971566SClaudiu Beznea 	unsigned int val, pres;
44536971566SClaudiu Beznea 
44636971566SClaudiu Beznea 	spin_lock_irqsave(master->lock, flags);
44736971566SClaudiu Beznea 	regmap_read(master->regmap, master->layout->offset, &val);
44836971566SClaudiu Beznea 	spin_unlock_irqrestore(master->lock, flags);
44936971566SClaudiu Beznea 
45036971566SClaudiu Beznea 	val &= master->layout->mask;
45136971566SClaudiu Beznea 	pres = (val >> master->layout->pres_shift) & MASTER_PRES_MASK;
45236971566SClaudiu Beznea 	if (pres == MASTER_PRES_MAX && master->characteristics->have_div3_pres)
45336971566SClaudiu Beznea 		pres = 3;
45436971566SClaudiu Beznea 	else
45536971566SClaudiu Beznea 		pres = (1 << pres);
45636971566SClaudiu Beznea 
45736971566SClaudiu Beznea 	if (master->pms.rate !=
45836971566SClaudiu Beznea 	    DIV_ROUND_CLOSEST_ULL(master->pms.parent_rate, pres) ||
45936971566SClaudiu Beznea 	    (master->pms.parent != (val & AT91_PMC_CSS)))
46036971566SClaudiu Beznea 		pr_warn("MCKR PRES was not configured properly by firmware!\n");
46136971566SClaudiu Beznea }
46236971566SClaudiu Beznea 
4637a110b91SClaudiu Beznea static const struct clk_ops master_pres_ops = {
4647a110b91SClaudiu Beznea 	.prepare = clk_master_prepare,
4657a110b91SClaudiu Beznea 	.is_prepared = clk_master_is_prepared,
4667a110b91SClaudiu Beznea 	.recalc_rate = clk_master_pres_recalc_rate,
4677a110b91SClaudiu Beznea 	.get_parent = clk_master_pres_get_parent,
46836971566SClaudiu Beznea 	.save_context = clk_master_pres_save_context,
46936971566SClaudiu Beznea 	.restore_context = clk_master_pres_restore_context,
4707a110b91SClaudiu Beznea };
4717a110b91SClaudiu Beznea 
4727a110b91SClaudiu Beznea static struct clk_hw * __init
at91_clk_register_master_internal(struct regmap * regmap,const char * name,int num_parents,const char ** parent_names,struct clk_hw ** parent_hws,const struct clk_master_layout * layout,const struct clk_master_characteristics * characteristics,const struct clk_ops * ops,spinlock_t * lock,u32 flags)4737a110b91SClaudiu Beznea at91_clk_register_master_internal(struct regmap *regmap,
4747a110b91SClaudiu Beznea 		const char *name, int num_parents,
4757a110b91SClaudiu Beznea 		const char **parent_names,
476*171e502cSClaudiu Beznea 		struct clk_hw **parent_hws,
4777a110b91SClaudiu Beznea 		const struct clk_master_layout *layout,
4787a110b91SClaudiu Beznea 		const struct clk_master_characteristics *characteristics,
4798e842f02SClaudiu Beznea 		const struct clk_ops *ops, spinlock_t *lock, u32 flags)
4807a110b91SClaudiu Beznea {
4817a110b91SClaudiu Beznea 	struct clk_master *master;
482*171e502cSClaudiu Beznea 	struct clk_init_data init = {};
4837a110b91SClaudiu Beznea 	struct clk_hw *hw;
4847029db09SClaudiu Beznea 	unsigned int mckr;
4857029db09SClaudiu Beznea 	unsigned long irqflags;
4867a110b91SClaudiu Beznea 	int ret;
4877a110b91SClaudiu Beznea 
488*171e502cSClaudiu Beznea 	if (!name || !num_parents || !(parent_names || parent_hws) || !lock)
4897a110b91SClaudiu Beznea 		return ERR_PTR(-EINVAL);
4907a110b91SClaudiu Beznea 
4917a110b91SClaudiu Beznea 	master = kzalloc(sizeof(*master), GFP_KERNEL);
4927a110b91SClaudiu Beznea 	if (!master)
4937a110b91SClaudiu Beznea 		return ERR_PTR(-ENOMEM);
4947a110b91SClaudiu Beznea 
4957a110b91SClaudiu Beznea 	init.name = name;
4967a110b91SClaudiu Beznea 	init.ops = ops;
497*171e502cSClaudiu Beznea 	if (parent_hws)
498*171e502cSClaudiu Beznea 		init.parent_hws = (const struct clk_hw **)parent_hws;
499*171e502cSClaudiu Beznea 	else
5007a110b91SClaudiu Beznea 		init.parent_names = parent_names;
5017a110b91SClaudiu Beznea 	init.num_parents = num_parents;
5027a110b91SClaudiu Beznea 	init.flags = flags;
5037a110b91SClaudiu Beznea 
5047a110b91SClaudiu Beznea 	master->hw.init = &init;
5057a110b91SClaudiu Beznea 	master->layout = layout;
5067a110b91SClaudiu Beznea 	master->characteristics = characteristics;
5077a110b91SClaudiu Beznea 	master->regmap = regmap;
5087a110b91SClaudiu Beznea 	master->lock = lock;
5097a110b91SClaudiu Beznea 
5107029db09SClaudiu Beznea 	if (ops == &master_div_ops_chg) {
5117029db09SClaudiu Beznea 		spin_lock_irqsave(master->lock, irqflags);
5127029db09SClaudiu Beznea 		regmap_read(master->regmap, master->layout->offset, &mckr);
5137029db09SClaudiu Beznea 		spin_unlock_irqrestore(master->lock, irqflags);
5147029db09SClaudiu Beznea 
5157029db09SClaudiu Beznea 		mckr &= layout->mask;
5167029db09SClaudiu Beznea 		mckr = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
5177029db09SClaudiu Beznea 		master->div = characteristics->divisors[mckr];
5187029db09SClaudiu Beznea 	}
5197029db09SClaudiu Beznea 
5207a110b91SClaudiu Beznea 	hw = &master->hw;
5217a110b91SClaudiu Beznea 	ret = clk_hw_register(NULL, &master->hw);
5227a110b91SClaudiu Beznea 	if (ret) {
5237a110b91SClaudiu Beznea 		kfree(master);
5247a110b91SClaudiu Beznea 		hw = ERR_PTR(ret);
5257a110b91SClaudiu Beznea 	}
5267a110b91SClaudiu Beznea 
5277a110b91SClaudiu Beznea 	return hw;
5287a110b91SClaudiu Beznea }
5297a110b91SClaudiu Beznea 
5307a110b91SClaudiu Beznea struct clk_hw * __init
at91_clk_register_master_pres(struct regmap * regmap,const char * name,int num_parents,const char ** parent_names,struct clk_hw ** parent_hws,const struct clk_master_layout * layout,const struct clk_master_characteristics * characteristics,spinlock_t * lock)5317a110b91SClaudiu Beznea at91_clk_register_master_pres(struct regmap *regmap,
5327a110b91SClaudiu Beznea 		const char *name, int num_parents,
5337a110b91SClaudiu Beznea 		const char **parent_names,
534*171e502cSClaudiu Beznea 		struct clk_hw **parent_hws,
5357a110b91SClaudiu Beznea 		const struct clk_master_layout *layout,
5367a110b91SClaudiu Beznea 		const struct clk_master_characteristics *characteristics,
5378e842f02SClaudiu Beznea 		spinlock_t *lock)
5387a110b91SClaudiu Beznea {
5397a110b91SClaudiu Beznea 	return at91_clk_register_master_internal(regmap, name, num_parents,
540*171e502cSClaudiu Beznea 						 parent_names, parent_hws, layout,
5418e842f02SClaudiu Beznea 						 characteristics,
5428e842f02SClaudiu Beznea 						 &master_pres_ops,
5438e842f02SClaudiu Beznea 						 lock, CLK_SET_RATE_GATE);
5447a110b91SClaudiu Beznea }
5457a110b91SClaudiu Beznea 
5467a110b91SClaudiu Beznea struct clk_hw * __init
at91_clk_register_master_div(struct regmap * regmap,const char * name,const char * parent_name,struct clk_hw * parent_hw,const struct clk_master_layout * layout,const struct clk_master_characteristics * characteristics,spinlock_t * lock,u32 flags,u32 safe_div)5477a110b91SClaudiu Beznea at91_clk_register_master_div(struct regmap *regmap,
5487a110b91SClaudiu Beznea 		const char *name, const char *parent_name,
549*171e502cSClaudiu Beznea 		struct clk_hw *parent_hw, const struct clk_master_layout *layout,
5507a110b91SClaudiu Beznea 		const struct clk_master_characteristics *characteristics,
5517029db09SClaudiu Beznea 		spinlock_t *lock, u32 flags, u32 safe_div)
5527a110b91SClaudiu Beznea {
5537a110b91SClaudiu Beznea 	const struct clk_ops *ops;
5547029db09SClaudiu Beznea 	struct clk_hw *hw;
5557a110b91SClaudiu Beznea 
5567a110b91SClaudiu Beznea 	if (flags & CLK_SET_RATE_GATE)
5577a110b91SClaudiu Beznea 		ops = &master_div_ops;
5587a110b91SClaudiu Beznea 	else
5597a110b91SClaudiu Beznea 		ops = &master_div_ops_chg;
5607a110b91SClaudiu Beznea 
5617029db09SClaudiu Beznea 	hw = at91_clk_register_master_internal(regmap, name, 1,
562*171e502cSClaudiu Beznea 					       parent_name ? &parent_name : NULL,
563*171e502cSClaudiu Beznea 					       parent_hw ? &parent_hw : NULL, layout,
5647a110b91SClaudiu Beznea 					       characteristics, ops,
5658e842f02SClaudiu Beznea 					       lock, flags);
5667029db09SClaudiu Beznea 
5677029db09SClaudiu Beznea 	if (!IS_ERR(hw) && safe_div) {
5687029db09SClaudiu Beznea 		master_div = to_clk_master(hw);
5697029db09SClaudiu Beznea 		master_div->safe_div = safe_div;
5707029db09SClaudiu Beznea 		clk_notifier_register(hw->clk,
5717029db09SClaudiu Beznea 				      &clk_master_div_notifier);
5727029db09SClaudiu Beznea 	}
5737029db09SClaudiu Beznea 
5747029db09SClaudiu Beznea 	return hw;
5757a110b91SClaudiu Beznea }
5767a110b91SClaudiu Beznea 
5777a110b91SClaudiu Beznea static unsigned long
clk_sama7g5_master_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)5787a110b91SClaudiu Beznea clk_sama7g5_master_recalc_rate(struct clk_hw *hw,
5797a110b91SClaudiu Beznea 			       unsigned long parent_rate)
5807a110b91SClaudiu Beznea {
5817a110b91SClaudiu Beznea 	struct clk_master *master = to_clk_master(hw);
5827a110b91SClaudiu Beznea 
5837a110b91SClaudiu Beznea 	return DIV_ROUND_CLOSEST_ULL(parent_rate, (1 << master->div));
5847a110b91SClaudiu Beznea }
5857a110b91SClaudiu Beznea 
clk_sama7g5_master_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)58675c88143SClaudiu Beznea static int clk_sama7g5_master_determine_rate(struct clk_hw *hw,
58775c88143SClaudiu Beznea 					     struct clk_rate_request *req)
58875c88143SClaudiu Beznea {
58975c88143SClaudiu Beznea 	struct clk_master *master = to_clk_master(hw);
59075c88143SClaudiu Beznea 	struct clk_hw *parent;
59175c88143SClaudiu Beznea 	long best_rate = LONG_MIN, best_diff = LONG_MIN;
59275c88143SClaudiu Beznea 	unsigned long parent_rate;
59375c88143SClaudiu Beznea 	unsigned int div, i;
59475c88143SClaudiu Beznea 
59575c88143SClaudiu Beznea 	/* First: check the dividers of MCR. */
59675c88143SClaudiu Beznea 	for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
59775c88143SClaudiu Beznea 		parent = clk_hw_get_parent_by_index(hw, i);
59875c88143SClaudiu Beznea 		if (!parent)
59975c88143SClaudiu Beznea 			continue;
60075c88143SClaudiu Beznea 
60175c88143SClaudiu Beznea 		parent_rate = clk_hw_get_rate(parent);
60275c88143SClaudiu Beznea 		if (!parent_rate)
60375c88143SClaudiu Beznea 			continue;
60475c88143SClaudiu Beznea 
60575c88143SClaudiu Beznea 		for (div = 0; div < MASTER_PRES_MAX + 1; div++) {
60675c88143SClaudiu Beznea 			clk_sama7g5_master_best_diff(req, parent, parent_rate,
60775c88143SClaudiu Beznea 						     &best_rate, &best_diff,
60875c88143SClaudiu Beznea 						     div);
60975c88143SClaudiu Beznea 			if (!best_diff)
61075c88143SClaudiu Beznea 				break;
61175c88143SClaudiu Beznea 		}
61275c88143SClaudiu Beznea 
61375c88143SClaudiu Beznea 		if (!best_diff)
61475c88143SClaudiu Beznea 			break;
61575c88143SClaudiu Beznea 	}
61675c88143SClaudiu Beznea 
61775c88143SClaudiu Beznea 	/* Second: try to request rate form changeable parent. */
61875c88143SClaudiu Beznea 	if (master->chg_pid < 0)
61975c88143SClaudiu Beznea 		goto end;
62075c88143SClaudiu Beznea 
62175c88143SClaudiu Beznea 	parent = clk_hw_get_parent_by_index(hw, master->chg_pid);
62275c88143SClaudiu Beznea 	if (!parent)
62375c88143SClaudiu Beznea 		goto end;
62475c88143SClaudiu Beznea 
62575c88143SClaudiu Beznea 	for (div = 0; div < MASTER_PRES_MAX + 1; div++) {
626262ca38fSMaxime Ripard 		struct clk_rate_request req_parent;
627262ca38fSMaxime Ripard 		unsigned long req_rate;
62875c88143SClaudiu Beznea 
629262ca38fSMaxime Ripard 		if (div == MASTER_PRES_MAX)
630262ca38fSMaxime Ripard 			req_rate = req->rate * 3;
631262ca38fSMaxime Ripard 		else
632262ca38fSMaxime Ripard 			req_rate = req->rate << div;
633262ca38fSMaxime Ripard 
634262ca38fSMaxime Ripard 		clk_hw_forward_rate_request(hw, req, parent, &req_parent, req_rate);
63575c88143SClaudiu Beznea 		if (__clk_determine_rate(parent, &req_parent))
63675c88143SClaudiu Beznea 			continue;
63775c88143SClaudiu Beznea 
63875c88143SClaudiu Beznea 		clk_sama7g5_master_best_diff(req, parent, req_parent.rate,
63975c88143SClaudiu Beznea 					     &best_rate, &best_diff, div);
64075c88143SClaudiu Beznea 
64175c88143SClaudiu Beznea 		if (!best_diff)
64275c88143SClaudiu Beznea 			break;
64375c88143SClaudiu Beznea 	}
64475c88143SClaudiu Beznea 
64575c88143SClaudiu Beznea end:
64675c88143SClaudiu Beznea 	pr_debug("MCK: %s, best_rate = %ld, parent clk: %s @ %ld\n",
64775c88143SClaudiu Beznea 		 __func__, best_rate,
64875c88143SClaudiu Beznea 		 __clk_get_name((req->best_parent_hw)->clk),
64975c88143SClaudiu Beznea 		req->best_parent_rate);
65075c88143SClaudiu Beznea 
65175c88143SClaudiu Beznea 	if (best_rate < 0)
65275c88143SClaudiu Beznea 		return -EINVAL;
65375c88143SClaudiu Beznea 
65475c88143SClaudiu Beznea 	req->rate = best_rate;
65575c88143SClaudiu Beznea 
65675c88143SClaudiu Beznea 	return 0;
65775c88143SClaudiu Beznea }
65875c88143SClaudiu Beznea 
clk_sama7g5_master_get_parent(struct clk_hw * hw)65975c88143SClaudiu Beznea static u8 clk_sama7g5_master_get_parent(struct clk_hw *hw)
66075c88143SClaudiu Beznea {
66175c88143SClaudiu Beznea 	struct clk_master *master = to_clk_master(hw);
66275c88143SClaudiu Beznea 	unsigned long flags;
66375c88143SClaudiu Beznea 	u8 index;
66475c88143SClaudiu Beznea 
66575c88143SClaudiu Beznea 	spin_lock_irqsave(master->lock, flags);
66675c88143SClaudiu Beznea 	index = clk_mux_val_to_index(&master->hw, master->mux_table, 0,
66775c88143SClaudiu Beznea 				     master->parent);
66875c88143SClaudiu Beznea 	spin_unlock_irqrestore(master->lock, flags);
66975c88143SClaudiu Beznea 
67075c88143SClaudiu Beznea 	return index;
67175c88143SClaudiu Beznea }
67275c88143SClaudiu Beznea 
clk_sama7g5_master_set_parent(struct clk_hw * hw,u8 index)67375c88143SClaudiu Beznea static int clk_sama7g5_master_set_parent(struct clk_hw *hw, u8 index)
67475c88143SClaudiu Beznea {
67575c88143SClaudiu Beznea 	struct clk_master *master = to_clk_master(hw);
67675c88143SClaudiu Beznea 	unsigned long flags;
67775c88143SClaudiu Beznea 
67875c88143SClaudiu Beznea 	if (index >= clk_hw_get_num_parents(hw))
67975c88143SClaudiu Beznea 		return -EINVAL;
68075c88143SClaudiu Beznea 
68175c88143SClaudiu Beznea 	spin_lock_irqsave(master->lock, flags);
68275c88143SClaudiu Beznea 	master->parent = clk_mux_index_to_val(master->mux_table, 0, index);
68375c88143SClaudiu Beznea 	spin_unlock_irqrestore(master->lock, flags);
68475c88143SClaudiu Beznea 
68575c88143SClaudiu Beznea 	return 0;
68675c88143SClaudiu Beznea }
68775c88143SClaudiu Beznea 
clk_sama7g5_master_set(struct clk_master * master,unsigned int status)68836971566SClaudiu Beznea static void clk_sama7g5_master_set(struct clk_master *master,
68936971566SClaudiu Beznea 				   unsigned int status)
69075c88143SClaudiu Beznea {
69175c88143SClaudiu Beznea 	unsigned long flags;
69275c88143SClaudiu Beznea 	unsigned int val, cparent;
693c5538816SClaudiu Beznea 	unsigned int enable = status ? AT91_PMC_MCR_V2_EN : 0;
69488bdeed3SClaudiu Beznea 	unsigned int parent = master->parent << PMC_MCR_CSS_SHIFT;
69588bdeed3SClaudiu Beznea 	unsigned int div = master->div << MASTER_DIV_SHIFT;
69675c88143SClaudiu Beznea 
69775c88143SClaudiu Beznea 	spin_lock_irqsave(master->lock, flags);
69875c88143SClaudiu Beznea 
699c5538816SClaudiu Beznea 	regmap_write(master->regmap, AT91_PMC_MCR_V2,
700c5538816SClaudiu Beznea 		     AT91_PMC_MCR_V2_ID(master->id));
701c5538816SClaudiu Beznea 	regmap_read(master->regmap, AT91_PMC_MCR_V2, &val);
702c5538816SClaudiu Beznea 	regmap_update_bits(master->regmap, AT91_PMC_MCR_V2,
703c5538816SClaudiu Beznea 			   enable | AT91_PMC_MCR_V2_CSS | AT91_PMC_MCR_V2_DIV |
704c5538816SClaudiu Beznea 			   AT91_PMC_MCR_V2_CMD | AT91_PMC_MCR_V2_ID_MSK,
70588bdeed3SClaudiu Beznea 			   enable | parent | div | AT91_PMC_MCR_V2_CMD |
706c5538816SClaudiu Beznea 			   AT91_PMC_MCR_V2_ID(master->id));
70775c88143SClaudiu Beznea 
708c5538816SClaudiu Beznea 	cparent = (val & AT91_PMC_MCR_V2_CSS) >> PMC_MCR_CSS_SHIFT;
70975c88143SClaudiu Beznea 
71075c88143SClaudiu Beznea 	/* Wait here only if parent is being changed. */
71175c88143SClaudiu Beznea 	while ((cparent != master->parent) && !clk_master_ready(master))
71275c88143SClaudiu Beznea 		cpu_relax();
71375c88143SClaudiu Beznea 
71475c88143SClaudiu Beznea 	spin_unlock_irqrestore(master->lock, flags);
71536971566SClaudiu Beznea }
71636971566SClaudiu Beznea 
clk_sama7g5_master_enable(struct clk_hw * hw)71736971566SClaudiu Beznea static int clk_sama7g5_master_enable(struct clk_hw *hw)
71836971566SClaudiu Beznea {
71936971566SClaudiu Beznea 	struct clk_master *master = to_clk_master(hw);
72036971566SClaudiu Beznea 
72136971566SClaudiu Beznea 	clk_sama7g5_master_set(master, 1);
72275c88143SClaudiu Beznea 
72375c88143SClaudiu Beznea 	return 0;
72475c88143SClaudiu Beznea }
72575c88143SClaudiu Beznea 
clk_sama7g5_master_disable(struct clk_hw * hw)72675c88143SClaudiu Beznea static void clk_sama7g5_master_disable(struct clk_hw *hw)
72775c88143SClaudiu Beznea {
72875c88143SClaudiu Beznea 	struct clk_master *master = to_clk_master(hw);
72975c88143SClaudiu Beznea 	unsigned long flags;
73075c88143SClaudiu Beznea 
73175c88143SClaudiu Beznea 	spin_lock_irqsave(master->lock, flags);
73275c88143SClaudiu Beznea 
733c5538816SClaudiu Beznea 	regmap_write(master->regmap, AT91_PMC_MCR_V2, master->id);
734c5538816SClaudiu Beznea 	regmap_update_bits(master->regmap, AT91_PMC_MCR_V2,
735c5538816SClaudiu Beznea 			   AT91_PMC_MCR_V2_EN | AT91_PMC_MCR_V2_CMD |
736c5538816SClaudiu Beznea 			   AT91_PMC_MCR_V2_ID_MSK,
737c5538816SClaudiu Beznea 			   AT91_PMC_MCR_V2_CMD |
738c5538816SClaudiu Beznea 			   AT91_PMC_MCR_V2_ID(master->id));
73975c88143SClaudiu Beznea 
74075c88143SClaudiu Beznea 	spin_unlock_irqrestore(master->lock, flags);
74175c88143SClaudiu Beznea }
74275c88143SClaudiu Beznea 
clk_sama7g5_master_is_enabled(struct clk_hw * hw)74375c88143SClaudiu Beznea static int clk_sama7g5_master_is_enabled(struct clk_hw *hw)
74475c88143SClaudiu Beznea {
74575c88143SClaudiu Beznea 	struct clk_master *master = to_clk_master(hw);
74675c88143SClaudiu Beznea 	unsigned long flags;
74775c88143SClaudiu Beznea 	unsigned int val;
74875c88143SClaudiu Beznea 
74975c88143SClaudiu Beznea 	spin_lock_irqsave(master->lock, flags);
75075c88143SClaudiu Beznea 
751c5538816SClaudiu Beznea 	regmap_write(master->regmap, AT91_PMC_MCR_V2, master->id);
752c5538816SClaudiu Beznea 	regmap_read(master->regmap, AT91_PMC_MCR_V2, &val);
75375c88143SClaudiu Beznea 
75475c88143SClaudiu Beznea 	spin_unlock_irqrestore(master->lock, flags);
75575c88143SClaudiu Beznea 
756c5538816SClaudiu Beznea 	return !!(val & AT91_PMC_MCR_V2_EN);
75775c88143SClaudiu Beznea }
75875c88143SClaudiu Beznea 
clk_sama7g5_master_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)75975c88143SClaudiu Beznea static int clk_sama7g5_master_set_rate(struct clk_hw *hw, unsigned long rate,
76075c88143SClaudiu Beznea 				       unsigned long parent_rate)
76175c88143SClaudiu Beznea {
76275c88143SClaudiu Beznea 	struct clk_master *master = to_clk_master(hw);
76375c88143SClaudiu Beznea 	unsigned long div, flags;
76475c88143SClaudiu Beznea 
76575c88143SClaudiu Beznea 	div = DIV_ROUND_CLOSEST(parent_rate, rate);
76675c88143SClaudiu Beznea 	if ((div > (1 << (MASTER_PRES_MAX - 1))) || (div & (div - 1)))
76775c88143SClaudiu Beznea 		return -EINVAL;
76875c88143SClaudiu Beznea 
76975c88143SClaudiu Beznea 	if (div == 3)
77075c88143SClaudiu Beznea 		div = MASTER_PRES_MAX;
771c2910c00SClaudiu Beznea 	else if (div)
77275c88143SClaudiu Beznea 		div = ffs(div) - 1;
77375c88143SClaudiu Beznea 
77475c88143SClaudiu Beznea 	spin_lock_irqsave(master->lock, flags);
77575c88143SClaudiu Beznea 	master->div = div;
77675c88143SClaudiu Beznea 	spin_unlock_irqrestore(master->lock, flags);
77775c88143SClaudiu Beznea 
77875c88143SClaudiu Beznea 	return 0;
77975c88143SClaudiu Beznea }
78075c88143SClaudiu Beznea 
clk_sama7g5_master_save_context(struct clk_hw * hw)78136971566SClaudiu Beznea static int clk_sama7g5_master_save_context(struct clk_hw *hw)
78236971566SClaudiu Beznea {
78336971566SClaudiu Beznea 	struct clk_master *master = to_clk_master(hw);
78436971566SClaudiu Beznea 
78536971566SClaudiu Beznea 	master->pms.status = clk_sama7g5_master_is_enabled(hw);
78636971566SClaudiu Beznea 
78736971566SClaudiu Beznea 	return 0;
78836971566SClaudiu Beznea }
78936971566SClaudiu Beznea 
clk_sama7g5_master_restore_context(struct clk_hw * hw)79036971566SClaudiu Beznea static void clk_sama7g5_master_restore_context(struct clk_hw *hw)
79136971566SClaudiu Beznea {
79236971566SClaudiu Beznea 	struct clk_master *master = to_clk_master(hw);
79336971566SClaudiu Beznea 
79436971566SClaudiu Beznea 	if (master->pms.status)
79536971566SClaudiu Beznea 		clk_sama7g5_master_set(master, master->pms.status);
79636971566SClaudiu Beznea }
79736971566SClaudiu Beznea 
79875c88143SClaudiu Beznea static const struct clk_ops sama7g5_master_ops = {
79975c88143SClaudiu Beznea 	.enable = clk_sama7g5_master_enable,
80075c88143SClaudiu Beznea 	.disable = clk_sama7g5_master_disable,
80175c88143SClaudiu Beznea 	.is_enabled = clk_sama7g5_master_is_enabled,
80275c88143SClaudiu Beznea 	.recalc_rate = clk_sama7g5_master_recalc_rate,
80375c88143SClaudiu Beznea 	.determine_rate = clk_sama7g5_master_determine_rate,
80475c88143SClaudiu Beznea 	.set_rate = clk_sama7g5_master_set_rate,
80575c88143SClaudiu Beznea 	.get_parent = clk_sama7g5_master_get_parent,
80675c88143SClaudiu Beznea 	.set_parent = clk_sama7g5_master_set_parent,
80736971566SClaudiu Beznea 	.save_context = clk_sama7g5_master_save_context,
80836971566SClaudiu Beznea 	.restore_context = clk_sama7g5_master_restore_context,
80975c88143SClaudiu Beznea };
81075c88143SClaudiu Beznea 
81175c88143SClaudiu Beznea struct clk_hw * __init
at91_clk_sama7g5_register_master(struct regmap * regmap,const char * name,int num_parents,const char ** parent_names,struct clk_hw ** parent_hws,u32 * mux_table,spinlock_t * lock,u8 id,bool critical,int chg_pid)81275c88143SClaudiu Beznea at91_clk_sama7g5_register_master(struct regmap *regmap,
81375c88143SClaudiu Beznea 				 const char *name, int num_parents,
81475c88143SClaudiu Beznea 				 const char **parent_names,
815*171e502cSClaudiu Beznea 				 struct clk_hw **parent_hws,
81675c88143SClaudiu Beznea 				 u32 *mux_table,
81775c88143SClaudiu Beznea 				 spinlock_t *lock, u8 id,
81875c88143SClaudiu Beznea 				 bool critical, int chg_pid)
81975c88143SClaudiu Beznea {
82075c88143SClaudiu Beznea 	struct clk_master *master;
82175c88143SClaudiu Beznea 	struct clk_hw *hw;
822*171e502cSClaudiu Beznea 	struct clk_init_data init = {};
82375c88143SClaudiu Beznea 	unsigned long flags;
82475c88143SClaudiu Beznea 	unsigned int val;
82575c88143SClaudiu Beznea 	int ret;
82675c88143SClaudiu Beznea 
827*171e502cSClaudiu Beznea 	if (!name || !num_parents || !(parent_names || parent_hws) || !mux_table ||
82875c88143SClaudiu Beznea 	    !lock || id > MASTER_MAX_ID)
82975c88143SClaudiu Beznea 		return ERR_PTR(-EINVAL);
83075c88143SClaudiu Beznea 
83175c88143SClaudiu Beznea 	master = kzalloc(sizeof(*master), GFP_KERNEL);
83275c88143SClaudiu Beznea 	if (!master)
83375c88143SClaudiu Beznea 		return ERR_PTR(-ENOMEM);
83475c88143SClaudiu Beznea 
83575c88143SClaudiu Beznea 	init.name = name;
83675c88143SClaudiu Beznea 	init.ops = &sama7g5_master_ops;
837*171e502cSClaudiu Beznea 	if (parent_hws)
838*171e502cSClaudiu Beznea 		init.parent_hws = (const struct clk_hw **)parent_hws;
839*171e502cSClaudiu Beznea 	else
84075c88143SClaudiu Beznea 		init.parent_names = parent_names;
84175c88143SClaudiu Beznea 	init.num_parents = num_parents;
84275c88143SClaudiu Beznea 	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
84375c88143SClaudiu Beznea 	if (chg_pid >= 0)
84475c88143SClaudiu Beznea 		init.flags |= CLK_SET_RATE_PARENT;
84575c88143SClaudiu Beznea 	if (critical)
84675c88143SClaudiu Beznea 		init.flags |= CLK_IS_CRITICAL;
84775c88143SClaudiu Beznea 
84875c88143SClaudiu Beznea 	master->hw.init = &init;
84975c88143SClaudiu Beznea 	master->regmap = regmap;
85075c88143SClaudiu Beznea 	master->id = id;
85175c88143SClaudiu Beznea 	master->chg_pid = chg_pid;
85275c88143SClaudiu Beznea 	master->lock = lock;
85375c88143SClaudiu Beznea 	master->mux_table = mux_table;
85475c88143SClaudiu Beznea 
85575c88143SClaudiu Beznea 	spin_lock_irqsave(master->lock, flags);
856c5538816SClaudiu Beznea 	regmap_write(master->regmap, AT91_PMC_MCR_V2, master->id);
857c5538816SClaudiu Beznea 	regmap_read(master->regmap, AT91_PMC_MCR_V2, &val);
858c5538816SClaudiu Beznea 	master->parent = (val & AT91_PMC_MCR_V2_CSS) >> PMC_MCR_CSS_SHIFT;
859c5538816SClaudiu Beznea 	master->div = (val & AT91_PMC_MCR_V2_DIV) >> MASTER_DIV_SHIFT;
86075c88143SClaudiu Beznea 	spin_unlock_irqrestore(master->lock, flags);
86175c88143SClaudiu Beznea 
86275c88143SClaudiu Beznea 	hw = &master->hw;
86375c88143SClaudiu Beznea 	ret = clk_hw_register(NULL, &master->hw);
86475c88143SClaudiu Beznea 	if (ret) {
86575c88143SClaudiu Beznea 		kfree(master);
86675c88143SClaudiu Beznea 		hw = ERR_PTR(ret);
86775c88143SClaudiu Beznea 	}
86875c88143SClaudiu Beznea 
86975c88143SClaudiu Beznea 	return hw;
87075c88143SClaudiu Beznea }
87175c88143SClaudiu Beznea 
872b2e39dc0SAlexandre Belloni const struct clk_master_layout at91rm9200_master_layout = {
873e442d234SBoris BREZILLON 	.mask = 0x31F,
874e442d234SBoris BREZILLON 	.pres_shift = 2,
875e5be5370SAlexandre Belloni 	.offset = AT91_PMC_MCKR,
876e442d234SBoris BREZILLON };
877e442d234SBoris BREZILLON 
878b2e39dc0SAlexandre Belloni const struct clk_master_layout at91sam9x5_master_layout = {
879e442d234SBoris BREZILLON 	.mask = 0x373,
880e442d234SBoris BREZILLON 	.pres_shift = 4,
881e5be5370SAlexandre Belloni 	.offset = AT91_PMC_MCKR,
882e442d234SBoris BREZILLON };
883