aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/st/Makefile1
-rw-r--r--drivers/clk/st/clkgen-mux.c529
3 files changed, 531 insertions, 0 deletions
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index b5221b712387..85a33e6d765c 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -45,6 +45,7 @@ obj-$(CONFIG_ARCH_SHMOBILE_MULTI) += shmobile/
45obj-$(CONFIG_ARCH_SIRF) += sirf/ 45obj-$(CONFIG_ARCH_SIRF) += sirf/
46obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/ 46obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/
47obj-$(CONFIG_PLAT_SPEAR) += spear/ 47obj-$(CONFIG_PLAT_SPEAR) += spear/
48obj-$(CONFIG_ARCH_STI) += st/
48obj-$(CONFIG_ARCH_SUNXI) += sunxi/ 49obj-$(CONFIG_ARCH_SUNXI) += sunxi/
49obj-$(CONFIG_ARCH_TEGRA) += tegra/ 50obj-$(CONFIG_ARCH_TEGRA) += tegra/
50obj-$(CONFIG_ARCH_OMAP2PLUS) += ti/ 51obj-$(CONFIG_ARCH_OMAP2PLUS) += ti/
diff --git a/drivers/clk/st/Makefile b/drivers/clk/st/Makefile
new file mode 100644
index 000000000000..e42b370edef9
--- /dev/null
+++ b/drivers/clk/st/Makefile
@@ -0,0 +1 @@
obj-y += clkgen-mux.o
diff --git a/drivers/clk/st/clkgen-mux.c b/drivers/clk/st/clkgen-mux.c
new file mode 100644
index 000000000000..65d702c2df8d
--- /dev/null
+++ b/drivers/clk/st/clkgen-mux.c
@@ -0,0 +1,529 @@
1/*
2 * clkgen-mux.c: ST GEN-MUX Clock driver
3 *
4 * Copyright (C) 2014 STMicroelectronics (R&D) Limited
5 *
6 * Authors: Stephen Gallimore <stephen.gallimore@st.com>
7 * Pankaj Dev <pankaj.dev@st.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 */
15
16#include <linux/slab.h>
17#include <linux/of_address.h>
18#include <linux/clk-provider.h>
19
20static DEFINE_SPINLOCK(clkgena_divmux_lock);
21
22static const char ** __init clkgen_mux_get_parents(struct device_node *np,
23 int *num_parents)
24{
25 const char **parents;
26 int nparents, i;
27
28 nparents = of_count_phandle_with_args(np, "clocks", "#clock-cells");
29 if (WARN_ON(nparents <= 0))
30 return ERR_PTR(-EINVAL);
31
32 parents = kzalloc(nparents * sizeof(const char *), GFP_KERNEL);
33 if (!parents)
34 return ERR_PTR(-ENOMEM);
35
36 for (i = 0; i < nparents; i++)
37 parents[i] = of_clk_get_parent_name(np, i);
38
39 *num_parents = nparents;
40 return parents;
41}
42
43/**
44 * DOC: Clock mux with a programmable divider on each of its three inputs.
45 * The mux has an input setting which effectively gates its output.
46 *
47 * Traits of this clock:
48 * prepare - clk_(un)prepare only ensures parent is (un)prepared
49 * enable - clk_enable and clk_disable are functional & control gating
50 * rate - set rate is supported
51 * parent - set/get parent
52 */
53
54#define NUM_INPUTS 3
55
56struct clkgena_divmux {
57 struct clk_hw hw;
58 /* Subclassed mux and divider structures */
59 struct clk_mux mux;
60 struct clk_divider div[NUM_INPUTS];
61 /* Enable/running feedback register bits for each input */
62 void __iomem *feedback_reg[NUM_INPUTS];
63 int feedback_bit_idx;
64
65 u8 muxsel;
66};
67
68#define to_clkgena_divmux(_hw) container_of(_hw, struct clkgena_divmux, hw)
69
70struct clkgena_divmux_data {
71 int num_outputs;
72 int mux_offset;
73 int mux_offset2;
74 int mux_start_bit;
75 int div_offsets[NUM_INPUTS];
76 int fb_offsets[NUM_INPUTS];
77 int fb_start_bit_idx;
78};
79
80#define CKGAX_CLKOPSRC_SWITCH_OFF 0x3
81
82static int clkgena_divmux_is_running(struct clkgena_divmux *mux)
83{
84 u32 regval = readl(mux->feedback_reg[mux->muxsel]);
85 u32 running = regval & BIT(mux->feedback_bit_idx);
86 return !!running;
87}
88
89static int clkgena_divmux_enable(struct clk_hw *hw)
90{
91 struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
92 struct clk_hw *mux_hw = &genamux->mux.hw;
93 unsigned long timeout;
94 int ret = 0;
95
96 mux_hw->clk = hw->clk;
97
98 ret = clk_mux_ops.set_parent(mux_hw, genamux->muxsel);
99 if (ret)
100 return ret;
101
102 timeout = jiffies + msecs_to_jiffies(10);
103
104 while (!clkgena_divmux_is_running(genamux)) {
105 if (time_after(jiffies, timeout))
106 return -ETIMEDOUT;
107 cpu_relax();
108 }
109
110 return 0;
111}
112
113static void clkgena_divmux_disable(struct clk_hw *hw)
114{
115 struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
116 struct clk_hw *mux_hw = &genamux->mux.hw;
117
118 mux_hw->clk = hw->clk;
119
120 clk_mux_ops.set_parent(mux_hw, CKGAX_CLKOPSRC_SWITCH_OFF);
121}
122
123static int clkgena_divmux_is_enabled(struct clk_hw *hw)
124{
125 struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
126 struct clk_hw *mux_hw = &genamux->mux.hw;
127
128 mux_hw->clk = hw->clk;
129
130 return (s8)clk_mux_ops.get_parent(mux_hw) > 0;
131}
132
133u8 clkgena_divmux_get_parent(struct clk_hw *hw)
134{
135 struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
136 struct clk_hw *mux_hw = &genamux->mux.hw;
137
138 mux_hw->clk = hw->clk;
139
140 genamux->muxsel = clk_mux_ops.get_parent(mux_hw);
141 if ((s8)genamux->muxsel < 0) {
142 pr_debug("%s: %s: Invalid parent, setting to default.\n",
143 __func__, __clk_get_name(hw->clk));
144 genamux->muxsel = 0;
145 }
146
147 return genamux->muxsel;
148}
149
150static int clkgena_divmux_set_parent(struct clk_hw *hw, u8 index)
151{
152 struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
153
154 if (index >= CKGAX_CLKOPSRC_SWITCH_OFF)
155 return -EINVAL;
156
157 genamux->muxsel = index;
158
159 /*
160 * If the mux is already enabled, call enable directly to set the
161 * new mux position and wait for it to start running again. Otherwise
162 * do nothing.
163 */
164 if (clkgena_divmux_is_enabled(hw))
165 clkgena_divmux_enable(hw);
166
167 return 0;
168}
169
170unsigned long clkgena_divmux_recalc_rate(struct clk_hw *hw,
171 unsigned long parent_rate)
172{
173 struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
174 struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw;
175
176 div_hw->clk = hw->clk;
177
178 return clk_divider_ops.recalc_rate(div_hw, parent_rate);
179}
180
181static int clkgena_divmux_set_rate(struct clk_hw *hw, unsigned long rate,
182 unsigned long parent_rate)
183{
184 struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
185 struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw;
186
187 div_hw->clk = hw->clk;
188
189 return clk_divider_ops.set_rate(div_hw, rate, parent_rate);
190}
191
192static long clkgena_divmux_round_rate(struct clk_hw *hw, unsigned long rate,
193 unsigned long *prate)
194{
195 struct clkgena_divmux *genamux = to_clkgena_divmux(hw);
196 struct clk_hw *div_hw = &genamux->div[genamux->muxsel].hw;
197
198 div_hw->clk = hw->clk;
199
200 return clk_divider_ops.round_rate(div_hw, rate, prate);
201}
202
203static const struct clk_ops clkgena_divmux_ops = {
204 .enable = clkgena_divmux_enable,
205 .disable = clkgena_divmux_disable,
206 .is_enabled = clkgena_divmux_is_enabled,
207 .get_parent = clkgena_divmux_get_parent,
208 .set_parent = clkgena_divmux_set_parent,
209 .round_rate = clkgena_divmux_round_rate,
210 .recalc_rate = clkgena_divmux_recalc_rate,
211 .set_rate = clkgena_divmux_set_rate,
212};
213
214/**
215 * clk_register_genamux - register a genamux clock with the clock framework
216 */
217struct clk *clk_register_genamux(const char *name,
218 const char **parent_names, u8 num_parents,
219 void __iomem *reg,
220 const struct clkgena_divmux_data *muxdata,
221 u32 idx)
222{
223 /*
224 * Fixed constants across all ClockgenA variants
225 */
226 const int mux_width = 2;
227 const int divider_width = 5;
228 struct clkgena_divmux *genamux;
229 struct clk *clk;
230 struct clk_init_data init;
231 int i;
232
233 genamux = kzalloc(sizeof(*genamux), GFP_KERNEL);
234 if (!genamux)
235 return ERR_PTR(-ENOMEM);
236
237 init.name = name;
238 init.ops = &clkgena_divmux_ops;
239 init.flags = CLK_IS_BASIC;
240 init.parent_names = parent_names;
241 init.num_parents = num_parents;
242
243 genamux->mux.lock = &clkgena_divmux_lock;
244 genamux->mux.mask = BIT(mux_width) - 1;
245 genamux->mux.shift = muxdata->mux_start_bit + (idx * mux_width);
246 if (genamux->mux.shift > 31) {
247 /*
248 * We have spilled into the second mux register so
249 * adjust the register address and the bit shift accordingly
250 */
251 genamux->mux.reg = reg + muxdata->mux_offset2;
252 genamux->mux.shift -= 32;
253 } else {
254 genamux->mux.reg = reg + muxdata->mux_offset;
255 }
256
257 for (i = 0; i < NUM_INPUTS; i++) {
258 /*
259 * Divider config for each input
260 */
261 void __iomem *divbase = reg + muxdata->div_offsets[i];
262 genamux->div[i].width = divider_width;
263 genamux->div[i].reg = divbase + (idx * sizeof(u32));
264
265 /*
266 * Mux enabled/running feedback register for each input.
267 */
268 genamux->feedback_reg[i] = reg + muxdata->fb_offsets[i];
269 }
270
271 genamux->feedback_bit_idx = muxdata->fb_start_bit_idx + idx;
272 genamux->hw.init = &init;
273
274 clk = clk_register(NULL, &genamux->hw);
275 if (IS_ERR(clk)) {
276 kfree(genamux);
277 goto err;
278 }
279
280 pr_debug("%s: parent %s rate %lu\n",
281 __clk_get_name(clk),
282 __clk_get_name(clk_get_parent(clk)),
283 clk_get_rate(clk));
284err:
285 return clk;
286}
287
288static struct clkgena_divmux_data st_divmux_c65hs = {
289 .num_outputs = 4,
290 .mux_offset = 0x14,
291 .mux_start_bit = 0,
292 .div_offsets = { 0x800, 0x900, 0xb00 },
293 .fb_offsets = { 0x18, 0x1c, 0x20 },
294 .fb_start_bit_idx = 0,
295};
296
297static struct clkgena_divmux_data st_divmux_c65ls = {
298 .num_outputs = 14,
299 .mux_offset = 0x14,
300 .mux_offset2 = 0x24,
301 .mux_start_bit = 8,
302 .div_offsets = { 0x810, 0xa10, 0xb10 },
303 .fb_offsets = { 0x18, 0x1c, 0x20 },
304 .fb_start_bit_idx = 4,
305};
306
307static struct clkgena_divmux_data st_divmux_c32odf0 = {
308 .num_outputs = 8,
309 .mux_offset = 0x1c,
310 .mux_start_bit = 0,
311 .div_offsets = { 0x800, 0x900, 0xa60 },
312 .fb_offsets = { 0x2c, 0x24, 0x28 },
313 .fb_start_bit_idx = 0,
314};
315
316static struct clkgena_divmux_data st_divmux_c32odf1 = {
317 .num_outputs = 8,
318 .mux_offset = 0x1c,
319 .mux_start_bit = 16,
320 .div_offsets = { 0x820, 0x980, 0xa80 },
321 .fb_offsets = { 0x2c, 0x24, 0x28 },
322 .fb_start_bit_idx = 8,
323};
324
325static struct clkgena_divmux_data st_divmux_c32odf2 = {
326 .num_outputs = 8,
327 .mux_offset = 0x20,
328 .mux_start_bit = 0,
329 .div_offsets = { 0x840, 0xa20, 0xb10 },
330 .fb_offsets = { 0x2c, 0x24, 0x28 },
331 .fb_start_bit_idx = 16,
332};
333
334static struct clkgena_divmux_data st_divmux_c32odf3 = {
335 .num_outputs = 8,
336 .mux_offset = 0x20,
337 .mux_start_bit = 16,
338 .div_offsets = { 0x860, 0xa40, 0xb30 },
339 .fb_offsets = { 0x2c, 0x24, 0x28 },
340 .fb_start_bit_idx = 24,
341};
342
343static struct of_device_id clkgena_divmux_of_match[] = {
344 {
345 .compatible = "st,clkgena-divmux-c65-hs",
346 .data = &st_divmux_c65hs,
347 },
348 {
349 .compatible = "st,clkgena-divmux-c65-ls",
350 .data = &st_divmux_c65ls,
351 },
352 {
353 .compatible = "st,clkgena-divmux-c32-odf0",
354 .data = &st_divmux_c32odf0,
355 },
356 {
357 .compatible = "st,clkgena-divmux-c32-odf1",
358 .data = &st_divmux_c32odf1,
359 },
360 {
361 .compatible = "st,clkgena-divmux-c32-odf2",
362 .data = &st_divmux_c32odf2,
363 },
364 {
365 .compatible = "st,clkgena-divmux-c32-odf3",
366 .data = &st_divmux_c32odf3,
367 },
368 {}
369};
370
371static void __iomem * __init clkgen_get_register_base(
372 struct device_node *np)
373{
374 struct device_node *pnode;
375 void __iomem *reg = NULL;
376
377 pnode = of_get_parent(np);
378 if (!pnode)
379 return NULL;
380
381 reg = of_iomap(pnode, 0);
382
383 of_node_put(pnode);
384 return reg;
385}
386
387void __init st_of_clkgena_divmux_setup(struct device_node *np)
388{
389 const struct of_device_id *match;
390 const struct clkgena_divmux_data *data;
391 struct clk_onecell_data *clk_data;
392 void __iomem *reg;
393 const char **parents;
394 int num_parents = 0, i;
395
396 match = of_match_node(clkgena_divmux_of_match, np);
397 if (WARN_ON(!match))
398 return;
399
400 data = (struct clkgena_divmux_data *)match->data;
401
402 reg = clkgen_get_register_base(np);
403 if (!reg)
404 return;
405
406 parents = clkgen_mux_get_parents(np, &num_parents);
407 if (IS_ERR(parents))
408 return;
409
410 clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
411 if (!clk_data)
412 goto err;
413
414 clk_data->clk_num = data->num_outputs;
415 clk_data->clks = kzalloc(clk_data->clk_num * sizeof(struct clk *),
416 GFP_KERNEL);
417
418 if (!clk_data->clks)
419 goto err;
420
421 for (i = 0; i < clk_data->clk_num; i++) {
422 struct clk *clk;
423 const char *clk_name;
424
425 if (of_property_read_string_index(np, "clock-output-names",
426 i, &clk_name))
427 break;
428
429 /*
430 * If we read an empty clock name then the output is unused
431 */
432 if (*clk_name == '\0')
433 continue;
434
435 clk = clk_register_genamux(clk_name, parents, num_parents,
436 reg, data, i);
437
438 if (IS_ERR(clk))
439 goto err;
440
441 clk_data->clks[i] = clk;
442 }
443
444 kfree(parents);
445
446 of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
447 return;
448err:
449 if (clk_data)
450 kfree(clk_data->clks);
451
452 kfree(clk_data);
453 kfree(parents);
454}
455CLK_OF_DECLARE(clkgenadivmux, "st,clkgena-divmux", st_of_clkgena_divmux_setup);
456
457struct clkgena_prediv_data {
458 u32 offset;
459 u8 shift;
460 struct clk_div_table *table;
461};
462
463static struct clk_div_table prediv_table16[] = {
464 { .val = 0, .div = 1 },
465 { .val = 1, .div = 16 },
466 { .div = 0 },
467};
468
469static struct clkgena_prediv_data prediv_c65_data = {
470 .offset = 0x4c,
471 .shift = 31,
472 .table = prediv_table16,
473};
474
475static struct clkgena_prediv_data prediv_c32_data = {
476 .offset = 0x50,
477 .shift = 1,
478 .table = prediv_table16,
479};
480
481static struct of_device_id clkgena_prediv_of_match[] = {
482 { .compatible = "st,clkgena-prediv-c65", .data = &prediv_c65_data },
483 { .compatible = "st,clkgena-prediv-c32", .data = &prediv_c32_data },
484 {}
485};
486
487void __init st_of_clkgena_prediv_setup(struct device_node *np)
488{
489 const struct of_device_id *match;
490 void __iomem *reg;
491 const char *parent_name, *clk_name;
492 struct clk *clk;
493 struct clkgena_prediv_data *data;
494
495 match = of_match_node(clkgena_prediv_of_match, np);
496 if (!match) {
497 pr_err("%s: No matching data\n", __func__);
498 return;
499 }
500
501 data = (struct clkgena_prediv_data *)match->data;
502
503 reg = clkgen_get_register_base(np);
504 if (!reg)
505 return;
506
507 parent_name = of_clk_get_parent_name(np, 0);
508 if (!parent_name)
509 return;
510
511 if (of_property_read_string_index(np, "clock-output-names",
512 0, &clk_name))
513 return;
514
515 clk = clk_register_divider_table(NULL, clk_name, parent_name, 0,
516 reg + data->offset, data->shift, 1,
517 0, data->table, NULL);
518 if (IS_ERR(clk))
519 return;
520
521 of_clk_add_provider(np, of_clk_src_simple_get, clk);
522 pr_debug("%s: parent %s rate %u\n",
523 __clk_get_name(clk),
524 __clk_get_name(clk_get_parent(clk)),
525 (unsigned int)clk_get_rate(clk));
526
527 return;
528}
529CLK_OF_DECLARE(clkgenaprediv, "st,clkgena-prediv", st_of_clkgena_prediv_setup);