aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/sh
diff options
context:
space:
mode:
authorPaul Mundt <lethal@linux-sh.org>2012-06-19 23:37:50 -0400
committerPaul Mundt <lethal@linux-sh.org>2012-06-19 23:37:50 -0400
commite1eaf354528ceb002c8e2840a55c44cd76aaaed8 (patch)
tree714f5410ceb924fe7b7447d7a4af58d5433c6a48 /drivers/sh
parent0412ddc82223ea2bb3a9db21355e5fe0862a97e5 (diff)
parent609d7558f232e583a31951c65a6ee43d81c65720 (diff)
Merge branch 'sh/clkfwk' into sh-latest
Diffstat (limited to 'drivers/sh')
-rw-r--r--drivers/sh/clk/cpg.c333
1 files changed, 142 insertions, 191 deletions
diff --git a/drivers/sh/clk/cpg.c b/drivers/sh/clk/cpg.c
index f0d015dd0fef..07e9fb4f8041 100644
--- a/drivers/sh/clk/cpg.c
+++ b/drivers/sh/clk/cpg.c
@@ -14,6 +14,8 @@
14#include <linux/io.h> 14#include <linux/io.h>
15#include <linux/sh_clk.h> 15#include <linux/sh_clk.h>
16 16
17#define CPG_CKSTP_BIT BIT(8)
18
17static unsigned int sh_clk_read(struct clk *clk) 19static unsigned int sh_clk_read(struct clk *clk)
18{ 20{
19 if (clk->flags & CLK_ENABLE_REG_8BIT) 21 if (clk->flags & CLK_ENABLE_REG_8BIT)
@@ -66,71 +68,43 @@ int __init sh_clk_mstp_register(struct clk *clks, int nr)
66 return ret; 68 return ret;
67} 69}
68 70
69static long sh_clk_div_round_rate(struct clk *clk, unsigned long rate) 71/*
72 * Div/mult table lookup helpers
73 */
74static inline struct clk_div_table *clk_to_div_table(struct clk *clk)
70{ 75{
71 return clk_rate_table_round(clk, clk->freq_table, rate); 76 return clk->priv;
72} 77}
73 78
74static int sh_clk_div6_divisors[64] = { 79static inline struct clk_div_mult_table *clk_to_div_mult_table(struct clk *clk)
75 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 80{
76 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 81 return clk_to_div_table(clk)->div_mult_table;
77 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 82}
78 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64
79};
80 83
81static struct clk_div_mult_table sh_clk_div6_table = { 84/*
82 .divisors = sh_clk_div6_divisors, 85 * Common div ops
83 .nr_divisors = ARRAY_SIZE(sh_clk_div6_divisors), 86 */
84}; 87static long sh_clk_div_round_rate(struct clk *clk, unsigned long rate)
88{
89 return clk_rate_table_round(clk, clk->freq_table, rate);
90}
85 91
86static unsigned long sh_clk_div6_recalc(struct clk *clk) 92static unsigned long sh_clk_div_recalc(struct clk *clk)
87{ 93{
88 struct clk_div_mult_table *table = &sh_clk_div6_table; 94 struct clk_div_mult_table *table = clk_to_div_mult_table(clk);
89 unsigned int idx; 95 unsigned int idx;
90 96
91 clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, 97 clk_rate_table_build(clk, clk->freq_table, table->nr_divisors,
92 table, NULL); 98 table, clk->arch_flags ? &clk->arch_flags : NULL);
93 99
94 idx = sh_clk_read(clk) & 0x003f; 100 idx = (sh_clk_read(clk) >> clk->enable_bit) & clk->div_mask;
95 101
96 return clk->freq_table[idx].frequency; 102 return clk->freq_table[idx].frequency;
97} 103}
98 104
99static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent) 105static int sh_clk_div_set_rate(struct clk *clk, unsigned long rate)
100{
101 struct clk_div_mult_table *table = &sh_clk_div6_table;
102 u32 value;
103 int ret, i;
104
105 if (!clk->parent_table || !clk->parent_num)
106 return -EINVAL;
107
108 /* Search the parent */
109 for (i = 0; i < clk->parent_num; i++)
110 if (clk->parent_table[i] == parent)
111 break;
112
113 if (i == clk->parent_num)
114 return -ENODEV;
115
116 ret = clk_reparent(clk, parent);
117 if (ret < 0)
118 return ret;
119
120 value = sh_clk_read(clk) &
121 ~(((1 << clk->src_width) - 1) << clk->src_shift);
122
123 sh_clk_write(value | (i << clk->src_shift), clk);
124
125 /* Rebuild the frequency table */
126 clk_rate_table_build(clk, clk->freq_table, table->nr_divisors,
127 table, NULL);
128
129 return 0;
130}
131
132static int sh_clk_div6_set_rate(struct clk *clk, unsigned long rate)
133{ 106{
107 struct clk_div_table *dt = clk_to_div_table(clk);
134 unsigned long value; 108 unsigned long value;
135 int idx; 109 int idx;
136 110
@@ -139,51 +113,53 @@ static int sh_clk_div6_set_rate(struct clk *clk, unsigned long rate)
139 return idx; 113 return idx;
140 114
141 value = sh_clk_read(clk); 115 value = sh_clk_read(clk);
142 value &= ~0x3f; 116 value &= ~(clk->div_mask << clk->enable_bit);
143 value |= idx; 117 value |= (idx << clk->enable_bit);
144 sh_clk_write(value, clk); 118 sh_clk_write(value, clk);
119
120 /* XXX: Should use a post-change notifier */
121 if (dt->kick)
122 dt->kick(clk);
123
145 return 0; 124 return 0;
146} 125}
147 126
148static int sh_clk_div6_enable(struct clk *clk) 127static int sh_clk_div_enable(struct clk *clk)
149{ 128{
150 unsigned long value; 129 sh_clk_write(sh_clk_read(clk) & ~CPG_CKSTP_BIT, clk);
151 int ret; 130 return 0;
152
153 ret = sh_clk_div6_set_rate(clk, clk->rate);
154 if (ret == 0) {
155 value = sh_clk_read(clk);
156 value &= ~0x100; /* clear stop bit to enable clock */
157 sh_clk_write(value, clk);
158 }
159 return ret;
160} 131}
161 132
162static void sh_clk_div6_disable(struct clk *clk) 133static void sh_clk_div_disable(struct clk *clk)
163{ 134{
164 unsigned long value; 135 unsigned int val;
165 136
166 value = sh_clk_read(clk); 137 val = sh_clk_read(clk);
167 value |= 0x100; /* stop clock */ 138 val |= CPG_CKSTP_BIT;
168 value |= 0x3f; /* VDIV bits must be non-zero, overwrite divider */ 139
169 sh_clk_write(value, clk); 140 /*
141 * div6 clocks require the divisor field to be non-zero or the
142 * above CKSTP toggle silently fails. Ensure that the divisor
143 * array is reset to its initial state on disable.
144 */
145 if (clk->flags & CLK_MASK_DIV_ON_DISABLE)
146 val |= clk->div_mask;
147
148 sh_clk_write(val, clk);
170} 149}
171 150
172static struct sh_clk_ops sh_clk_div6_clk_ops = { 151static struct sh_clk_ops sh_clk_div_clk_ops = {
173 .recalc = sh_clk_div6_recalc, 152 .recalc = sh_clk_div_recalc,
153 .set_rate = sh_clk_div_set_rate,
174 .round_rate = sh_clk_div_round_rate, 154 .round_rate = sh_clk_div_round_rate,
175 .set_rate = sh_clk_div6_set_rate,
176 .enable = sh_clk_div6_enable,
177 .disable = sh_clk_div6_disable,
178}; 155};
179 156
180static struct sh_clk_ops sh_clk_div6_reparent_clk_ops = { 157static struct sh_clk_ops sh_clk_div_enable_clk_ops = {
181 .recalc = sh_clk_div6_recalc, 158 .recalc = sh_clk_div_recalc,
159 .set_rate = sh_clk_div_set_rate,
182 .round_rate = sh_clk_div_round_rate, 160 .round_rate = sh_clk_div_round_rate,
183 .set_rate = sh_clk_div6_set_rate, 161 .enable = sh_clk_div_enable,
184 .enable = sh_clk_div6_enable, 162 .disable = sh_clk_div_disable,
185 .disable = sh_clk_div6_disable,
186 .set_parent = sh_clk_div6_set_parent,
187}; 163};
188 164
189static int __init sh_clk_init_parent(struct clk *clk) 165static int __init sh_clk_init_parent(struct clk *clk)
@@ -218,12 +194,12 @@ static int __init sh_clk_init_parent(struct clk *clk)
218 return 0; 194 return 0;
219} 195}
220 196
221static int __init sh_clk_div6_register_ops(struct clk *clks, int nr, 197static int __init sh_clk_div_register_ops(struct clk *clks, int nr,
222 struct sh_clk_ops *ops) 198 struct clk_div_table *table, struct sh_clk_ops *ops)
223{ 199{
224 struct clk *clkp; 200 struct clk *clkp;
225 void *freq_table; 201 void *freq_table;
226 int nr_divs = sh_clk_div6_table.nr_divisors; 202 int nr_divs = table->div_mult_table->nr_divisors;
227 int freq_table_size = sizeof(struct cpufreq_frequency_table); 203 int freq_table_size = sizeof(struct cpufreq_frequency_table);
228 int ret = 0; 204 int ret = 0;
229 int k; 205 int k;
@@ -231,7 +207,7 @@ static int __init sh_clk_div6_register_ops(struct clk *clks, int nr,
231 freq_table_size *= (nr_divs + 1); 207 freq_table_size *= (nr_divs + 1);
232 freq_table = kzalloc(freq_table_size * nr, GFP_KERNEL); 208 freq_table = kzalloc(freq_table_size * nr, GFP_KERNEL);
233 if (!freq_table) { 209 if (!freq_table) {
234 pr_err("sh_clk_div6_register: unable to alloc memory\n"); 210 pr_err("%s: unable to alloc memory\n", __func__);
235 return -ENOMEM; 211 return -ENOMEM;
236 } 212 }
237 213
@@ -239,47 +215,98 @@ static int __init sh_clk_div6_register_ops(struct clk *clks, int nr,
239 clkp = clks + k; 215 clkp = clks + k;
240 216
241 clkp->ops = ops; 217 clkp->ops = ops;
218 clkp->priv = table;
219
242 clkp->freq_table = freq_table + (k * freq_table_size); 220 clkp->freq_table = freq_table + (k * freq_table_size);
243 clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END; 221 clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END;
244 ret = clk_register(clkp);
245 if (ret < 0)
246 break;
247 222
248 ret = sh_clk_init_parent(clkp); 223 ret = clk_register(clkp);
224 if (ret == 0)
225 ret = sh_clk_init_parent(clkp);
249 } 226 }
250 227
251 return ret; 228 return ret;
252} 229}
253 230
254int __init sh_clk_div6_register(struct clk *clks, int nr) 231/*
255{ 232 * div6 support
256 return sh_clk_div6_register_ops(clks, nr, &sh_clk_div6_clk_ops); 233 */
257} 234static int sh_clk_div6_divisors[64] = {
235 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
236 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
237 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
238 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64
239};
258 240
259int __init sh_clk_div6_reparent_register(struct clk *clks, int nr) 241static struct clk_div_mult_table div6_div_mult_table = {
260{ 242 .divisors = sh_clk_div6_divisors,
261 return sh_clk_div6_register_ops(clks, nr, 243 .nr_divisors = ARRAY_SIZE(sh_clk_div6_divisors),
262 &sh_clk_div6_reparent_clk_ops); 244};
263}
264 245
265static unsigned long sh_clk_div4_recalc(struct clk *clk) 246static struct clk_div_table sh_clk_div6_table = {
247 .div_mult_table = &div6_div_mult_table,
248};
249
250static int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent)
266{ 251{
267 struct clk_div4_table *d4t = clk->priv; 252 struct clk_div_mult_table *table = clk_to_div_mult_table(clk);
268 struct clk_div_mult_table *table = d4t->div_mult_table; 253 u32 value;
269 unsigned int idx; 254 int ret, i;
270 255
256 if (!clk->parent_table || !clk->parent_num)
257 return -EINVAL;
258
259 /* Search the parent */
260 for (i = 0; i < clk->parent_num; i++)
261 if (clk->parent_table[i] == parent)
262 break;
263
264 if (i == clk->parent_num)
265 return -ENODEV;
266
267 ret = clk_reparent(clk, parent);
268 if (ret < 0)
269 return ret;
270
271 value = sh_clk_read(clk) &
272 ~(((1 << clk->src_width) - 1) << clk->src_shift);
273
274 sh_clk_write(value | (i << clk->src_shift), clk);
275
276 /* Rebuild the frequency table */
271 clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, 277 clk_rate_table_build(clk, clk->freq_table, table->nr_divisors,
272 table, &clk->arch_flags); 278 table, NULL);
273 279
274 idx = (sh_clk_read(clk) >> clk->enable_bit) & 0x000f; 280 return 0;
281}
275 282
276 return clk->freq_table[idx].frequency; 283static struct sh_clk_ops sh_clk_div6_reparent_clk_ops = {
284 .recalc = sh_clk_div_recalc,
285 .round_rate = sh_clk_div_round_rate,
286 .set_rate = sh_clk_div_set_rate,
287 .enable = sh_clk_div_enable,
288 .disable = sh_clk_div_disable,
289 .set_parent = sh_clk_div6_set_parent,
290};
291
292int __init sh_clk_div6_register(struct clk *clks, int nr)
293{
294 return sh_clk_div_register_ops(clks, nr, &sh_clk_div6_table,
295 &sh_clk_div_enable_clk_ops);
296}
297
298int __init sh_clk_div6_reparent_register(struct clk *clks, int nr)
299{
300 return sh_clk_div_register_ops(clks, nr, &sh_clk_div6_table,
301 &sh_clk_div6_reparent_clk_ops);
277} 302}
278 303
304/*
305 * div4 support
306 */
279static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent) 307static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent)
280{ 308{
281 struct clk_div4_table *d4t = clk->priv; 309 struct clk_div_mult_table *table = clk_to_div_mult_table(clk);
282 struct clk_div_mult_table *table = d4t->div_mult_table;
283 u32 value; 310 u32 value;
284 int ret; 311 int ret;
285 312
@@ -306,107 +333,31 @@ static int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent)
306 return 0; 333 return 0;
307} 334}
308 335
309static int sh_clk_div4_set_rate(struct clk *clk, unsigned long rate)
310{
311 struct clk_div4_table *d4t = clk->priv;
312 unsigned long value;
313 int idx = clk_rate_table_find(clk, clk->freq_table, rate);
314 if (idx < 0)
315 return idx;
316
317 value = sh_clk_read(clk);
318 value &= ~(0xf << clk->enable_bit);
319 value |= (idx << clk->enable_bit);
320 sh_clk_write(value, clk);
321
322 if (d4t->kick)
323 d4t->kick(clk);
324
325 return 0;
326}
327
328static int sh_clk_div4_enable(struct clk *clk)
329{
330 sh_clk_write(sh_clk_read(clk) & ~(1 << 8), clk);
331 return 0;
332}
333
334static void sh_clk_div4_disable(struct clk *clk)
335{
336 sh_clk_write(sh_clk_read(clk) | (1 << 8), clk);
337}
338
339static struct sh_clk_ops sh_clk_div4_clk_ops = {
340 .recalc = sh_clk_div4_recalc,
341 .set_rate = sh_clk_div4_set_rate,
342 .round_rate = sh_clk_div_round_rate,
343};
344
345static struct sh_clk_ops sh_clk_div4_enable_clk_ops = {
346 .recalc = sh_clk_div4_recalc,
347 .set_rate = sh_clk_div4_set_rate,
348 .round_rate = sh_clk_div_round_rate,
349 .enable = sh_clk_div4_enable,
350 .disable = sh_clk_div4_disable,
351};
352
353static struct sh_clk_ops sh_clk_div4_reparent_clk_ops = { 336static struct sh_clk_ops sh_clk_div4_reparent_clk_ops = {
354 .recalc = sh_clk_div4_recalc, 337 .recalc = sh_clk_div_recalc,
355 .set_rate = sh_clk_div4_set_rate, 338 .set_rate = sh_clk_div_set_rate,
356 .round_rate = sh_clk_div_round_rate, 339 .round_rate = sh_clk_div_round_rate,
357 .enable = sh_clk_div4_enable, 340 .enable = sh_clk_div_enable,
358 .disable = sh_clk_div4_disable, 341 .disable = sh_clk_div_disable,
359 .set_parent = sh_clk_div4_set_parent, 342 .set_parent = sh_clk_div4_set_parent,
360}; 343};
361 344
362static int __init sh_clk_div4_register_ops(struct clk *clks, int nr,
363 struct clk_div4_table *table, struct sh_clk_ops *ops)
364{
365 struct clk *clkp;
366 void *freq_table;
367 int nr_divs = table->div_mult_table->nr_divisors;
368 int freq_table_size = sizeof(struct cpufreq_frequency_table);
369 int ret = 0;
370 int k;
371
372 freq_table_size *= (nr_divs + 1);
373 freq_table = kzalloc(freq_table_size * nr, GFP_KERNEL);
374 if (!freq_table) {
375 pr_err("sh_clk_div4_register: unable to alloc memory\n");
376 return -ENOMEM;
377 }
378
379 for (k = 0; !ret && (k < nr); k++) {
380 clkp = clks + k;
381
382 clkp->ops = ops;
383 clkp->priv = table;
384
385 clkp->freq_table = freq_table + (k * freq_table_size);
386 clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END;
387
388 ret = clk_register(clkp);
389 }
390
391 return ret;
392}
393
394int __init sh_clk_div4_register(struct clk *clks, int nr, 345int __init sh_clk_div4_register(struct clk *clks, int nr,
395 struct clk_div4_table *table) 346 struct clk_div4_table *table)
396{ 347{
397 return sh_clk_div4_register_ops(clks, nr, table, &sh_clk_div4_clk_ops); 348 return sh_clk_div_register_ops(clks, nr, table, &sh_clk_div_clk_ops);
398} 349}
399 350
400int __init sh_clk_div4_enable_register(struct clk *clks, int nr, 351int __init sh_clk_div4_enable_register(struct clk *clks, int nr,
401 struct clk_div4_table *table) 352 struct clk_div4_table *table)
402{ 353{
403 return sh_clk_div4_register_ops(clks, nr, table, 354 return sh_clk_div_register_ops(clks, nr, table,
404 &sh_clk_div4_enable_clk_ops); 355 &sh_clk_div_enable_clk_ops);
405} 356}
406 357
407int __init sh_clk_div4_reparent_register(struct clk *clks, int nr, 358int __init sh_clk_div4_reparent_register(struct clk *clks, int nr,
408 struct clk_div4_table *table) 359 struct clk_div4_table *table)
409{ 360{
410 return sh_clk_div4_register_ops(clks, nr, table, 361 return sh_clk_div_register_ops(clks, nr, table,
411 &sh_clk_div4_reparent_clk_ops); 362 &sh_clk_div4_reparent_clk_ops);
412} 363}