blob: 4fb52e1fc848a3de85667986a700164b4b7f2952 [file] [log] [blame]
Dinh Nguyen66314222012-07-18 16:07:18 -06001/*
Dinh Nguyen56c5c132013-04-11 10:55:26 -05002 * Copyright 2011-2012 Calxeda, Inc.
3 * Copyright (C) 2012-2013 Altera Corporation <www.altera.com>
Dinh Nguyen66314222012-07-18 16:07:18 -06004 *
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 *
Dinh Nguyen56c5c132013-04-11 10:55:26 -050015 * Based from clk-highbank.c
16 *
Dinh Nguyen66314222012-07-18 16:07:18 -060017 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20#include <linux/clk.h>
21#include <linux/clkdev.h>
22#include <linux/clk-provider.h>
Dinh Nguyen56c5c132013-04-11 10:55:26 -050023#include <linux/io.h>
24#include <linux/of.h>
Dinh Nguyen6a7e7122013-12-09 17:16:38 -060025#include <linux/of_address.h>
Dinh Nguyen66314222012-07-18 16:07:18 -060026
Dinh Nguyen56c5c132013-04-11 10:55:26 -050027/* Clock Manager offsets */
Dinh Nguyen825f0c22013-06-05 10:02:55 -050028#define CLKMGR_CTRL 0x0
29#define CLKMGR_BYPASS 0x4
30#define CLKMGR_L4SRC 0x70
31#define CLKMGR_PERPLL_SRC 0xAC
Dinh Nguyen56c5c132013-04-11 10:55:26 -050032
33/* Clock bypass bits */
Dinh Nguyen825f0c22013-06-05 10:02:55 -050034#define MAINPLL_BYPASS (1<<0)
35#define SDRAMPLL_BYPASS (1<<1)
36#define SDRAMPLL_SRC_BYPASS (1<<2)
37#define PERPLL_BYPASS (1<<3)
38#define PERPLL_SRC_BYPASS (1<<4)
Dinh Nguyen56c5c132013-04-11 10:55:26 -050039
40#define SOCFPGA_PLL_BG_PWRDWN 0
41#define SOCFPGA_PLL_EXT_ENA 1
42#define SOCFPGA_PLL_PWR_DOWN 2
43#define SOCFPGA_PLL_DIVF_MASK 0x0000FFF8
44#define SOCFPGA_PLL_DIVF_SHIFT 3
45#define SOCFPGA_PLL_DIVQ_MASK 0x003F0000
46#define SOCFPGA_PLL_DIVQ_SHIFT 16
Dinh Nguyen825f0c22013-06-05 10:02:55 -050047#define SOCFGPA_MAX_PARENTS 3
48
49#define SOCFPGA_L4_MP_CLK "l4_mp_clk"
50#define SOCFPGA_L4_SP_CLK "l4_sp_clk"
51#define SOCFPGA_NAND_CLK "nand_clk"
52#define SOCFPGA_NAND_X_CLK "nand_x_clk"
Dinh Nguyen79a2e992013-09-17 11:23:05 -050053#define SOCFPGA_MMC_CLK "sdmmc_clk"
Dinh Nguyen825f0c22013-06-05 10:02:55 -050054#define SOCFPGA_DB_CLK "gpio_db_clk"
55
56#define div_mask(width) ((1 << (width)) - 1)
57#define streq(a, b) (strcmp((a), (b)) == 0)
Dinh Nguyen56c5c132013-04-11 10:55:26 -050058
Dinh Nguyen6a7e7122013-12-09 17:16:38 -060059static void __iomem *clk_mgr_base_addr;
Dinh Nguyen56c5c132013-04-11 10:55:26 -050060
61struct socfpga_clk {
62 struct clk_gate hw;
63 char *parent_name;
64 char *clk_name;
65 u32 fixed_div;
Dinh Nguyen825f0c22013-06-05 10:02:55 -050066 void __iomem *div_reg;
67 u32 width; /* only valid if div_reg != 0 */
68 u32 shift; /* only valid if div_reg != 0 */
Dinh Nguyen56c5c132013-04-11 10:55:26 -050069};
70#define to_socfpga_clk(p) container_of(p, struct socfpga_clk, hw.hw)
71
72static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk,
73 unsigned long parent_rate)
74{
75 struct socfpga_clk *socfpgaclk = to_socfpga_clk(hwclk);
76 unsigned long divf, divq, vco_freq, reg;
77 unsigned long bypass;
78
79 reg = readl(socfpgaclk->hw.reg);
80 bypass = readl(clk_mgr_base_addr + CLKMGR_BYPASS);
81 if (bypass & MAINPLL_BYPASS)
82 return parent_rate;
83
84 divf = (reg & SOCFPGA_PLL_DIVF_MASK) >> SOCFPGA_PLL_DIVF_SHIFT;
85 divq = (reg & SOCFPGA_PLL_DIVQ_MASK) >> SOCFPGA_PLL_DIVQ_SHIFT;
86 vco_freq = parent_rate * (divf + 1);
87 return vco_freq / (1 + divq);
88}
89
90
91static struct clk_ops clk_pll_ops = {
92 .recalc_rate = clk_pll_recalc_rate,
93};
94
95static unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk,
96 unsigned long parent_rate)
97{
98 struct socfpga_clk *socfpgaclk = to_socfpga_clk(hwclk);
99 u32 div;
100
101 if (socfpgaclk->fixed_div)
102 div = socfpgaclk->fixed_div;
103 else
104 div = ((readl(socfpgaclk->hw.reg) & 0x1ff) + 1);
105
106 return parent_rate / div;
107}
108
109static const struct clk_ops periclk_ops = {
110 .recalc_rate = clk_periclk_recalc_rate,
111};
112
113static __init struct clk *socfpga_clk_init(struct device_node *node,
114 const struct clk_ops *ops)
115{
116 u32 reg;
117 struct clk *clk;
118 struct socfpga_clk *socfpga_clk;
119 const char *clk_name = node->name;
120 const char *parent_name;
121 struct clk_init_data init;
122 int rc;
123 u32 fixed_div;
124
Dinh Nguyen4d043912013-10-24 03:18:30 -0500125 of_property_read_u32(node, "reg", &reg);
Dinh Nguyen56c5c132013-04-11 10:55:26 -0500126
127 socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL);
128 if (WARN_ON(!socfpga_clk))
129 return NULL;
130
131 socfpga_clk->hw.reg = clk_mgr_base_addr + reg;
132
133 rc = of_property_read_u32(node, "fixed-divider", &fixed_div);
134 if (rc)
135 socfpga_clk->fixed_div = 0;
136 else
137 socfpga_clk->fixed_div = fixed_div;
138
139 of_property_read_string(node, "clock-output-names", &clk_name);
140
141 init.name = clk_name;
142 init.ops = ops;
143 init.flags = 0;
144 parent_name = of_clk_get_parent_name(node, 0);
145 init.parent_names = &parent_name;
146 init.num_parents = 1;
147
148 socfpga_clk->hw.hw.init = &init;
149
Dinh Nguyen825f0c22013-06-05 10:02:55 -0500150 if (streq(clk_name, "main_pll") ||
151 streq(clk_name, "periph_pll") ||
152 streq(clk_name, "sdram_pll")) {
Dinh Nguyen56c5c132013-04-11 10:55:26 -0500153 socfpga_clk->hw.bit_idx = SOCFPGA_PLL_EXT_ENA;
154 clk_pll_ops.enable = clk_gate_ops.enable;
155 clk_pll_ops.disable = clk_gate_ops.disable;
156 }
157
158 clk = clk_register(NULL, &socfpga_clk->hw.hw);
159 if (WARN_ON(IS_ERR(clk))) {
160 kfree(socfpga_clk);
161 return NULL;
162 }
163 rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
164 return clk;
165}
166
Dinh Nguyen825f0c22013-06-05 10:02:55 -0500167static u8 socfpga_clk_get_parent(struct clk_hw *hwclk)
168{
169 u32 l4_src;
170 u32 perpll_src;
171
172 if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) {
173 l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
174 return l4_src &= 0x1;
175 }
176 if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) {
177 l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
178 return !!(l4_src & 2);
179 }
180
181 perpll_src = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
182 if (streq(hwclk->init->name, SOCFPGA_MMC_CLK))
183 return perpll_src &= 0x3;
184 if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) ||
185 streq(hwclk->init->name, SOCFPGA_NAND_X_CLK))
186 return (perpll_src >> 2) & 3;
187
188 /* QSPI clock */
189 return (perpll_src >> 4) & 3;
190
191}
192
193static int socfpga_clk_set_parent(struct clk_hw *hwclk, u8 parent)
194{
195 u32 src_reg;
196
197 if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) {
198 src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
199 src_reg &= ~0x1;
200 src_reg |= parent;
201 writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC);
202 } else if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) {
203 src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC);
204 src_reg &= ~0x2;
205 src_reg |= (parent << 1);
206 writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC);
207 } else {
208 src_reg = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
209 if (streq(hwclk->init->name, SOCFPGA_MMC_CLK)) {
210 src_reg &= ~0x3;
211 src_reg |= parent;
212 } else if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) ||
213 streq(hwclk->init->name, SOCFPGA_NAND_X_CLK)) {
214 src_reg &= ~0xC;
215 src_reg |= (parent << 2);
216 } else {/* QSPI clock */
217 src_reg &= ~0x30;
218 src_reg |= (parent << 4);
219 }
220 writel(src_reg, clk_mgr_base_addr + CLKMGR_PERPLL_SRC);
221 }
222
223 return 0;
224}
225
226static unsigned long socfpga_clk_recalc_rate(struct clk_hw *hwclk,
227 unsigned long parent_rate)
228{
229 struct socfpga_clk *socfpgaclk = to_socfpga_clk(hwclk);
230 u32 div = 1, val;
231
232 if (socfpgaclk->fixed_div)
233 div = socfpgaclk->fixed_div;
234 else if (socfpgaclk->div_reg) {
235 val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift;
236 val &= div_mask(socfpgaclk->width);
237 if (streq(hwclk->init->name, SOCFPGA_DB_CLK))
238 div = val + 1;
239 else
240 div = (1 << val);
241 }
242
243 return parent_rate / div;
244}
245
246static struct clk_ops gateclk_ops = {
247 .recalc_rate = socfpga_clk_recalc_rate,
248 .get_parent = socfpga_clk_get_parent,
249 .set_parent = socfpga_clk_set_parent,
250};
251
252static void __init socfpga_gate_clk_init(struct device_node *node,
253 const struct clk_ops *ops)
254{
255 u32 clk_gate[2];
256 u32 div_reg[3];
257 u32 fixed_div;
258 struct clk *clk;
259 struct socfpga_clk *socfpga_clk;
260 const char *clk_name = node->name;
261 const char *parent_name[SOCFGPA_MAX_PARENTS];
262 struct clk_init_data init;
263 int rc;
264 int i = 0;
265
266 socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL);
267 if (WARN_ON(!socfpga_clk))
268 return;
269
270 rc = of_property_read_u32_array(node, "clk-gate", clk_gate, 2);
271 if (rc)
272 clk_gate[0] = 0;
273
274 if (clk_gate[0]) {
275 socfpga_clk->hw.reg = clk_mgr_base_addr + clk_gate[0];
276 socfpga_clk->hw.bit_idx = clk_gate[1];
277
278 gateclk_ops.enable = clk_gate_ops.enable;
279 gateclk_ops.disable = clk_gate_ops.disable;
280 }
281
282 rc = of_property_read_u32(node, "fixed-divider", &fixed_div);
283 if (rc)
284 socfpga_clk->fixed_div = 0;
285 else
286 socfpga_clk->fixed_div = fixed_div;
287
288 rc = of_property_read_u32_array(node, "div-reg", div_reg, 3);
289 if (!rc) {
290 socfpga_clk->div_reg = clk_mgr_base_addr + div_reg[0];
291 socfpga_clk->shift = div_reg[1];
292 socfpga_clk->width = div_reg[2];
293 } else {
Sachin Kamat6d7ff6c2013-10-08 16:47:43 +0530294 socfpga_clk->div_reg = NULL;
Dinh Nguyen825f0c22013-06-05 10:02:55 -0500295 }
296
297 of_property_read_string(node, "clock-output-names", &clk_name);
298
299 init.name = clk_name;
300 init.ops = ops;
301 init.flags = 0;
302 while (i < SOCFGPA_MAX_PARENTS && (parent_name[i] =
303 of_clk_get_parent_name(node, i)) != NULL)
304 i++;
305
306 init.parent_names = parent_name;
307 init.num_parents = i;
308 socfpga_clk->hw.hw.init = &init;
309
310 clk = clk_register(NULL, &socfpga_clk->hw.hw);
311 if (WARN_ON(IS_ERR(clk))) {
312 kfree(socfpga_clk);
313 return;
314 }
315 rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
316 if (WARN_ON(rc))
317 return;
318}
319
Dinh Nguyen56c5c132013-04-11 10:55:26 -0500320static void __init socfpga_pll_init(struct device_node *node)
321{
322 socfpga_clk_init(node, &clk_pll_ops);
323}
Dinh Nguyen56c5c132013-04-11 10:55:26 -0500324
325static void __init socfpga_periph_init(struct device_node *node)
326{
327 socfpga_clk_init(node, &periclk_ops);
328}
Dinh Nguyen66314222012-07-18 16:07:18 -0600329
Dinh Nguyen825f0c22013-06-05 10:02:55 -0500330static void __init socfpga_gate_init(struct device_node *node)
331{
332 socfpga_gate_clk_init(node, &gateclk_ops);
333}
Dinh Nguyen6a7e7122013-12-09 17:16:38 -0600334
335static struct of_device_id socfpga_child_clocks[] = {
336 { .compatible = "altr,socfpga-pll-clock", socfpga_pll_init, },
337 { .compatible = "altr,socfpga-perip-clk", socfpga_periph_init, },
338 { .compatible = "altr,socfpga-gate-clk", socfpga_gate_init, },
339 {},
340};
341
342static void __init socfpga_clkmgr_init(struct device_node *node)
343{
344 clk_mgr_base_addr = of_iomap(node, 0);
345 of_clk_init(socfpga_child_clocks);
346}
347CLK_OF_DECLARE(socfpga_mgr, "altr,clk-mgr", socfpga_clkmgr_init);
Dinh Nguyen825f0c22013-06-05 10:02:55 -0500348
Dinh Nguyen66314222012-07-18 16:07:18 -0600349void __init socfpga_init_clocks(void)
350{
351 struct clk *clk;
Dinh Nguyen56c5c132013-04-11 10:55:26 -0500352 int ret;
Dinh Nguyen66314222012-07-18 16:07:18 -0600353
Dinh Nguyen56c5c132013-04-11 10:55:26 -0500354 clk = clk_register_fixed_factor(NULL, "smp_twd", "mpuclk", 0, 1, 4);
355 ret = clk_register_clkdev(clk, NULL, "smp_twd");
356 if (ret)
357 pr_err("smp_twd alias not registered\n");
Dinh Nguyen66314222012-07-18 16:07:18 -0600358}