summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/powerpc/platforms/Kconfig.cputype1
-rw-r--r--drivers/clk/Kconfig7
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/clk-ppc-corenet.c280
4 files changed, 289 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype
index 54f3936001aa..7819c40a6bc3 100644
--- a/arch/powerpc/platforms/Kconfig.cputype
+++ b/arch/powerpc/platforms/Kconfig.cputype
@@ -158,6 +158,7 @@ config E500
158config PPC_E500MC 158config PPC_E500MC
159 bool "e500mc Support" 159 bool "e500mc Support"
160 select PPC_FPU 160 select PPC_FPU
161 select COMMON_CLK
161 depends on E500 162 depends on E500
162 help 163 help
163 This must be enabled for running on e500mc (and derivatives 164 This must be enabled for running on e500mc (and derivatives
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 0357ac44638b..30fc2b48447c 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -81,6 +81,13 @@ config COMMON_CLK_AXI_CLKGEN
81 Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx 81 Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx
82 FPGAs. It is commonly used in Analog Devices' reference designs. 82 FPGAs. It is commonly used in Analog Devices' reference designs.
83 83
84config CLK_PPC_CORENET
85 bool "Clock driver for PowerPC corenet platforms"
86 depends on PPC_E500MC && OF
87 ---help---
88 This adds the clock driver support for Freescale PowerPC corenet
89 platforms using common clock framework.
90
84endmenu 91endmenu
85 92
86source "drivers/clk/mvebu/Kconfig" 93source "drivers/clk/mvebu/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 137d3e730f86..3a26115bb0f9 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -39,3 +39,4 @@ obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
39obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o 39obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
40obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o 40obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o
41obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o 41obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o
42obj-$(CONFIG_CLK_PPC_CORENET) += clk-ppc-corenet.o
diff --git a/drivers/clk/clk-ppc-corenet.c b/drivers/clk/clk-ppc-corenet.c
new file mode 100644
index 000000000000..a2d483fd9134
--- /dev/null
+++ b/drivers/clk/clk-ppc-corenet.c
@@ -0,0 +1,280 @@
1/*
2 * Copyright 2013 Freescale Semiconductor, Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * clock driver for Freescale PowerPC corenet SoCs.
9 */
10#include <linux/clk-provider.h>
11#include <linux/io.h>
12#include <linux/kernel.h>
13#include <linux/module.h>
14#include <linux/of_platform.h>
15#include <linux/of.h>
16#include <linux/slab.h>
17
18struct cmux_clk {
19 struct clk_hw hw;
20 void __iomem *reg;
21 u32 flags;
22};
23
24#define PLL_KILL BIT(31)
25#define CLKSEL_SHIFT 27
26#define CLKSEL_ADJUST BIT(0)
27#define to_cmux_clk(p) container_of(p, struct cmux_clk, hw)
28
29static void __iomem *base;
30static unsigned int clocks_per_pll;
31
32static int cmux_set_parent(struct clk_hw *hw, u8 idx)
33{
34 struct cmux_clk *clk = to_cmux_clk(hw);
35 u32 clksel;
36
37 clksel = ((idx / clocks_per_pll) << 2) + idx % clocks_per_pll;
38 if (clk->flags & CLKSEL_ADJUST)
39 clksel += 8;
40 clksel = (clksel & 0xf) << CLKSEL_SHIFT;
41 iowrite32be(clksel, clk->reg);
42
43 return 0;
44}
45
46static u8 cmux_get_parent(struct clk_hw *hw)
47{
48 struct cmux_clk *clk = to_cmux_clk(hw);
49 u32 clksel;
50
51 clksel = ioread32be(clk->reg);
52 clksel = (clksel >> CLKSEL_SHIFT) & 0xf;
53 if (clk->flags & CLKSEL_ADJUST)
54 clksel -= 8;
55 clksel = (clksel >> 2) * clocks_per_pll + clksel % 4;
56
57 return clksel;
58}
59
60const struct clk_ops cmux_ops = {
61 .get_parent = cmux_get_parent,
62 .set_parent = cmux_set_parent,
63};
64
65static void __init core_mux_init(struct device_node *np)
66{
67 struct clk *clk;
68 struct clk_init_data init;
69 struct cmux_clk *cmux_clk;
70 struct device_node *node;
71 int rc, count, i;
72 u32 offset;
73 const char *clk_name;
74 const char **parent_names;
75
76 rc = of_property_read_u32(np, "reg", &offset);
77 if (rc) {
78 pr_err("%s: could not get reg property\n", np->name);
79 return;
80 }
81
82 /* get the input clock source count */
83 count = of_property_count_strings(np, "clock-names");
84 if (count < 0) {
85 pr_err("%s: get clock count error\n", np->name);
86 return;
87 }
88 parent_names = kzalloc((sizeof(char *) * count), GFP_KERNEL);
89 if (!parent_names) {
90 pr_err("%s: could not allocate parent_names\n", __func__);
91 return;
92 }
93
94 for (i = 0; i < count; i++)
95 parent_names[i] = of_clk_get_parent_name(np, i);
96
97 cmux_clk = kzalloc(sizeof(struct cmux_clk), GFP_KERNEL);
98 if (!cmux_clk) {
99 pr_err("%s: could not allocate cmux_clk\n", __func__);
100 goto err_name;
101 }
102 cmux_clk->reg = base + offset;
103
104 node = of_find_compatible_node(NULL, NULL, "fsl,p4080-clockgen");
105 if (node && (offset >= 0x80))
106 cmux_clk->flags = CLKSEL_ADJUST;
107
108 rc = of_property_read_string_index(np, "clock-output-names",
109 0, &clk_name);
110 if (rc) {
111 pr_err("%s: read clock names error\n", np->name);
112 goto err_clk;
113 }
114
115 init.name = clk_name;
116 init.ops = &cmux_ops;
117 init.parent_names = parent_names;
118 init.num_parents = count;
119 init.flags = 0;
120 cmux_clk->hw.init = &init;
121
122 clk = clk_register(NULL, &cmux_clk->hw);
123 if (IS_ERR(clk)) {
124 pr_err("%s: could not register clock\n", clk_name);
125 goto err_clk;
126 }
127
128 rc = of_clk_add_provider(np, of_clk_src_simple_get, clk);
129 if (rc) {
130 pr_err("Could not register clock provider for node:%s\n",
131 np->name);
132 goto err_clk;
133 }
134 goto err_name;
135
136err_clk:
137 kfree(cmux_clk);
138err_name:
139 /* free *_names because they are reallocated when registered */
140 kfree(parent_names);
141}
142
143static void __init core_pll_init(struct device_node *np)
144{
145 u32 offset, mult;
146 int i, rc, count;
147 const char *clk_name, *parent_name;
148 struct clk_onecell_data *onecell_data;
149 struct clk **subclks;
150
151 rc = of_property_read_u32(np, "reg", &offset);
152 if (rc) {
153 pr_err("%s: could not get reg property\n", np->name);
154 return;
155 }
156
157 /* get the multiple of PLL */
158 mult = ioread32be(base + offset);
159
160 /* check if this PLL is disabled */
161 if (mult & PLL_KILL) {
162 pr_debug("PLL:%s is disabled\n", np->name);
163 return;
164 }
165 mult = (mult >> 1) & 0x3f;
166
167 parent_name = of_clk_get_parent_name(np, 0);
168 if (!parent_name) {
169 pr_err("PLL: %s must have a parent\n", np->name);
170 return;
171 }
172
173 count = of_property_count_strings(np, "clock-output-names");
174 if (count < 0 || count > 4) {
175 pr_err("%s: clock is not supported\n", np->name);
176 return;
177 }
178
179 /* output clock number per PLL */
180 clocks_per_pll = count;
181
182 subclks = kzalloc(sizeof(struct clk *) * count, GFP_KERNEL);
183 if (!subclks) {
184 pr_err("%s: could not allocate subclks\n", __func__);
185 return;
186 }
187
188 onecell_data = kzalloc(sizeof(struct clk_onecell_data), GFP_KERNEL);
189 if (!onecell_data) {
190 pr_err("%s: could not allocate onecell_data\n", __func__);
191 goto err_clks;
192 }
193
194 for (i = 0; i < count; i++) {
195 rc = of_property_read_string_index(np, "clock-output-names",
196 i, &clk_name);
197 if (rc) {
198 pr_err("%s: could not get clock names\n", np->name);
199 goto err_cell;
200 }
201
202 /*
203 * when count == 4, there are 4 output clocks:
204 * /1, /2, /3, /4 respectively
205 * when count < 4, there are at least 2 output clocks:
206 * /1, /2, (/4, if count == 3) respectively.
207 */
208 if (count == 4)
209 subclks[i] = clk_register_fixed_factor(NULL, clk_name,
210 parent_name, 0, mult, 1 + i);
211 else
212
213 subclks[i] = clk_register_fixed_factor(NULL, clk_name,
214 parent_name, 0, mult, 1 << i);
215
216 if (IS_ERR(subclks[i])) {
217 pr_err("%s: could not register clock\n", clk_name);
218 goto err_cell;
219 }
220 }
221
222 onecell_data->clks = subclks;
223 onecell_data->clk_num = count;
224
225 rc = of_clk_add_provider(np, of_clk_src_onecell_get, onecell_data);
226 if (rc) {
227 pr_err("Could not register clk provider for node:%s\n",
228 np->name);
229 goto err_cell;
230 }
231
232 return;
233err_cell:
234 kfree(onecell_data);
235err_clks:
236 kfree(subclks);
237}
238
239static const struct of_device_id clk_match[] __initconst = {
240 { .compatible = "fixed-clock", .data = of_fixed_clk_setup, },
241 { .compatible = "fsl,core-pll-clock", .data = core_pll_init, },
242 { .compatible = "fsl,core-mux-clock", .data = core_mux_init, },
243 {}
244};
245
246static int __init ppc_corenet_clk_probe(struct platform_device *pdev)
247{
248 struct device_node *np;
249
250 np = pdev->dev.of_node;
251 base = of_iomap(np, 0);
252 if (!base) {
253 dev_err(&pdev->dev, "iomap error\n");
254 return -ENOMEM;
255 }
256 of_clk_init(clk_match);
257
258 return 0;
259}
260
261static const struct of_device_id ppc_clk_ids[] __initconst = {
262 { .compatible = "fsl,qoriq-clockgen-1.0", },
263 { .compatible = "fsl,qoriq-clockgen-2", },
264 {}
265};
266
267static struct platform_driver ppc_corenet_clk_driver = {
268 .driver = {
269 .name = "ppc_corenet_clock",
270 .owner = THIS_MODULE,
271 .of_match_table = ppc_clk_ids,
272 },
273 .probe = ppc_corenet_clk_probe,
274};
275
276static int __init ppc_corenet_clk_init(void)
277{
278 return platform_driver_register(&ppc_corenet_clk_driver);
279}
280subsys_initcall(ppc_corenet_clk_init);