Commit | Line | Data |
---|---|---|
63b8d92c RK |
1 | /* |
2 | * Marvell Dove PMU Core PLL divider driver | |
3 | * | |
4 | * Cleaned up by substantially rewriting, and converted to DT by | |
5 | * Russell King. Origin is not known. | |
6 | */ | |
7 | #include <linux/clk-provider.h> | |
8 | #include <linux/delay.h> | |
9 | #include <linux/io.h> | |
10 | #include <linux/kernel.h> | |
11 | #include <linux/of.h> | |
12 | #include <linux/of_address.h> | |
13 | ||
14 | #include "dove-divider.h" | |
15 | ||
16 | struct dove_clk { | |
17 | const char *name; | |
18 | struct clk_hw hw; | |
19 | void __iomem *base; | |
20 | spinlock_t *lock; | |
21 | u8 div_bit_start; | |
22 | u8 div_bit_end; | |
23 | u8 div_bit_load; | |
24 | u8 div_bit_size; | |
25 | u32 *divider_table; | |
26 | }; | |
27 | ||
28 | enum { | |
29 | DIV_CTRL0 = 0, | |
30 | DIV_CTRL1 = 4, | |
31 | DIV_CTRL1_N_RESET_MASK = BIT(10), | |
32 | }; | |
33 | ||
34 | #define to_dove_clk(hw) container_of(hw, struct dove_clk, hw) | |
35 | ||
36 | static void dove_load_divider(void __iomem *base, u32 val, u32 mask, u32 load) | |
37 | { | |
38 | u32 v; | |
39 | ||
40 | v = readl_relaxed(base + DIV_CTRL1) | DIV_CTRL1_N_RESET_MASK; | |
41 | writel_relaxed(v, base + DIV_CTRL1); | |
42 | ||
43 | v = (readl_relaxed(base + DIV_CTRL0) & ~(mask | load)) | val; | |
44 | writel_relaxed(v, base + DIV_CTRL0); | |
45 | writel_relaxed(v | load, base + DIV_CTRL0); | |
46 | ndelay(250); | |
47 | writel_relaxed(v, base + DIV_CTRL0); | |
48 | } | |
49 | ||
50 | static unsigned int dove_get_divider(struct dove_clk *dc) | |
51 | { | |
52 | unsigned int divider; | |
53 | u32 val; | |
54 | ||
55 | val = readl_relaxed(dc->base + DIV_CTRL0); | |
56 | val >>= dc->div_bit_start; | |
57 | ||
58 | divider = val & ~(~0 << dc->div_bit_size); | |
59 | ||
60 | if (dc->divider_table) | |
61 | divider = dc->divider_table[divider]; | |
62 | ||
63 | return divider; | |
64 | } | |
65 | ||
66 | static int dove_calc_divider(const struct dove_clk *dc, unsigned long rate, | |
67 | unsigned long parent_rate, bool set) | |
68 | { | |
69 | unsigned int divider, max; | |
70 | ||
71 | divider = DIV_ROUND_CLOSEST(parent_rate, rate); | |
72 | ||
73 | if (dc->divider_table) { | |
74 | unsigned int i; | |
75 | ||
76 | for (i = 0; dc->divider_table[i]; i++) | |
77 | if (divider == dc->divider_table[i]) { | |
78 | divider = i; | |
79 | break; | |
80 | } | |
81 | ||
82 | if (!dc->divider_table[i]) | |
83 | return -EINVAL; | |
84 | } else { | |
85 | max = 1 << dc->div_bit_size; | |
86 | ||
87 | if (set && (divider == 0 || divider >= max)) | |
88 | return -EINVAL; | |
89 | if (divider >= max) | |
90 | divider = max - 1; | |
91 | else if (divider == 0) | |
92 | divider = 1; | |
93 | } | |
94 | ||
95 | return divider; | |
96 | } | |
97 | ||
98 | static unsigned long dove_recalc_rate(struct clk_hw *hw, unsigned long parent) | |
99 | { | |
100 | struct dove_clk *dc = to_dove_clk(hw); | |
101 | unsigned int divider = dove_get_divider(dc); | |
102 | unsigned long rate = DIV_ROUND_CLOSEST(parent, divider); | |
103 | ||
104 | pr_debug("%s(): %s divider=%u parent=%lu rate=%lu\n", | |
105 | __func__, dc->name, divider, parent, rate); | |
106 | ||
107 | return rate; | |
108 | } | |
109 | ||
110 | static long dove_round_rate(struct clk_hw *hw, unsigned long rate, | |
111 | unsigned long *parent) | |
112 | { | |
113 | struct dove_clk *dc = to_dove_clk(hw); | |
114 | unsigned long parent_rate = *parent; | |
115 | int divider; | |
116 | ||
117 | divider = dove_calc_divider(dc, rate, parent_rate, false); | |
118 | if (divider < 0) | |
119 | return divider; | |
120 | ||
121 | rate = DIV_ROUND_CLOSEST(parent_rate, divider); | |
122 | ||
123 | pr_debug("%s(): %s divider=%u parent=%lu rate=%lu\n", | |
124 | __func__, dc->name, divider, parent_rate, rate); | |
125 | ||
126 | return rate; | |
127 | } | |
128 | ||
129 | static int dove_set_clock(struct clk_hw *hw, unsigned long rate, | |
130 | unsigned long parent_rate) | |
131 | { | |
132 | struct dove_clk *dc = to_dove_clk(hw); | |
133 | u32 mask, load, div; | |
134 | int divider; | |
135 | ||
136 | divider = dove_calc_divider(dc, rate, parent_rate, true); | |
137 | if (divider < 0) | |
138 | return divider; | |
139 | ||
140 | pr_debug("%s(): %s divider=%u parent=%lu rate=%lu\n", | |
141 | __func__, dc->name, divider, parent_rate, rate); | |
142 | ||
143 | div = (u32)divider << dc->div_bit_start; | |
144 | mask = ~(~0 << dc->div_bit_size) << dc->div_bit_start; | |
145 | load = BIT(dc->div_bit_load); | |
146 | ||
147 | spin_lock(dc->lock); | |
148 | dove_load_divider(dc->base, div, mask, load); | |
149 | spin_unlock(dc->lock); | |
150 | ||
151 | return 0; | |
152 | } | |
153 | ||
154 | static const struct clk_ops dove_divider_ops = { | |
155 | .set_rate = dove_set_clock, | |
156 | .round_rate = dove_round_rate, | |
157 | .recalc_rate = dove_recalc_rate, | |
158 | }; | |
159 | ||
160 | static struct clk *clk_register_dove_divider(struct device *dev, | |
161 | struct dove_clk *dc, const char **parent_names, size_t num_parents, | |
162 | void __iomem *base) | |
163 | { | |
164 | char name[32]; | |
165 | struct clk_init_data init = { | |
166 | .name = name, | |
167 | .ops = &dove_divider_ops, | |
168 | .parent_names = parent_names, | |
169 | .num_parents = num_parents, | |
170 | }; | |
171 | ||
172 | strlcpy(name, dc->name, sizeof(name)); | |
173 | ||
174 | dc->hw.init = &init; | |
175 | dc->base = base; | |
176 | dc->div_bit_size = dc->div_bit_end - dc->div_bit_start + 1; | |
177 | ||
178 | return clk_register(dev, &dc->hw); | |
179 | } | |
180 | ||
181 | static DEFINE_SPINLOCK(dove_divider_lock); | |
182 | ||
183 | static u32 axi_divider[] = {-1, 2, 1, 3, 4, 6, 5, 7, 8, 10, 9, 0}; | |
184 | ||
185 | static struct dove_clk dove_hw_clocks[4] = { | |
186 | { | |
187 | .name = "axi", | |
188 | .lock = &dove_divider_lock, | |
189 | .div_bit_start = 1, | |
190 | .div_bit_end = 6, | |
191 | .div_bit_load = 7, | |
192 | .divider_table = axi_divider, | |
193 | }, { | |
194 | .name = "gpu", | |
195 | .lock = &dove_divider_lock, | |
196 | .div_bit_start = 8, | |
197 | .div_bit_end = 13, | |
198 | .div_bit_load = 14, | |
199 | }, { | |
200 | .name = "vmeta", | |
201 | .lock = &dove_divider_lock, | |
202 | .div_bit_start = 15, | |
203 | .div_bit_end = 20, | |
204 | .div_bit_load = 21, | |
205 | }, { | |
206 | .name = "lcd", | |
207 | .lock = &dove_divider_lock, | |
208 | .div_bit_start = 22, | |
209 | .div_bit_end = 27, | |
210 | .div_bit_load = 28, | |
211 | }, | |
212 | }; | |
213 | ||
214 | static const char *core_pll[] = { | |
215 | "core-pll", | |
216 | }; | |
217 | ||
218 | static int dove_divider_init(struct device *dev, void __iomem *base, | |
219 | struct clk **clks) | |
220 | { | |
221 | struct clk *clk; | |
222 | int i; | |
223 | ||
224 | /* | |
225 | * Create the core PLL clock. We treat this as a fixed rate | |
226 | * clock as we don't know any better, and documentation is sparse. | |
227 | */ | |
228 | clk = clk_register_fixed_rate(dev, core_pll[0], NULL, CLK_IS_ROOT, | |
229 | 2000000000UL); | |
230 | if (IS_ERR(clk)) | |
231 | return PTR_ERR(clk); | |
232 | ||
233 | for (i = 0; i < ARRAY_SIZE(dove_hw_clocks); i++) | |
234 | clks[i] = clk_register_dove_divider(dev, &dove_hw_clocks[i], | |
235 | core_pll, | |
236 | ARRAY_SIZE(core_pll), base); | |
237 | ||
238 | return 0; | |
239 | } | |
240 | ||
241 | static struct clk *dove_divider_clocks[4]; | |
242 | ||
243 | static struct clk_onecell_data dove_divider_data = { | |
244 | .clks = dove_divider_clocks, | |
245 | .clk_num = ARRAY_SIZE(dove_divider_clocks), | |
246 | }; | |
247 | ||
248 | void __init dove_divider_clk_init(struct device_node *np) | |
249 | { | |
1ce133ec | 250 | void __iomem *base; |
63b8d92c RK |
251 | |
252 | base = of_iomap(np, 0); | |
253 | if (WARN_ON(!base)) | |
254 | return; | |
255 | ||
256 | if (WARN_ON(dove_divider_init(NULL, base, dove_divider_clocks))) { | |
257 | iounmap(base); | |
258 | return; | |
259 | } | |
260 | ||
261 | of_clk_add_provider(np, of_clk_src_onecell_get, &dove_divider_data); | |
262 | } |