aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGregory CLEMENT <gregory.clement@free-electrons.com>2016-07-19 09:42:22 -0400
committerStephen Boyd <sboyd@codeaurora.org>2016-08-15 17:10:27 -0400
commit8ca4746a78abc39cc0496654068eaaadb0f3c4d0 (patch)
treec17fa40b5cebcc7c9c1502e29fd2f2fe21272790
parentc6d591c14e45e7ca8e158f2e0e5449371e23055c (diff)
clk: mvebu: Add the peripheral clock driver for Armada 3700
These clocks are the ones which will be used as source for the peripherals of the Armada 3700 SoC. On this SoC there is two blocks of clocks: the North bridge one and the South bridge one. Most of them are gatable. Most of the time their rate are their parent rated divided by a ratio depending of two registers. Their parent can be choose between the TBG clocks for most of them. However, some of them can't choose their parent or directly depend of the xtal clocks. Other ones do not use exactly the same pattern to find the ratio between their parent rate and their rate. For these reason each clock is a composite clock and the operations they use are different depending of the clock. According to the datasheet it would be possible to select the parent clock and the ratio, however currently the driver does not support it. Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com> Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
-rw-r--r--drivers/clk/mvebu/Makefile1
-rw-r--r--drivers/clk/mvebu/armada-37xx-periph.c449
2 files changed, 450 insertions, 0 deletions
diff --git a/drivers/clk/mvebu/Makefile b/drivers/clk/mvebu/Makefile
index 72e3512a9d4a..d9ae97fb43c4 100644
--- a/drivers/clk/mvebu/Makefile
+++ b/drivers/clk/mvebu/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_ARMADA_38X_CLK) += armada-38x.o
8obj-$(CONFIG_ARMADA_39X_CLK) += armada-39x.o 8obj-$(CONFIG_ARMADA_39X_CLK) += armada-39x.o
9obj-$(CONFIG_ARMADA_37XX_CLK) += armada-37xx-xtal.o 9obj-$(CONFIG_ARMADA_37XX_CLK) += armada-37xx-xtal.o
10obj-$(CONFIG_ARMADA_37XX_CLK) += armada-37xx-tbg.o 10obj-$(CONFIG_ARMADA_37XX_CLK) += armada-37xx-tbg.o
11obj-$(CONFIG_ARMADA_37XX_CLK) += armada-37xx-periph.o
11obj-$(CONFIG_ARMADA_XP_CLK) += armada-xp.o 12obj-$(CONFIG_ARMADA_XP_CLK) += armada-xp.o
12obj-$(CONFIG_ARMADA_AP806_SYSCON) += ap806-system-controller.o 13obj-$(CONFIG_ARMADA_AP806_SYSCON) += ap806-system-controller.o
13obj-$(CONFIG_ARMADA_CP110_SYSCON) += cp110-system-controller.o 14obj-$(CONFIG_ARMADA_CP110_SYSCON) += cp110-system-controller.o
diff --git a/drivers/clk/mvebu/armada-37xx-periph.c b/drivers/clk/mvebu/armada-37xx-periph.c
new file mode 100644
index 000000000000..5bb13c9bb802
--- /dev/null
+++ b/drivers/clk/mvebu/armada-37xx-periph.c
@@ -0,0 +1,449 @@
1/*
2 * Marvell Armada 37xx SoC Peripheral clocks
3 *
4 * Copyright (C) 2016 Marvell
5 *
6 * Gregory CLEMENT <gregory.clement@free-electrons.com>
7 *
8 * This file is licensed under the terms of the GNU General Public
9 * License version 2 or later. This program is licensed "as is"
10 * without any warranty of any kind, whether express or implied.
11 *
12 * Most of the peripheral clocks can be modelled like this:
13 * _____ _______ _______
14 * TBG-A-P --| | | | | | ______
15 * TBG-B-P --| Mux |--| /div1 |--| /div2 |--| Gate |--> perip_clk
16 * TBG-A-S --| | | | | | |______|
17 * TBG-B-S --|_____| |_______| |_______|
18 *
19 * However some clocks may use only one or two block or and use the
20 * xtal clock as parent.
21 */
22
23#include <linux/clk-provider.h>
24#include <linux/of.h>
25#include <linux/of_device.h>
26#include <linux/platform_device.h>
27#include <linux/slab.h>
28
29#define TBG_SEL 0x0
30#define DIV_SEL0 0x4
31#define DIV_SEL1 0x8
32#define DIV_SEL2 0xC
33#define CLK_SEL 0x10
34#define CLK_DIS 0x14
35
36struct clk_periph_driver_data {
37 struct clk_hw_onecell_data *hw_data;
38 spinlock_t lock;
39};
40
41struct clk_double_div {
42 struct clk_hw hw;
43 void __iomem *reg1;
44 u8 shift1;
45 void __iomem *reg2;
46 u8 shift2;
47};
48
49#define to_clk_double_div(_hw) container_of(_hw, struct clk_double_div, hw)
50
51struct clk_periph_data {
52 const char *name;
53 const char * const *parent_names;
54 int num_parents;
55 struct clk_hw *mux_hw;
56 struct clk_hw *rate_hw;
57 struct clk_hw *gate_hw;
58 bool is_double_div;
59};
60
61static const struct clk_div_table clk_table6[] = {
62 { .val = 1, .div = 1, },
63 { .val = 2, .div = 2, },
64 { .val = 3, .div = 3, },
65 { .val = 4, .div = 4, },
66 { .val = 5, .div = 5, },
67 { .val = 6, .div = 6, },
68 { .val = 0, .div = 0, }, /* last entry */
69};
70
71static const struct clk_div_table clk_table1[] = {
72 { .val = 0, .div = 1, },
73 { .val = 1, .div = 2, },
74 { .val = 0, .div = 0, }, /* last entry */
75};
76
77static const struct clk_div_table clk_table2[] = {
78 { .val = 0, .div = 2, },
79 { .val = 1, .div = 4, },
80 { .val = 0, .div = 0, }, /* last entry */
81};
82static const struct clk_ops clk_double_div_ops;
83
84#define PERIPH_GATE(_name, _bit) \
85struct clk_gate gate_##_name = { \
86 .reg = (void *)CLK_DIS, \
87 .bit_idx = _bit, \
88 .hw.init = &(struct clk_init_data){ \
89 .ops = &clk_gate_ops, \
90 } \
91};
92
93#define PERIPH_MUX(_name, _shift) \
94struct clk_mux mux_##_name = { \
95 .reg = (void *)TBG_SEL, \
96 .shift = _shift, \
97 .mask = 3, \
98 .hw.init = &(struct clk_init_data){ \
99 .ops = &clk_mux_ro_ops, \
100 } \
101};
102
103#define PERIPH_DOUBLEDIV(_name, _reg1, _reg2, _shift1, _shift2) \
104struct clk_double_div rate_##_name = { \
105 .reg1 = (void *)_reg1, \
106 .reg2 = (void *)_reg2, \
107 .shift1 = _shift1, \
108 .shift2 = _shift2, \
109 .hw.init = &(struct clk_init_data){ \
110 .ops = &clk_double_div_ops, \
111 } \
112};
113
114#define PERIPH_DIV(_name, _reg, _shift, _table) \
115struct clk_divider rate_##_name = { \
116 .reg = (void *)_reg, \
117 .table = _table, \
118 .shift = _shift, \
119 .hw.init = &(struct clk_init_data){ \
120 .ops = &clk_divider_ro_ops, \
121 } \
122};
123
124#define PERIPH_CLK_FULL_DD(_name, _bit, _shift, _reg1, _reg2, _shift1, _shift2)\
125static PERIPH_GATE(_name, _bit); \
126static PERIPH_MUX(_name, _shift); \
127static PERIPH_DOUBLEDIV(_name, _reg1, _reg2, _shift1, _shift2);
128
129#define PERIPH_CLK_FULL(_name, _bit, _shift, _reg, _shift1, _table) \
130static PERIPH_GATE(_name, _bit); \
131static PERIPH_MUX(_name, _shift); \
132static PERIPH_DIV(_name, _reg, _shift1, _table);
133
134#define PERIPH_CLK_GATE_DIV(_name, _bit, _reg, _shift, _table) \
135static PERIPH_GATE(_name, _bit); \
136static PERIPH_DIV(_name, _reg, _shift, _table);
137
138#define PERIPH_CLK_MUX_DIV(_name, _shift, _reg, _shift_div, _table) \
139static PERIPH_MUX(_name, _shift); \
140static PERIPH_DIV(_name, _reg, _shift_div, _table);
141
142#define PERIPH_CLK_MUX_DD(_name, _shift, _reg1, _reg2, _shift1, _shift2)\
143static PERIPH_MUX(_name, _shift); \
144static PERIPH_DOUBLEDIV(_name, _reg1, _reg2, _shift1, _shift2);
145
146#define REF_CLK_FULL(_name) \
147 { .name = #_name, \
148 .parent_names = (const char *[]){ "TBG-A-P", \
149 "TBG-B-P", "TBG-A-S", "TBG-B-S"}, \
150 .num_parents = 4, \
151 .mux_hw = &mux_##_name.hw, \
152 .gate_hw = &gate_##_name.hw, \
153 .rate_hw = &rate_##_name.hw, \
154 }
155
156#define REF_CLK_FULL_DD(_name) \
157 { .name = #_name, \
158 .parent_names = (const char *[]){ "TBG-A-P", \
159 "TBG-B-P", "TBG-A-S", "TBG-B-S"}, \
160 .num_parents = 4, \
161 .mux_hw = &mux_##_name.hw, \
162 .gate_hw = &gate_##_name.hw, \
163 .rate_hw = &rate_##_name.hw, \
164 .is_double_div = true, \
165 }
166
167#define REF_CLK_GATE(_name, _parent_name) \
168 { .name = #_name, \
169 .parent_names = (const char *[]){ _parent_name}, \
170 .num_parents = 1, \
171 .gate_hw = &gate_##_name.hw, \
172 }
173
174#define REF_CLK_GATE_DIV(_name, _parent_name) \
175 { .name = #_name, \
176 .parent_names = (const char *[]){ _parent_name}, \
177 .num_parents = 1, \
178 .gate_hw = &gate_##_name.hw, \
179 .rate_hw = &rate_##_name.hw, \
180 }
181
182#define REF_CLK_MUX_DIV(_name) \
183 { .name = #_name, \
184 .parent_names = (const char *[]){ "TBG-A-P", \
185 "TBG-B-P", "TBG-A-S", "TBG-B-S"}, \
186 .num_parents = 4, \
187 .mux_hw = &mux_##_name.hw, \
188 .rate_hw = &rate_##_name.hw, \
189 }
190
191#define REF_CLK_MUX_DD(_name) \
192 { .name = #_name, \
193 .parent_names = (const char *[]){ "TBG-A-P", \
194 "TBG-B-P", "TBG-A-S", "TBG-B-S"}, \
195 .num_parents = 4, \
196 .mux_hw = &mux_##_name.hw, \
197 .rate_hw = &rate_##_name.hw, \
198 .is_double_div = true, \
199 }
200
201/* NB periph clocks */
202PERIPH_CLK_FULL_DD(mmc, 2, 0, DIV_SEL2, DIV_SEL2, 16, 13);
203PERIPH_CLK_FULL_DD(sata_host, 3, 2, DIV_SEL2, DIV_SEL2, 10, 7);
204PERIPH_CLK_FULL_DD(sec_at, 6, 4, DIV_SEL1, DIV_SEL1, 3, 0);
205PERIPH_CLK_FULL_DD(sec_dap, 7, 6, DIV_SEL1, DIV_SEL1, 9, 6);
206PERIPH_CLK_FULL_DD(tscem, 8, 8, DIV_SEL1, DIV_SEL1, 15, 12);
207PERIPH_CLK_FULL(tscem_tmx, 10, 10, DIV_SEL1, 18, clk_table6);
208static PERIPH_GATE(avs, 11);
209PERIPH_CLK_FULL_DD(pwm, 13, 14, DIV_SEL0, DIV_SEL0, 3, 0);
210PERIPH_CLK_FULL_DD(sqf, 12, 12, DIV_SEL1, DIV_SEL1, 27, 24);
211static PERIPH_GATE(i2c_2, 16);
212static PERIPH_GATE(i2c_1, 17);
213PERIPH_CLK_GATE_DIV(ddr_phy, 19, DIV_SEL0, 18, clk_table2);
214PERIPH_CLK_FULL_DD(ddr_fclk, 21, 16, DIV_SEL0, DIV_SEL0, 15, 12);
215PERIPH_CLK_FULL(trace, 22, 18, DIV_SEL0, 20, clk_table6);
216PERIPH_CLK_FULL(counter, 23, 20, DIV_SEL0, 23, clk_table6);
217PERIPH_CLK_FULL_DD(eip97, 24, 24, DIV_SEL2, DIV_SEL2, 22, 19);
218PERIPH_CLK_MUX_DIV(cpu, 22, DIV_SEL0, 28, clk_table6);
219
220static struct clk_periph_data data_nb[] ={
221 REF_CLK_FULL_DD(mmc),
222 REF_CLK_FULL_DD(sata_host),
223 REF_CLK_FULL_DD(sec_at),
224 REF_CLK_FULL_DD(sec_dap),
225 REF_CLK_FULL_DD(tscem),
226 REF_CLK_FULL(tscem_tmx),
227 REF_CLK_GATE(avs, "xtal"),
228 REF_CLK_FULL_DD(sqf),
229 REF_CLK_FULL_DD(pwm),
230 REF_CLK_GATE(i2c_2, "xtal"),
231 REF_CLK_GATE(i2c_1, "xtal"),
232 REF_CLK_GATE_DIV(ddr_phy, "TBG-A-S"),
233 REF_CLK_FULL_DD(ddr_fclk),
234 REF_CLK_FULL(trace),
235 REF_CLK_FULL(counter),
236 REF_CLK_FULL_DD(eip97),
237 REF_CLK_MUX_DIV(cpu),
238 { },
239};
240
241/* SB periph clocks */
242PERIPH_CLK_MUX_DD(gbe_50, 6, DIV_SEL2, DIV_SEL2, 6, 9);
243PERIPH_CLK_MUX_DD(gbe_core, 8, DIV_SEL1, DIV_SEL1, 18, 21);
244PERIPH_CLK_MUX_DD(gbe_125, 10, DIV_SEL1, DIV_SEL1, 6, 9);
245static PERIPH_GATE(gbe1_50, 0);
246static PERIPH_GATE(gbe0_50, 1);
247static PERIPH_GATE(gbe1_125, 2);
248static PERIPH_GATE(gbe0_125, 3);
249PERIPH_CLK_GATE_DIV(gbe1_core, 4, DIV_SEL1, 13, clk_table1);
250PERIPH_CLK_GATE_DIV(gbe0_core, 5, DIV_SEL1, 14, clk_table1);
251PERIPH_CLK_GATE_DIV(gbe_bm, 12, DIV_SEL1, 0, clk_table1);
252PERIPH_CLK_FULL_DD(sdio, 11, 14, DIV_SEL0, DIV_SEL0, 3, 6);
253PERIPH_CLK_FULL_DD(usb32_usb2_sys, 16, 16, DIV_SEL0, DIV_SEL0, 9, 12);
254PERIPH_CLK_FULL_DD(usb32_ss_sys, 17, 18, DIV_SEL0, DIV_SEL0, 15, 18);
255
256static struct clk_periph_data data_sb[] = {
257 REF_CLK_MUX_DD(gbe_50),
258 REF_CLK_MUX_DD(gbe_core),
259 REF_CLK_MUX_DD(gbe_125),
260 REF_CLK_GATE(gbe1_50, "gbe_50"),
261 REF_CLK_GATE(gbe0_50, "gbe_50"),
262 REF_CLK_GATE(gbe1_125, "gbe_125"),
263 REF_CLK_GATE(gbe0_125, "gbe_125"),
264 REF_CLK_GATE_DIV(gbe1_core, "gbe_core"),
265 REF_CLK_GATE_DIV(gbe0_core, "gbe_core"),
266 REF_CLK_GATE_DIV(gbe_bm, "gbe_core"),
267 REF_CLK_FULL_DD(sdio),
268 REF_CLK_FULL_DD(usb32_usb2_sys),
269 REF_CLK_FULL_DD(usb32_ss_sys),
270 { },
271};
272
273static unsigned int get_div(void __iomem *reg, int shift)
274{
275 u32 val;
276
277 val = (readl(reg) >> shift) & 0x7;
278 if (val > 6)
279 return 0;
280 return val;
281}
282
283static unsigned long clk_double_div_recalc_rate(struct clk_hw *hw,
284 unsigned long parent_rate)
285{
286 struct clk_double_div *double_div = to_clk_double_div(hw);
287 unsigned int div;
288
289 div = get_div(double_div->reg1, double_div->shift1);
290 div *= get_div(double_div->reg2, double_div->shift2);
291
292 return DIV_ROUND_UP_ULL((u64)parent_rate, div);
293}
294
295static const struct clk_ops clk_double_div_ops = {
296 .recalc_rate = clk_double_div_recalc_rate,
297};
298
299static const struct of_device_id armada_3700_periph_clock_of_match[] = {
300 { .compatible = "marvell,armada-3700-periph-clock-nb",
301 .data = data_nb, },
302 { .compatible = "marvell,armada-3700-periph-clock-sb",
303 .data = data_sb, },
304 { }
305};
306static int armada_3700_add_composite_clk(const struct clk_periph_data *data,
307 void __iomem *reg, spinlock_t *lock,
308 struct device *dev, struct clk_hw *hw)
309{
310 const struct clk_ops *mux_ops = NULL, *gate_ops = NULL,
311 *rate_ops = NULL;
312 struct clk_hw *mux_hw = NULL, *gate_hw = NULL, *rate_hw = NULL;
313
314 if (data->mux_hw) {
315 struct clk_mux *mux;
316
317 mux_hw = data->mux_hw;
318 mux = to_clk_mux(mux_hw);
319 mux->lock = lock;
320 mux_ops = mux_hw->init->ops;
321 mux->reg = reg + (u64)mux->reg;
322 }
323
324 if (data->gate_hw) {
325 struct clk_gate *gate;
326
327 gate_hw = data->gate_hw;
328 gate = to_clk_gate(gate_hw);
329 gate->lock = lock;
330 gate_ops = gate_hw->init->ops;
331 gate->reg = reg + (u64)gate->reg;
332 }
333
334 if (data->rate_hw) {
335 rate_hw = data->rate_hw;
336 rate_ops = rate_hw->init->ops;
337 if (data->is_double_div) {
338 struct clk_double_div *rate;
339
340 rate = to_clk_double_div(rate_hw);
341 rate->reg1 = reg + (u64)rate->reg1;
342 rate->reg2 = reg + (u64)rate->reg2;
343 } else {
344 struct clk_divider *rate = to_clk_divider(rate_hw);
345 const struct clk_div_table *clkt;
346 int table_size = 0;
347
348 rate->reg = reg + (u64)rate->reg;
349 for (clkt = rate->table; clkt->div; clkt++)
350 table_size++;
351 rate->width = order_base_2(table_size);
352 rate->lock = lock;
353 }
354 }
355
356 hw = clk_hw_register_composite(dev, data->name, data->parent_names,
357 data->num_parents, mux_hw,
358 mux_ops, rate_hw, rate_ops,
359 gate_hw, gate_ops, CLK_IGNORE_UNUSED);
360
361 if (IS_ERR(hw))
362 return PTR_ERR(hw);
363
364 return 0;
365}
366
367static int armada_3700_periph_clock_probe(struct platform_device *pdev)
368{
369 struct clk_periph_driver_data *driver_data;
370 struct device_node *np = pdev->dev.of_node;
371 const struct clk_periph_data *data;
372 struct device *dev = &pdev->dev;
373 int num_periph = 0, i, ret;
374 struct resource *res;
375 void __iomem *reg;
376
377 data = of_device_get_match_data(dev);
378 if (!data)
379 return -ENODEV;
380
381 while (data[num_periph].name)
382 num_periph++;
383
384 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
385 reg = devm_ioremap_resource(dev, res);
386 if (IS_ERR(reg)) {
387 dev_err(dev, "Could not map the periph clock registers\n");
388 return PTR_ERR(reg);
389 }
390
391 driver_data = devm_kzalloc(dev, sizeof(*driver_data), GFP_KERNEL);
392 if (!driver_data)
393 return -ENOMEM;
394
395 driver_data->hw_data = devm_kzalloc(dev, sizeof(*driver_data->hw_data) +
396 sizeof(*driver_data->hw_data->hws) * num_periph,
397 GFP_KERNEL);
398 if (!driver_data->hw_data)
399 return -ENOMEM;
400 driver_data->hw_data->num = num_periph;
401
402 spin_lock_init(&driver_data->lock);
403
404 for (i = 0; i < num_periph; i++) {
405 struct clk_hw *hw = driver_data->hw_data->hws[i];
406
407 if (armada_3700_add_composite_clk(&data[i], reg,
408 &driver_data->lock, dev, hw))
409 dev_err(dev, "Can't register periph clock %s\n",
410 data[i].name);
411
412 }
413
414 ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get,
415 driver_data->hw_data);
416 if (ret) {
417 for (i = 0; i < num_periph; i++)
418 clk_hw_unregister(driver_data->hw_data->hws[i]);
419 return ret;
420 }
421
422 platform_set_drvdata(pdev, driver_data);
423 return 0;
424}
425
426static int armada_3700_periph_clock_remove(struct platform_device *pdev)
427{
428 struct clk_periph_driver_data *data = platform_get_drvdata(pdev);
429 struct clk_hw_onecell_data *hw_data = data->hw_data;
430 int i;
431
432 of_clk_del_provider(pdev->dev.of_node);
433
434 for (i = 0; i < hw_data->num; i++)
435 clk_hw_unregister(hw_data->hws[i]);
436
437 return 0;
438}
439
440static struct platform_driver armada_3700_periph_clock_driver = {
441 .probe = armada_3700_periph_clock_probe,
442 .remove = armada_3700_periph_clock_remove,
443 .driver = {
444 .name = "marvell-armada-3700-periph-clock",
445 .of_match_table = armada_3700_periph_clock_of_match,
446 },
447};
448
449builtin_platform_driver(armada_3700_periph_clock_driver);