xref: /openbmc/linux/drivers/clk/at91/clk-master.c (revision e5be5370)
1e442d234SBoris BREZILLON /*
2e442d234SBoris BREZILLON  *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
3e442d234SBoris BREZILLON  *
4e442d234SBoris BREZILLON  * This program is free software; you can redistribute it and/or modify
5e442d234SBoris BREZILLON  * it under the terms of the GNU General Public License as published by
6e442d234SBoris BREZILLON  * the Free Software Foundation; either version 2 of the License, or
7e442d234SBoris BREZILLON  * (at your option) any later version.
8e442d234SBoris BREZILLON  *
9e442d234SBoris BREZILLON  */
10e442d234SBoris BREZILLON 
11e442d234SBoris BREZILLON #include <linux/clk-provider.h>
12e442d234SBoris BREZILLON #include <linux/clkdev.h>
13e442d234SBoris BREZILLON #include <linux/clk/at91_pmc.h>
14e442d234SBoris BREZILLON #include <linux/of.h>
151bdf0232SBoris Brezillon #include <linux/mfd/syscon.h>
161bdf0232SBoris Brezillon #include <linux/regmap.h>
17e442d234SBoris BREZILLON 
18e442d234SBoris BREZILLON #include "pmc.h"
19e442d234SBoris BREZILLON 
20e442d234SBoris BREZILLON #define MASTER_PRES_MASK	0x7
21e442d234SBoris BREZILLON #define MASTER_PRES_MAX		MASTER_PRES_MASK
22e442d234SBoris BREZILLON #define MASTER_DIV_SHIFT	8
23e442d234SBoris BREZILLON #define MASTER_DIV_MASK		0x3
24e442d234SBoris BREZILLON 
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;
30e442d234SBoris BREZILLON 	const struct clk_master_layout *layout;
31e442d234SBoris BREZILLON 	const struct clk_master_characteristics *characteristics;
32e5be5370SAlexandre Belloni 	u32 mckr;
33e442d234SBoris BREZILLON };
34e442d234SBoris BREZILLON 
351bdf0232SBoris Brezillon static inline bool clk_master_ready(struct regmap *regmap)
361bdf0232SBoris Brezillon {
371bdf0232SBoris Brezillon 	unsigned int status;
381bdf0232SBoris Brezillon 
391bdf0232SBoris Brezillon 	regmap_read(regmap, AT91_PMC_SR, &status);
401bdf0232SBoris Brezillon 
411bdf0232SBoris Brezillon 	return status & AT91_PMC_MCKRDY ? 1 : 0;
421bdf0232SBoris Brezillon }
431bdf0232SBoris Brezillon 
44e442d234SBoris BREZILLON static int clk_master_prepare(struct clk_hw *hw)
45e442d234SBoris BREZILLON {
46e442d234SBoris BREZILLON 	struct clk_master *master = to_clk_master(hw);
47e442d234SBoris BREZILLON 
4899a81706SAlexandre Belloni 	while (!clk_master_ready(master->regmap))
4999a81706SAlexandre Belloni 		cpu_relax();
50e442d234SBoris BREZILLON 
51e442d234SBoris BREZILLON 	return 0;
52e442d234SBoris BREZILLON }
53e442d234SBoris BREZILLON 
54e442d234SBoris BREZILLON static int clk_master_is_prepared(struct clk_hw *hw)
55e442d234SBoris BREZILLON {
56e442d234SBoris BREZILLON 	struct clk_master *master = to_clk_master(hw);
57e442d234SBoris BREZILLON 
581bdf0232SBoris Brezillon 	return clk_master_ready(master->regmap);
59e442d234SBoris BREZILLON }
60e442d234SBoris BREZILLON 
61e442d234SBoris BREZILLON static unsigned long clk_master_recalc_rate(struct clk_hw *hw,
62e442d234SBoris BREZILLON 					    unsigned long parent_rate)
63e442d234SBoris BREZILLON {
64e442d234SBoris BREZILLON 	u8 pres;
65e442d234SBoris BREZILLON 	u8 div;
66e442d234SBoris BREZILLON 	unsigned long rate = parent_rate;
67e442d234SBoris BREZILLON 	struct clk_master *master = to_clk_master(hw);
68e442d234SBoris BREZILLON 	const struct clk_master_layout *layout = master->layout;
69e442d234SBoris BREZILLON 	const struct clk_master_characteristics *characteristics =
70e442d234SBoris BREZILLON 						master->characteristics;
711bdf0232SBoris Brezillon 	unsigned int mckr;
72e442d234SBoris BREZILLON 
73e5be5370SAlexandre Belloni 	regmap_read(master->regmap, master->layout->offset, &mckr);
741bdf0232SBoris Brezillon 	mckr &= layout->mask;
75e442d234SBoris BREZILLON 
761bdf0232SBoris Brezillon 	pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK;
771bdf0232SBoris Brezillon 	div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
78e442d234SBoris BREZILLON 
79e442d234SBoris BREZILLON 	if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX)
80e442d234SBoris BREZILLON 		rate /= 3;
81e442d234SBoris BREZILLON 	else
82e442d234SBoris BREZILLON 		rate >>= pres;
83e442d234SBoris BREZILLON 
84e442d234SBoris BREZILLON 	rate /= characteristics->divisors[div];
85e442d234SBoris BREZILLON 
86e442d234SBoris BREZILLON 	if (rate < characteristics->output.min)
87e442d234SBoris BREZILLON 		pr_warn("master clk is underclocked");
88e442d234SBoris BREZILLON 	else if (rate > characteristics->output.max)
89e442d234SBoris BREZILLON 		pr_warn("master clk is overclocked");
90e442d234SBoris BREZILLON 
91e442d234SBoris BREZILLON 	return rate;
92e442d234SBoris BREZILLON }
93e442d234SBoris BREZILLON 
94e442d234SBoris BREZILLON static u8 clk_master_get_parent(struct clk_hw *hw)
95e442d234SBoris BREZILLON {
96e442d234SBoris BREZILLON 	struct clk_master *master = to_clk_master(hw);
971bdf0232SBoris Brezillon 	unsigned int mckr;
98e442d234SBoris BREZILLON 
99e5be5370SAlexandre Belloni 	regmap_read(master->regmap, master->layout->offset, &mckr);
1001bdf0232SBoris Brezillon 
1011bdf0232SBoris Brezillon 	return mckr & AT91_PMC_CSS;
102e442d234SBoris BREZILLON }
103e442d234SBoris BREZILLON 
104e442d234SBoris BREZILLON static const struct clk_ops master_ops = {
105e442d234SBoris BREZILLON 	.prepare = clk_master_prepare,
106e442d234SBoris BREZILLON 	.is_prepared = clk_master_is_prepared,
107e442d234SBoris BREZILLON 	.recalc_rate = clk_master_recalc_rate,
108e442d234SBoris BREZILLON 	.get_parent = clk_master_get_parent,
109e442d234SBoris BREZILLON };
110e442d234SBoris BREZILLON 
111b2e39dc0SAlexandre Belloni struct clk_hw * __init
11299a81706SAlexandre Belloni at91_clk_register_master(struct regmap *regmap,
113e442d234SBoris BREZILLON 		const char *name, int num_parents,
114e442d234SBoris BREZILLON 		const char **parent_names,
115e442d234SBoris BREZILLON 		const struct clk_master_layout *layout,
116e442d234SBoris BREZILLON 		const struct clk_master_characteristics *characteristics)
117e442d234SBoris BREZILLON {
118e442d234SBoris BREZILLON 	struct clk_master *master;
119e442d234SBoris BREZILLON 	struct clk_init_data init;
120f5644f10SStephen Boyd 	struct clk_hw *hw;
121f5644f10SStephen Boyd 	int ret;
122e442d234SBoris BREZILLON 
1231bdf0232SBoris Brezillon 	if (!name || !num_parents || !parent_names)
124e442d234SBoris BREZILLON 		return ERR_PTR(-EINVAL);
125e442d234SBoris BREZILLON 
126e442d234SBoris BREZILLON 	master = kzalloc(sizeof(*master), GFP_KERNEL);
127e442d234SBoris BREZILLON 	if (!master)
128e442d234SBoris BREZILLON 		return ERR_PTR(-ENOMEM);
129e442d234SBoris BREZILLON 
130e442d234SBoris BREZILLON 	init.name = name;
131e442d234SBoris BREZILLON 	init.ops = &master_ops;
132e442d234SBoris BREZILLON 	init.parent_names = parent_names;
133e442d234SBoris BREZILLON 	init.num_parents = num_parents;
134e442d234SBoris BREZILLON 	init.flags = 0;
135e442d234SBoris BREZILLON 
136e442d234SBoris BREZILLON 	master->hw.init = &init;
137e442d234SBoris BREZILLON 	master->layout = layout;
138e442d234SBoris BREZILLON 	master->characteristics = characteristics;
1391bdf0232SBoris Brezillon 	master->regmap = regmap;
140e442d234SBoris BREZILLON 
141f5644f10SStephen Boyd 	hw = &master->hw;
142f5644f10SStephen Boyd 	ret = clk_hw_register(NULL, &master->hw);
143f5644f10SStephen Boyd 	if (ret) {
144e442d234SBoris BREZILLON 		kfree(master);
145f5644f10SStephen Boyd 		hw = ERR_PTR(ret);
146c76a024eSDavid Dueck 	}
147e442d234SBoris BREZILLON 
148f5644f10SStephen Boyd 	return hw;
149e442d234SBoris BREZILLON }
150e442d234SBoris BREZILLON 
151b2e39dc0SAlexandre Belloni const struct clk_master_layout at91rm9200_master_layout = {
152e442d234SBoris BREZILLON 	.mask = 0x31F,
153e442d234SBoris BREZILLON 	.pres_shift = 2,
154e5be5370SAlexandre Belloni 	.offset = AT91_PMC_MCKR,
155e442d234SBoris BREZILLON };
156e442d234SBoris BREZILLON 
157b2e39dc0SAlexandre Belloni const struct clk_master_layout at91sam9x5_master_layout = {
158e442d234SBoris BREZILLON 	.mask = 0x373,
159e442d234SBoris BREZILLON 	.pres_shift = 4,
160e5be5370SAlexandre Belloni 	.offset = AT91_PMC_MCKR,
161e442d234SBoris BREZILLON };
162