diff options
Diffstat (limited to 'drivers/clk/socfpga')
-rw-r--r-- | drivers/clk/socfpga/Makefile | 3 | ||||
-rw-r--r-- | drivers/clk/socfpga/clk-gate.c | 263 | ||||
-rw-r--r-- | drivers/clk/socfpga/clk-periph.c | 94 | ||||
-rw-r--r-- | drivers/clk/socfpga/clk-pll.c | 131 | ||||
-rw-r--r-- | drivers/clk/socfpga/clk.c | 326 | ||||
-rw-r--r-- | drivers/clk/socfpga/clk.h | 57 |
6 files changed, 560 insertions, 314 deletions
diff --git a/drivers/clk/socfpga/Makefile b/drivers/clk/socfpga/Makefile index 0303c0b99cd0..7e2d15a0c7b8 100644 --- a/drivers/clk/socfpga/Makefile +++ b/drivers/clk/socfpga/Makefile | |||
@@ -1 +1,4 @@ | |||
1 | obj-y += clk.o | 1 | obj-y += clk.o |
2 | obj-y += clk-gate.o | ||
3 | obj-y += clk-pll.o | ||
4 | obj-y += clk-periph.o | ||
diff --git a/drivers/clk/socfpga/clk-gate.c b/drivers/clk/socfpga/clk-gate.c new file mode 100644 index 000000000000..501d513bf890 --- /dev/null +++ b/drivers/clk/socfpga/clk-gate.c | |||
@@ -0,0 +1,263 @@ | |||
1 | /* | ||
2 | * Copyright 2011-2012 Calxeda, Inc. | ||
3 | * Copyright (C) 2012-2013 Altera Corporation <www.altera.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * Based from clk-highbank.c | ||
16 | * | ||
17 | */ | ||
18 | #include <linux/clk.h> | ||
19 | #include <linux/clkdev.h> | ||
20 | #include <linux/clk-provider.h> | ||
21 | #include <linux/io.h> | ||
22 | #include <linux/mfd/syscon.h> | ||
23 | #include <linux/of.h> | ||
24 | #include <linux/regmap.h> | ||
25 | |||
26 | #include "clk.h" | ||
27 | |||
28 | #define SOCFPGA_L4_MP_CLK "l4_mp_clk" | ||
29 | #define SOCFPGA_L4_SP_CLK "l4_sp_clk" | ||
30 | #define SOCFPGA_NAND_CLK "nand_clk" | ||
31 | #define SOCFPGA_NAND_X_CLK "nand_x_clk" | ||
32 | #define SOCFPGA_MMC_CLK "sdmmc_clk" | ||
33 | #define SOCFPGA_GPIO_DB_CLK_OFFSET 0xA8 | ||
34 | |||
35 | #define div_mask(width) ((1 << (width)) - 1) | ||
36 | #define streq(a, b) (strcmp((a), (b)) == 0) | ||
37 | |||
38 | #define to_socfpga_gate_clk(p) container_of(p, struct socfpga_gate_clk, hw.hw) | ||
39 | |||
40 | /* SDMMC Group for System Manager defines */ | ||
41 | #define SYSMGR_SDMMCGRP_CTRL_OFFSET 0x108 | ||
42 | #define SYSMGR_SDMMC_CTRL_SET(smplsel, drvsel) \ | ||
43 | ((((smplsel) & 0x7) << 3) | (((drvsel) & 0x7) << 0)) | ||
44 | |||
45 | static u8 socfpga_clk_get_parent(struct clk_hw *hwclk) | ||
46 | { | ||
47 | u32 l4_src; | ||
48 | u32 perpll_src; | ||
49 | |||
50 | if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) { | ||
51 | l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC); | ||
52 | return l4_src &= 0x1; | ||
53 | } | ||
54 | if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) { | ||
55 | l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC); | ||
56 | return !!(l4_src & 2); | ||
57 | } | ||
58 | |||
59 | perpll_src = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC); | ||
60 | if (streq(hwclk->init->name, SOCFPGA_MMC_CLK)) | ||
61 | return perpll_src &= 0x3; | ||
62 | if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) || | ||
63 | streq(hwclk->init->name, SOCFPGA_NAND_X_CLK)) | ||
64 | return (perpll_src >> 2) & 3; | ||
65 | |||
66 | /* QSPI clock */ | ||
67 | return (perpll_src >> 4) & 3; | ||
68 | |||
69 | } | ||
70 | |||
71 | static int socfpga_clk_set_parent(struct clk_hw *hwclk, u8 parent) | ||
72 | { | ||
73 | u32 src_reg; | ||
74 | |||
75 | if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) { | ||
76 | src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC); | ||
77 | src_reg &= ~0x1; | ||
78 | src_reg |= parent; | ||
79 | writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC); | ||
80 | } else if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) { | ||
81 | src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC); | ||
82 | src_reg &= ~0x2; | ||
83 | src_reg |= (parent << 1); | ||
84 | writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC); | ||
85 | } else { | ||
86 | src_reg = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC); | ||
87 | if (streq(hwclk->init->name, SOCFPGA_MMC_CLK)) { | ||
88 | src_reg &= ~0x3; | ||
89 | src_reg |= parent; | ||
90 | } else if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) || | ||
91 | streq(hwclk->init->name, SOCFPGA_NAND_X_CLK)) { | ||
92 | src_reg &= ~0xC; | ||
93 | src_reg |= (parent << 2); | ||
94 | } else {/* QSPI clock */ | ||
95 | src_reg &= ~0x30; | ||
96 | src_reg |= (parent << 4); | ||
97 | } | ||
98 | writel(src_reg, clk_mgr_base_addr + CLKMGR_PERPLL_SRC); | ||
99 | } | ||
100 | |||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static unsigned long socfpga_clk_recalc_rate(struct clk_hw *hwclk, | ||
105 | unsigned long parent_rate) | ||
106 | { | ||
107 | struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk); | ||
108 | u32 div = 1, val; | ||
109 | |||
110 | if (socfpgaclk->fixed_div) | ||
111 | div = socfpgaclk->fixed_div; | ||
112 | else if (socfpgaclk->div_reg) { | ||
113 | val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift; | ||
114 | val &= div_mask(socfpgaclk->width); | ||
115 | /* Check for GPIO_DB_CLK by its offset */ | ||
116 | if ((int) socfpgaclk->div_reg & SOCFPGA_GPIO_DB_CLK_OFFSET) | ||
117 | div = val + 1; | ||
118 | else | ||
119 | div = (1 << val); | ||
120 | } | ||
121 | |||
122 | return parent_rate / div; | ||
123 | } | ||
124 | |||
125 | static int socfpga_clk_prepare(struct clk_hw *hwclk) | ||
126 | { | ||
127 | struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk); | ||
128 | struct regmap *sys_mgr_base_addr; | ||
129 | int i; | ||
130 | u32 hs_timing; | ||
131 | u32 clk_phase[2]; | ||
132 | |||
133 | if (socfpgaclk->clk_phase[0] || socfpgaclk->clk_phase[1]) { | ||
134 | sys_mgr_base_addr = syscon_regmap_lookup_by_compatible("altr,sys-mgr"); | ||
135 | if (IS_ERR(sys_mgr_base_addr)) { | ||
136 | pr_err("%s: failed to find altr,sys-mgr regmap!\n", __func__); | ||
137 | return -EINVAL; | ||
138 | } | ||
139 | |||
140 | for (i = 0; i < 2; i++) { | ||
141 | switch (socfpgaclk->clk_phase[i]) { | ||
142 | case 0: | ||
143 | clk_phase[i] = 0; | ||
144 | break; | ||
145 | case 45: | ||
146 | clk_phase[i] = 1; | ||
147 | break; | ||
148 | case 90: | ||
149 | clk_phase[i] = 2; | ||
150 | break; | ||
151 | case 135: | ||
152 | clk_phase[i] = 3; | ||
153 | break; | ||
154 | case 180: | ||
155 | clk_phase[i] = 4; | ||
156 | break; | ||
157 | case 225: | ||
158 | clk_phase[i] = 5; | ||
159 | break; | ||
160 | case 270: | ||
161 | clk_phase[i] = 6; | ||
162 | break; | ||
163 | case 315: | ||
164 | clk_phase[i] = 7; | ||
165 | break; | ||
166 | default: | ||
167 | clk_phase[i] = 0; | ||
168 | break; | ||
169 | } | ||
170 | } | ||
171 | hs_timing = SYSMGR_SDMMC_CTRL_SET(clk_phase[0], clk_phase[1]); | ||
172 | regmap_write(sys_mgr_base_addr, SYSMGR_SDMMCGRP_CTRL_OFFSET, | ||
173 | hs_timing); | ||
174 | } | ||
175 | return 0; | ||
176 | } | ||
177 | |||
178 | static struct clk_ops gateclk_ops = { | ||
179 | .prepare = socfpga_clk_prepare, | ||
180 | .recalc_rate = socfpga_clk_recalc_rate, | ||
181 | .get_parent = socfpga_clk_get_parent, | ||
182 | .set_parent = socfpga_clk_set_parent, | ||
183 | }; | ||
184 | |||
185 | static void __init __socfpga_gate_init(struct device_node *node, | ||
186 | const struct clk_ops *ops) | ||
187 | { | ||
188 | u32 clk_gate[2]; | ||
189 | u32 div_reg[3]; | ||
190 | u32 clk_phase[2]; | ||
191 | u32 fixed_div; | ||
192 | struct clk *clk; | ||
193 | struct socfpga_gate_clk *socfpga_clk; | ||
194 | const char *clk_name = node->name; | ||
195 | const char *parent_name[SOCFPGA_MAX_PARENTS]; | ||
196 | struct clk_init_data init; | ||
197 | int rc; | ||
198 | int i = 0; | ||
199 | |||
200 | socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL); | ||
201 | if (WARN_ON(!socfpga_clk)) | ||
202 | return; | ||
203 | |||
204 | rc = of_property_read_u32_array(node, "clk-gate", clk_gate, 2); | ||
205 | if (rc) | ||
206 | clk_gate[0] = 0; | ||
207 | |||
208 | if (clk_gate[0]) { | ||
209 | socfpga_clk->hw.reg = clk_mgr_base_addr + clk_gate[0]; | ||
210 | socfpga_clk->hw.bit_idx = clk_gate[1]; | ||
211 | |||
212 | gateclk_ops.enable = clk_gate_ops.enable; | ||
213 | gateclk_ops.disable = clk_gate_ops.disable; | ||
214 | } | ||
215 | |||
216 | rc = of_property_read_u32(node, "fixed-divider", &fixed_div); | ||
217 | if (rc) | ||
218 | socfpga_clk->fixed_div = 0; | ||
219 | else | ||
220 | socfpga_clk->fixed_div = fixed_div; | ||
221 | |||
222 | rc = of_property_read_u32_array(node, "div-reg", div_reg, 3); | ||
223 | if (!rc) { | ||
224 | socfpga_clk->div_reg = clk_mgr_base_addr + div_reg[0]; | ||
225 | socfpga_clk->shift = div_reg[1]; | ||
226 | socfpga_clk->width = div_reg[2]; | ||
227 | } else { | ||
228 | socfpga_clk->div_reg = 0; | ||
229 | } | ||
230 | |||
231 | rc = of_property_read_u32_array(node, "clk-phase", clk_phase, 2); | ||
232 | if (!rc) { | ||
233 | socfpga_clk->clk_phase[0] = clk_phase[0]; | ||
234 | socfpga_clk->clk_phase[1] = clk_phase[1]; | ||
235 | } | ||
236 | |||
237 | of_property_read_string(node, "clock-output-names", &clk_name); | ||
238 | |||
239 | init.name = clk_name; | ||
240 | init.ops = ops; | ||
241 | init.flags = 0; | ||
242 | while (i < SOCFPGA_MAX_PARENTS && (parent_name[i] = | ||
243 | of_clk_get_parent_name(node, i)) != NULL) | ||
244 | i++; | ||
245 | |||
246 | init.parent_names = parent_name; | ||
247 | init.num_parents = i; | ||
248 | socfpga_clk->hw.hw.init = &init; | ||
249 | |||
250 | clk = clk_register(NULL, &socfpga_clk->hw.hw); | ||
251 | if (WARN_ON(IS_ERR(clk))) { | ||
252 | kfree(socfpga_clk); | ||
253 | return; | ||
254 | } | ||
255 | rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); | ||
256 | if (WARN_ON(rc)) | ||
257 | return; | ||
258 | } | ||
259 | |||
260 | void __init socfpga_gate_init(struct device_node *node) | ||
261 | { | ||
262 | __socfpga_gate_init(node, &gateclk_ops); | ||
263 | } | ||
diff --git a/drivers/clk/socfpga/clk-periph.c b/drivers/clk/socfpga/clk-periph.c new file mode 100644 index 000000000000..81623a3736f9 --- /dev/null +++ b/drivers/clk/socfpga/clk-periph.c | |||
@@ -0,0 +1,94 @@ | |||
1 | /* | ||
2 | * Copyright 2011-2012 Calxeda, Inc. | ||
3 | * Copyright (C) 2012-2013 Altera Corporation <www.altera.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * Based from clk-highbank.c | ||
16 | * | ||
17 | */ | ||
18 | #include <linux/clk.h> | ||
19 | #include <linux/clkdev.h> | ||
20 | #include <linux/clk-provider.h> | ||
21 | #include <linux/io.h> | ||
22 | #include <linux/of.h> | ||
23 | |||
24 | #include "clk.h" | ||
25 | |||
26 | #define to_socfpga_periph_clk(p) container_of(p, struct socfpga_periph_clk, hw.hw) | ||
27 | |||
28 | static unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk, | ||
29 | unsigned long parent_rate) | ||
30 | { | ||
31 | struct socfpga_periph_clk *socfpgaclk = to_socfpga_periph_clk(hwclk); | ||
32 | u32 div; | ||
33 | |||
34 | if (socfpgaclk->fixed_div) | ||
35 | div = socfpgaclk->fixed_div; | ||
36 | else | ||
37 | div = ((readl(socfpgaclk->hw.reg) & 0x1ff) + 1); | ||
38 | |||
39 | return parent_rate / div; | ||
40 | } | ||
41 | |||
42 | static const struct clk_ops periclk_ops = { | ||
43 | .recalc_rate = clk_periclk_recalc_rate, | ||
44 | }; | ||
45 | |||
46 | static __init void __socfpga_periph_init(struct device_node *node, | ||
47 | const struct clk_ops *ops) | ||
48 | { | ||
49 | u32 reg; | ||
50 | struct clk *clk; | ||
51 | struct socfpga_periph_clk *periph_clk; | ||
52 | const char *clk_name = node->name; | ||
53 | const char *parent_name; | ||
54 | struct clk_init_data init; | ||
55 | int rc; | ||
56 | u32 fixed_div; | ||
57 | |||
58 | of_property_read_u32(node, "reg", ®); | ||
59 | |||
60 | periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL); | ||
61 | if (WARN_ON(!periph_clk)) | ||
62 | return; | ||
63 | |||
64 | periph_clk->hw.reg = clk_mgr_base_addr + reg; | ||
65 | |||
66 | rc = of_property_read_u32(node, "fixed-divider", &fixed_div); | ||
67 | if (rc) | ||
68 | periph_clk->fixed_div = 0; | ||
69 | else | ||
70 | periph_clk->fixed_div = fixed_div; | ||
71 | |||
72 | of_property_read_string(node, "clock-output-names", &clk_name); | ||
73 | |||
74 | init.name = clk_name; | ||
75 | init.ops = ops; | ||
76 | init.flags = 0; | ||
77 | parent_name = of_clk_get_parent_name(node, 0); | ||
78 | init.parent_names = &parent_name; | ||
79 | init.num_parents = 1; | ||
80 | |||
81 | periph_clk->hw.hw.init = &init; | ||
82 | |||
83 | clk = clk_register(NULL, &periph_clk->hw.hw); | ||
84 | if (WARN_ON(IS_ERR(clk))) { | ||
85 | kfree(periph_clk); | ||
86 | return; | ||
87 | } | ||
88 | rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); | ||
89 | } | ||
90 | |||
91 | void __init socfpga_periph_init(struct device_node *node) | ||
92 | { | ||
93 | __socfpga_periph_init(node, &periclk_ops); | ||
94 | } | ||
diff --git a/drivers/clk/socfpga/clk-pll.c b/drivers/clk/socfpga/clk-pll.c new file mode 100644 index 000000000000..88dafb5e9627 --- /dev/null +++ b/drivers/clk/socfpga/clk-pll.c | |||
@@ -0,0 +1,131 @@ | |||
1 | /* | ||
2 | * Copyright 2011-2012 Calxeda, Inc. | ||
3 | * Copyright (C) 2012-2013 Altera Corporation <www.altera.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * Based from clk-highbank.c | ||
16 | * | ||
17 | */ | ||
18 | #include <linux/clk.h> | ||
19 | #include <linux/clkdev.h> | ||
20 | #include <linux/clk-provider.h> | ||
21 | #include <linux/io.h> | ||
22 | #include <linux/of.h> | ||
23 | |||
24 | #include "clk.h" | ||
25 | |||
26 | /* Clock bypass bits */ | ||
27 | #define MAINPLL_BYPASS (1<<0) | ||
28 | #define SDRAMPLL_BYPASS (1<<1) | ||
29 | #define SDRAMPLL_SRC_BYPASS (1<<2) | ||
30 | #define PERPLL_BYPASS (1<<3) | ||
31 | #define PERPLL_SRC_BYPASS (1<<4) | ||
32 | |||
33 | #define SOCFPGA_PLL_BG_PWRDWN 0 | ||
34 | #define SOCFPGA_PLL_EXT_ENA 1 | ||
35 | #define SOCFPGA_PLL_PWR_DOWN 2 | ||
36 | #define SOCFPGA_PLL_DIVF_MASK 0x0000FFF8 | ||
37 | #define SOCFPGA_PLL_DIVF_SHIFT 3 | ||
38 | #define SOCFPGA_PLL_DIVQ_MASK 0x003F0000 | ||
39 | #define SOCFPGA_PLL_DIVQ_SHIFT 16 | ||
40 | |||
41 | #define CLK_MGR_PLL_CLK_SRC_SHIFT 22 | ||
42 | #define CLK_MGR_PLL_CLK_SRC_MASK 0x3 | ||
43 | |||
44 | #define to_socfpga_clk(p) container_of(p, struct socfpga_pll, hw.hw) | ||
45 | |||
46 | static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk, | ||
47 | unsigned long parent_rate) | ||
48 | { | ||
49 | struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk); | ||
50 | unsigned long divf, divq, reg; | ||
51 | unsigned long long vco_freq; | ||
52 | unsigned long bypass; | ||
53 | |||
54 | reg = readl(socfpgaclk->hw.reg); | ||
55 | bypass = readl(clk_mgr_base_addr + CLKMGR_BYPASS); | ||
56 | if (bypass & MAINPLL_BYPASS) | ||
57 | return parent_rate; | ||
58 | |||
59 | divf = (reg & SOCFPGA_PLL_DIVF_MASK) >> SOCFPGA_PLL_DIVF_SHIFT; | ||
60 | divq = (reg & SOCFPGA_PLL_DIVQ_MASK) >> SOCFPGA_PLL_DIVQ_SHIFT; | ||
61 | vco_freq = (unsigned long long)parent_rate * (divf + 1); | ||
62 | do_div(vco_freq, (1 + divq)); | ||
63 | return (unsigned long)vco_freq; | ||
64 | } | ||
65 | |||
66 | static u8 clk_pll_get_parent(struct clk_hw *hwclk) | ||
67 | { | ||
68 | u32 pll_src; | ||
69 | struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk); | ||
70 | |||
71 | pll_src = readl(socfpgaclk->hw.reg); | ||
72 | return (pll_src >> CLK_MGR_PLL_CLK_SRC_SHIFT) & | ||
73 | CLK_MGR_PLL_CLK_SRC_MASK; | ||
74 | } | ||
75 | |||
76 | static struct clk_ops clk_pll_ops = { | ||
77 | .recalc_rate = clk_pll_recalc_rate, | ||
78 | .get_parent = clk_pll_get_parent, | ||
79 | }; | ||
80 | |||
81 | static __init struct clk *__socfpga_pll_init(struct device_node *node, | ||
82 | const struct clk_ops *ops) | ||
83 | { | ||
84 | u32 reg; | ||
85 | struct clk *clk; | ||
86 | struct socfpga_pll *pll_clk; | ||
87 | const char *clk_name = node->name; | ||
88 | const char *parent_name[SOCFPGA_MAX_PARENTS]; | ||
89 | struct clk_init_data init; | ||
90 | int rc; | ||
91 | int i = 0; | ||
92 | |||
93 | of_property_read_u32(node, "reg", ®); | ||
94 | |||
95 | pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL); | ||
96 | if (WARN_ON(!pll_clk)) | ||
97 | return NULL; | ||
98 | |||
99 | pll_clk->hw.reg = clk_mgr_base_addr + reg; | ||
100 | |||
101 | of_property_read_string(node, "clock-output-names", &clk_name); | ||
102 | |||
103 | init.name = clk_name; | ||
104 | init.ops = ops; | ||
105 | init.flags = 0; | ||
106 | |||
107 | while (i < SOCFPGA_MAX_PARENTS && (parent_name[i] = | ||
108 | of_clk_get_parent_name(node, i)) != NULL) | ||
109 | i++; | ||
110 | |||
111 | init.num_parents = i; | ||
112 | init.parent_names = parent_name; | ||
113 | pll_clk->hw.hw.init = &init; | ||
114 | |||
115 | pll_clk->hw.bit_idx = SOCFPGA_PLL_EXT_ENA; | ||
116 | clk_pll_ops.enable = clk_gate_ops.enable; | ||
117 | clk_pll_ops.disable = clk_gate_ops.disable; | ||
118 | |||
119 | clk = clk_register(NULL, &pll_clk->hw.hw); | ||
120 | if (WARN_ON(IS_ERR(clk))) { | ||
121 | kfree(pll_clk); | ||
122 | return NULL; | ||
123 | } | ||
124 | rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); | ||
125 | return clk; | ||
126 | } | ||
127 | |||
128 | void __init socfpga_pll_init(struct device_node *node) | ||
129 | { | ||
130 | __socfpga_pll_init(node, &clk_pll_ops); | ||
131 | } | ||
diff --git a/drivers/clk/socfpga/clk.c b/drivers/clk/socfpga/clk.c index 5983a26a8c5f..35a960a993f9 100644 --- a/drivers/clk/socfpga/clk.c +++ b/drivers/clk/socfpga/clk.c | |||
@@ -22,325 +22,23 @@ | |||
22 | #include <linux/clk-provider.h> | 22 | #include <linux/clk-provider.h> |
23 | #include <linux/io.h> | 23 | #include <linux/io.h> |
24 | #include <linux/of.h> | 24 | #include <linux/of.h> |
25 | #include <linux/of_address.h> | ||
25 | 26 | ||
26 | /* Clock Manager offsets */ | 27 | #include "clk.h" |
27 | #define CLKMGR_CTRL 0x0 | ||
28 | #define CLKMGR_BYPASS 0x4 | ||
29 | #define CLKMGR_L4SRC 0x70 | ||
30 | #define CLKMGR_PERPLL_SRC 0xAC | ||
31 | 28 | ||
32 | /* Clock bypass bits */ | 29 | void __iomem *clk_mgr_base_addr; |
33 | #define MAINPLL_BYPASS (1<<0) | ||
34 | #define SDRAMPLL_BYPASS (1<<1) | ||
35 | #define SDRAMPLL_SRC_BYPASS (1<<2) | ||
36 | #define PERPLL_BYPASS (1<<3) | ||
37 | #define PERPLL_SRC_BYPASS (1<<4) | ||
38 | 30 | ||
39 | #define SOCFPGA_PLL_BG_PWRDWN 0 | 31 | static const struct of_device_id socfpga_child_clocks[] __initconst = { |
40 | #define SOCFPGA_PLL_EXT_ENA 1 | 32 | { .compatible = "altr,socfpga-pll-clock", socfpga_pll_init, }, |
41 | #define SOCFPGA_PLL_PWR_DOWN 2 | 33 | { .compatible = "altr,socfpga-perip-clk", socfpga_periph_init, }, |
42 | #define SOCFPGA_PLL_DIVF_MASK 0x0000FFF8 | 34 | { .compatible = "altr,socfpga-gate-clk", socfpga_gate_init, }, |
43 | #define SOCFPGA_PLL_DIVF_SHIFT 3 | 35 | {}, |
44 | #define SOCFPGA_PLL_DIVQ_MASK 0x003F0000 | ||
45 | #define SOCFPGA_PLL_DIVQ_SHIFT 16 | ||
46 | #define SOCFGPA_MAX_PARENTS 3 | ||
47 | |||
48 | #define SOCFPGA_L4_MP_CLK "l4_mp_clk" | ||
49 | #define SOCFPGA_L4_SP_CLK "l4_sp_clk" | ||
50 | #define SOCFPGA_NAND_CLK "nand_clk" | ||
51 | #define SOCFPGA_NAND_X_CLK "nand_x_clk" | ||
52 | #define SOCFPGA_MMC_CLK "sdmmc_clk" | ||
53 | #define SOCFPGA_DB_CLK "gpio_db_clk" | ||
54 | |||
55 | #define div_mask(width) ((1 << (width)) - 1) | ||
56 | #define streq(a, b) (strcmp((a), (b)) == 0) | ||
57 | |||
58 | extern void __iomem *clk_mgr_base_addr; | ||
59 | |||
60 | struct socfpga_clk { | ||
61 | struct clk_gate hw; | ||
62 | char *parent_name; | ||
63 | char *clk_name; | ||
64 | u32 fixed_div; | ||
65 | void __iomem *div_reg; | ||
66 | u32 width; /* only valid if div_reg != 0 */ | ||
67 | u32 shift; /* only valid if div_reg != 0 */ | ||
68 | }; | ||
69 | #define to_socfpga_clk(p) container_of(p, struct socfpga_clk, hw.hw) | ||
70 | |||
71 | static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk, | ||
72 | unsigned long parent_rate) | ||
73 | { | ||
74 | struct socfpga_clk *socfpgaclk = to_socfpga_clk(hwclk); | ||
75 | unsigned long divf, divq, vco_freq, reg; | ||
76 | unsigned long bypass; | ||
77 | |||
78 | reg = readl(socfpgaclk->hw.reg); | ||
79 | bypass = readl(clk_mgr_base_addr + CLKMGR_BYPASS); | ||
80 | if (bypass & MAINPLL_BYPASS) | ||
81 | return parent_rate; | ||
82 | |||
83 | divf = (reg & SOCFPGA_PLL_DIVF_MASK) >> SOCFPGA_PLL_DIVF_SHIFT; | ||
84 | divq = (reg & SOCFPGA_PLL_DIVQ_MASK) >> SOCFPGA_PLL_DIVQ_SHIFT; | ||
85 | vco_freq = parent_rate * (divf + 1); | ||
86 | return vco_freq / (1 + divq); | ||
87 | } | ||
88 | |||
89 | |||
90 | static struct clk_ops clk_pll_ops = { | ||
91 | .recalc_rate = clk_pll_recalc_rate, | ||
92 | }; | ||
93 | |||
94 | static unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk, | ||
95 | unsigned long parent_rate) | ||
96 | { | ||
97 | struct socfpga_clk *socfpgaclk = to_socfpga_clk(hwclk); | ||
98 | u32 div; | ||
99 | |||
100 | if (socfpgaclk->fixed_div) | ||
101 | div = socfpgaclk->fixed_div; | ||
102 | else | ||
103 | div = ((readl(socfpgaclk->hw.reg) & 0x1ff) + 1); | ||
104 | |||
105 | return parent_rate / div; | ||
106 | } | ||
107 | |||
108 | static const struct clk_ops periclk_ops = { | ||
109 | .recalc_rate = clk_periclk_recalc_rate, | ||
110 | }; | ||
111 | |||
112 | static __init struct clk *socfpga_clk_init(struct device_node *node, | ||
113 | const struct clk_ops *ops) | ||
114 | { | ||
115 | u32 reg; | ||
116 | struct clk *clk; | ||
117 | struct socfpga_clk *socfpga_clk; | ||
118 | const char *clk_name = node->name; | ||
119 | const char *parent_name; | ||
120 | struct clk_init_data init; | ||
121 | int rc; | ||
122 | u32 fixed_div; | ||
123 | |||
124 | of_property_read_u32(node, "reg", ®); | ||
125 | |||
126 | socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL); | ||
127 | if (WARN_ON(!socfpga_clk)) | ||
128 | return NULL; | ||
129 | |||
130 | socfpga_clk->hw.reg = clk_mgr_base_addr + reg; | ||
131 | |||
132 | rc = of_property_read_u32(node, "fixed-divider", &fixed_div); | ||
133 | if (rc) | ||
134 | socfpga_clk->fixed_div = 0; | ||
135 | else | ||
136 | socfpga_clk->fixed_div = fixed_div; | ||
137 | |||
138 | of_property_read_string(node, "clock-output-names", &clk_name); | ||
139 | |||
140 | init.name = clk_name; | ||
141 | init.ops = ops; | ||
142 | init.flags = 0; | ||
143 | parent_name = of_clk_get_parent_name(node, 0); | ||
144 | init.parent_names = &parent_name; | ||
145 | init.num_parents = 1; | ||
146 | |||
147 | socfpga_clk->hw.hw.init = &init; | ||
148 | |||
149 | if (streq(clk_name, "main_pll") || | ||
150 | streq(clk_name, "periph_pll") || | ||
151 | streq(clk_name, "sdram_pll")) { | ||
152 | socfpga_clk->hw.bit_idx = SOCFPGA_PLL_EXT_ENA; | ||
153 | clk_pll_ops.enable = clk_gate_ops.enable; | ||
154 | clk_pll_ops.disable = clk_gate_ops.disable; | ||
155 | } | ||
156 | |||
157 | clk = clk_register(NULL, &socfpga_clk->hw.hw); | ||
158 | if (WARN_ON(IS_ERR(clk))) { | ||
159 | kfree(socfpga_clk); | ||
160 | return NULL; | ||
161 | } | ||
162 | rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); | ||
163 | return clk; | ||
164 | } | ||
165 | |||
166 | static u8 socfpga_clk_get_parent(struct clk_hw *hwclk) | ||
167 | { | ||
168 | u32 l4_src; | ||
169 | u32 perpll_src; | ||
170 | |||
171 | if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) { | ||
172 | l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC); | ||
173 | return l4_src &= 0x1; | ||
174 | } | ||
175 | if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) { | ||
176 | l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC); | ||
177 | return !!(l4_src & 2); | ||
178 | } | ||
179 | |||
180 | perpll_src = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC); | ||
181 | if (streq(hwclk->init->name, SOCFPGA_MMC_CLK)) | ||
182 | return perpll_src &= 0x3; | ||
183 | if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) || | ||
184 | streq(hwclk->init->name, SOCFPGA_NAND_X_CLK)) | ||
185 | return (perpll_src >> 2) & 3; | ||
186 | |||
187 | /* QSPI clock */ | ||
188 | return (perpll_src >> 4) & 3; | ||
189 | |||
190 | } | ||
191 | |||
192 | static int socfpga_clk_set_parent(struct clk_hw *hwclk, u8 parent) | ||
193 | { | ||
194 | u32 src_reg; | ||
195 | |||
196 | if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) { | ||
197 | src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC); | ||
198 | src_reg &= ~0x1; | ||
199 | src_reg |= parent; | ||
200 | writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC); | ||
201 | } else if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) { | ||
202 | src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC); | ||
203 | src_reg &= ~0x2; | ||
204 | src_reg |= (parent << 1); | ||
205 | writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC); | ||
206 | } else { | ||
207 | src_reg = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC); | ||
208 | if (streq(hwclk->init->name, SOCFPGA_MMC_CLK)) { | ||
209 | src_reg &= ~0x3; | ||
210 | src_reg |= parent; | ||
211 | } else if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) || | ||
212 | streq(hwclk->init->name, SOCFPGA_NAND_X_CLK)) { | ||
213 | src_reg &= ~0xC; | ||
214 | src_reg |= (parent << 2); | ||
215 | } else {/* QSPI clock */ | ||
216 | src_reg &= ~0x30; | ||
217 | src_reg |= (parent << 4); | ||
218 | } | ||
219 | writel(src_reg, clk_mgr_base_addr + CLKMGR_PERPLL_SRC); | ||
220 | } | ||
221 | |||
222 | return 0; | ||
223 | } | ||
224 | |||
225 | static unsigned long socfpga_clk_recalc_rate(struct clk_hw *hwclk, | ||
226 | unsigned long parent_rate) | ||
227 | { | ||
228 | struct socfpga_clk *socfpgaclk = to_socfpga_clk(hwclk); | ||
229 | u32 div = 1, val; | ||
230 | |||
231 | if (socfpgaclk->fixed_div) | ||
232 | div = socfpgaclk->fixed_div; | ||
233 | else if (socfpgaclk->div_reg) { | ||
234 | val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift; | ||
235 | val &= div_mask(socfpgaclk->width); | ||
236 | if (streq(hwclk->init->name, SOCFPGA_DB_CLK)) | ||
237 | div = val + 1; | ||
238 | else | ||
239 | div = (1 << val); | ||
240 | } | ||
241 | |||
242 | return parent_rate / div; | ||
243 | } | ||
244 | |||
245 | static struct clk_ops gateclk_ops = { | ||
246 | .recalc_rate = socfpga_clk_recalc_rate, | ||
247 | .get_parent = socfpga_clk_get_parent, | ||
248 | .set_parent = socfpga_clk_set_parent, | ||
249 | }; | 36 | }; |
250 | 37 | ||
251 | static void __init socfpga_gate_clk_init(struct device_node *node, | 38 | static void __init socfpga_clkmgr_init(struct device_node *node) |
252 | const struct clk_ops *ops) | ||
253 | { | ||
254 | u32 clk_gate[2]; | ||
255 | u32 div_reg[3]; | ||
256 | u32 fixed_div; | ||
257 | struct clk *clk; | ||
258 | struct socfpga_clk *socfpga_clk; | ||
259 | const char *clk_name = node->name; | ||
260 | const char *parent_name[SOCFGPA_MAX_PARENTS]; | ||
261 | struct clk_init_data init; | ||
262 | int rc; | ||
263 | int i = 0; | ||
264 | |||
265 | socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL); | ||
266 | if (WARN_ON(!socfpga_clk)) | ||
267 | return; | ||
268 | |||
269 | rc = of_property_read_u32_array(node, "clk-gate", clk_gate, 2); | ||
270 | if (rc) | ||
271 | clk_gate[0] = 0; | ||
272 | |||
273 | if (clk_gate[0]) { | ||
274 | socfpga_clk->hw.reg = clk_mgr_base_addr + clk_gate[0]; | ||
275 | socfpga_clk->hw.bit_idx = clk_gate[1]; | ||
276 | |||
277 | gateclk_ops.enable = clk_gate_ops.enable; | ||
278 | gateclk_ops.disable = clk_gate_ops.disable; | ||
279 | } | ||
280 | |||
281 | rc = of_property_read_u32(node, "fixed-divider", &fixed_div); | ||
282 | if (rc) | ||
283 | socfpga_clk->fixed_div = 0; | ||
284 | else | ||
285 | socfpga_clk->fixed_div = fixed_div; | ||
286 | |||
287 | rc = of_property_read_u32_array(node, "div-reg", div_reg, 3); | ||
288 | if (!rc) { | ||
289 | socfpga_clk->div_reg = clk_mgr_base_addr + div_reg[0]; | ||
290 | socfpga_clk->shift = div_reg[1]; | ||
291 | socfpga_clk->width = div_reg[2]; | ||
292 | } else { | ||
293 | socfpga_clk->div_reg = NULL; | ||
294 | } | ||
295 | |||
296 | of_property_read_string(node, "clock-output-names", &clk_name); | ||
297 | |||
298 | init.name = clk_name; | ||
299 | init.ops = ops; | ||
300 | init.flags = 0; | ||
301 | while (i < SOCFGPA_MAX_PARENTS && (parent_name[i] = | ||
302 | of_clk_get_parent_name(node, i)) != NULL) | ||
303 | i++; | ||
304 | |||
305 | init.parent_names = parent_name; | ||
306 | init.num_parents = i; | ||
307 | socfpga_clk->hw.hw.init = &init; | ||
308 | |||
309 | clk = clk_register(NULL, &socfpga_clk->hw.hw); | ||
310 | if (WARN_ON(IS_ERR(clk))) { | ||
311 | kfree(socfpga_clk); | ||
312 | return; | ||
313 | } | ||
314 | rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); | ||
315 | if (WARN_ON(rc)) | ||
316 | return; | ||
317 | } | ||
318 | |||
319 | static void __init socfpga_pll_init(struct device_node *node) | ||
320 | { | 39 | { |
321 | socfpga_clk_init(node, &clk_pll_ops); | 40 | clk_mgr_base_addr = of_iomap(node, 0); |
41 | of_clk_init(socfpga_child_clocks); | ||
322 | } | 42 | } |
323 | CLK_OF_DECLARE(socfpga_pll, "altr,socfpga-pll-clock", socfpga_pll_init); | 43 | CLK_OF_DECLARE(socfpga_mgr, "altr,clk-mgr", socfpga_clkmgr_init); |
324 | 44 | ||
325 | static void __init socfpga_periph_init(struct device_node *node) | ||
326 | { | ||
327 | socfpga_clk_init(node, &periclk_ops); | ||
328 | } | ||
329 | CLK_OF_DECLARE(socfpga_periph, "altr,socfpga-perip-clk", socfpga_periph_init); | ||
330 | |||
331 | static void __init socfpga_gate_init(struct device_node *node) | ||
332 | { | ||
333 | socfpga_gate_clk_init(node, &gateclk_ops); | ||
334 | } | ||
335 | CLK_OF_DECLARE(socfpga_gate, "altr,socfpga-gate-clk", socfpga_gate_init); | ||
336 | |||
337 | void __init socfpga_init_clocks(void) | ||
338 | { | ||
339 | struct clk *clk; | ||
340 | int ret; | ||
341 | |||
342 | clk = clk_register_fixed_factor(NULL, "smp_twd", "mpuclk", 0, 1, 4); | ||
343 | ret = clk_register_clkdev(clk, NULL, "smp_twd"); | ||
344 | if (ret) | ||
345 | pr_err("smp_twd alias not registered\n"); | ||
346 | } | ||
diff --git a/drivers/clk/socfpga/clk.h b/drivers/clk/socfpga/clk.h new file mode 100644 index 000000000000..d2e54019c94f --- /dev/null +++ b/drivers/clk/socfpga/clk.h | |||
@@ -0,0 +1,57 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2013, Steffen Trumtrar <s.trumtrar@pengutronix.de> | ||
3 | * | ||
4 | * based on drivers/clk/tegra/clk.h | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #ifndef __SOCFPGA_CLK_H | ||
18 | #define __SOCFPGA_CLK_H | ||
19 | |||
20 | #include <linux/clk-provider.h> | ||
21 | #include <linux/clkdev.h> | ||
22 | |||
23 | /* Clock Manager offsets */ | ||
24 | #define CLKMGR_CTRL 0x0 | ||
25 | #define CLKMGR_BYPASS 0x4 | ||
26 | #define CLKMGR_L4SRC 0x70 | ||
27 | #define CLKMGR_PERPLL_SRC 0xAC | ||
28 | |||
29 | #define SOCFPGA_MAX_PARENTS 3 | ||
30 | |||
31 | extern void __iomem *clk_mgr_base_addr; | ||
32 | |||
33 | void __init socfpga_pll_init(struct device_node *node); | ||
34 | void __init socfpga_periph_init(struct device_node *node); | ||
35 | void __init socfpga_gate_init(struct device_node *node); | ||
36 | |||
37 | struct socfpga_pll { | ||
38 | struct clk_gate hw; | ||
39 | }; | ||
40 | |||
41 | struct socfpga_gate_clk { | ||
42 | struct clk_gate hw; | ||
43 | char *parent_name; | ||
44 | u32 fixed_div; | ||
45 | void __iomem *div_reg; | ||
46 | u32 width; /* only valid if div_reg != 0 */ | ||
47 | u32 shift; /* only valid if div_reg != 0 */ | ||
48 | u32 clk_phase[2]; | ||
49 | }; | ||
50 | |||
51 | struct socfpga_periph_clk { | ||
52 | struct clk_gate hw; | ||
53 | char *parent_name; | ||
54 | u32 fixed_div; | ||
55 | }; | ||
56 | |||
57 | #endif /* SOCFPGA_CLK_H */ | ||