diff options
author | Steffen Trumtrar <s.trumtrar@pengutronix.de> | 2014-01-06 11:27:37 -0500 |
---|---|---|
committer | Mike Turquette <mturquette@linaro.org> | 2014-02-18 17:08:13 -0500 |
commit | 97259e99bdc9144d071815536f1dbc2e41c6b5a8 (patch) | |
tree | 951034053e4874e158c8cb442e1025a2e66cd128 /drivers/clk/socfpga | |
parent | 0c5a1872ba04dbcf8430d805a8c34e0ee22f1f75 (diff) |
clk: socfpga: split clk code
Move the different kinds of clocks into their own files. The reason is to aid
readability of the code. This also goes along with the other SoC-specific
clock drivers.
The split introduces new structs for the three types of clocks and uses them.
Other changes are not done to the code.
Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
Signed-off-by: Dinh Nguyen <dinguyen@altera.com>
Diffstat (limited to 'drivers/clk/socfpga')
-rw-r--r-- | drivers/clk/socfpga/Makefile | 3 | ||||
-rw-r--r-- | drivers/clk/socfpga/clk-gate.c | 195 | ||||
-rw-r--r-- | drivers/clk/socfpga/clk-periph.c | 94 | ||||
-rw-r--r-- | drivers/clk/socfpga/clk-pll.c | 111 | ||||
-rw-r--r-- | drivers/clk/socfpga/clk.c | 308 | ||||
-rw-r--r-- | drivers/clk/socfpga/clk.h | 57 |
6 files changed, 462 insertions, 306 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..4efcf4e33a82 --- /dev/null +++ b/drivers/clk/socfpga/clk-gate.c | |||
@@ -0,0 +1,195 @@ | |||
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 SOCFPGA_L4_MP_CLK "l4_mp_clk" | ||
27 | #define SOCFPGA_L4_SP_CLK "l4_sp_clk" | ||
28 | #define SOCFPGA_NAND_CLK "nand_clk" | ||
29 | #define SOCFPGA_NAND_X_CLK "nand_x_clk" | ||
30 | #define SOCFPGA_MMC_CLK "sdmmc_clk" | ||
31 | #define SOCFPGA_GPIO_DB_CLK_OFFSET 0xA8 | ||
32 | |||
33 | #define div_mask(width) ((1 << (width)) - 1) | ||
34 | #define streq(a, b) (strcmp((a), (b)) == 0) | ||
35 | |||
36 | #define to_socfpga_gate_clk(p) container_of(p, struct socfpga_gate_clk, hw.hw) | ||
37 | |||
38 | static u8 socfpga_clk_get_parent(struct clk_hw *hwclk) | ||
39 | { | ||
40 | u32 l4_src; | ||
41 | u32 perpll_src; | ||
42 | |||
43 | if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) { | ||
44 | l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC); | ||
45 | return l4_src &= 0x1; | ||
46 | } | ||
47 | if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) { | ||
48 | l4_src = readl(clk_mgr_base_addr + CLKMGR_L4SRC); | ||
49 | return !!(l4_src & 2); | ||
50 | } | ||
51 | |||
52 | perpll_src = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC); | ||
53 | if (streq(hwclk->init->name, SOCFPGA_MMC_CLK)) | ||
54 | return perpll_src &= 0x3; | ||
55 | if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) || | ||
56 | streq(hwclk->init->name, SOCFPGA_NAND_X_CLK)) | ||
57 | return (perpll_src >> 2) & 3; | ||
58 | |||
59 | /* QSPI clock */ | ||
60 | return (perpll_src >> 4) & 3; | ||
61 | |||
62 | } | ||
63 | |||
64 | static int socfpga_clk_set_parent(struct clk_hw *hwclk, u8 parent) | ||
65 | { | ||
66 | u32 src_reg; | ||
67 | |||
68 | if (streq(hwclk->init->name, SOCFPGA_L4_MP_CLK)) { | ||
69 | src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC); | ||
70 | src_reg &= ~0x1; | ||
71 | src_reg |= parent; | ||
72 | writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC); | ||
73 | } else if (streq(hwclk->init->name, SOCFPGA_L4_SP_CLK)) { | ||
74 | src_reg = readl(clk_mgr_base_addr + CLKMGR_L4SRC); | ||
75 | src_reg &= ~0x2; | ||
76 | src_reg |= (parent << 1); | ||
77 | writel(src_reg, clk_mgr_base_addr + CLKMGR_L4SRC); | ||
78 | } else { | ||
79 | src_reg = readl(clk_mgr_base_addr + CLKMGR_PERPLL_SRC); | ||
80 | if (streq(hwclk->init->name, SOCFPGA_MMC_CLK)) { | ||
81 | src_reg &= ~0x3; | ||
82 | src_reg |= parent; | ||
83 | } else if (streq(hwclk->init->name, SOCFPGA_NAND_CLK) || | ||
84 | streq(hwclk->init->name, SOCFPGA_NAND_X_CLK)) { | ||
85 | src_reg &= ~0xC; | ||
86 | src_reg |= (parent << 2); | ||
87 | } else {/* QSPI clock */ | ||
88 | src_reg &= ~0x30; | ||
89 | src_reg |= (parent << 4); | ||
90 | } | ||
91 | writel(src_reg, clk_mgr_base_addr + CLKMGR_PERPLL_SRC); | ||
92 | } | ||
93 | |||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | static unsigned long socfpga_clk_recalc_rate(struct clk_hw *hwclk, | ||
98 | unsigned long parent_rate) | ||
99 | { | ||
100 | struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk); | ||
101 | u32 div = 1, val; | ||
102 | |||
103 | if (socfpgaclk->fixed_div) | ||
104 | div = socfpgaclk->fixed_div; | ||
105 | else if (socfpgaclk->div_reg) { | ||
106 | val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift; | ||
107 | val &= div_mask(socfpgaclk->width); | ||
108 | /* Check for GPIO_DB_CLK by its offset */ | ||
109 | if ((int) socfpgaclk->div_reg & SOCFPGA_GPIO_DB_CLK_OFFSET) | ||
110 | div = val + 1; | ||
111 | else | ||
112 | div = (1 << val); | ||
113 | } | ||
114 | |||
115 | return parent_rate / div; | ||
116 | } | ||
117 | |||
118 | static struct clk_ops gateclk_ops = { | ||
119 | .recalc_rate = socfpga_clk_recalc_rate, | ||
120 | .get_parent = socfpga_clk_get_parent, | ||
121 | .set_parent = socfpga_clk_set_parent, | ||
122 | }; | ||
123 | |||
124 | static void __init __socfpga_gate_init(struct device_node *node, | ||
125 | const struct clk_ops *ops) | ||
126 | { | ||
127 | u32 clk_gate[2]; | ||
128 | u32 div_reg[3]; | ||
129 | u32 fixed_div; | ||
130 | struct clk *clk; | ||
131 | struct socfpga_gate_clk *socfpga_clk; | ||
132 | const char *clk_name = node->name; | ||
133 | const char *parent_name[SOCFPGA_MAX_PARENTS]; | ||
134 | struct clk_init_data init; | ||
135 | int rc; | ||
136 | int i = 0; | ||
137 | |||
138 | socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL); | ||
139 | if (WARN_ON(!socfpga_clk)) | ||
140 | return; | ||
141 | |||
142 | rc = of_property_read_u32_array(node, "clk-gate", clk_gate, 2); | ||
143 | if (rc) | ||
144 | clk_gate[0] = 0; | ||
145 | |||
146 | if (clk_gate[0]) { | ||
147 | socfpga_clk->hw.reg = clk_mgr_base_addr + clk_gate[0]; | ||
148 | socfpga_clk->hw.bit_idx = clk_gate[1]; | ||
149 | |||
150 | gateclk_ops.enable = clk_gate_ops.enable; | ||
151 | gateclk_ops.disable = clk_gate_ops.disable; | ||
152 | } | ||
153 | |||
154 | rc = of_property_read_u32(node, "fixed-divider", &fixed_div); | ||
155 | if (rc) | ||
156 | socfpga_clk->fixed_div = 0; | ||
157 | else | ||
158 | socfpga_clk->fixed_div = fixed_div; | ||
159 | |||
160 | rc = of_property_read_u32_array(node, "div-reg", div_reg, 3); | ||
161 | if (!rc) { | ||
162 | socfpga_clk->div_reg = clk_mgr_base_addr + div_reg[0]; | ||
163 | socfpga_clk->shift = div_reg[1]; | ||
164 | socfpga_clk->width = div_reg[2]; | ||
165 | } else { | ||
166 | socfpga_clk->div_reg = 0; | ||
167 | } | ||
168 | |||
169 | of_property_read_string(node, "clock-output-names", &clk_name); | ||
170 | |||
171 | init.name = clk_name; | ||
172 | init.ops = ops; | ||
173 | init.flags = 0; | ||
174 | while (i < SOCFPGA_MAX_PARENTS && (parent_name[i] = | ||
175 | of_clk_get_parent_name(node, i)) != NULL) | ||
176 | i++; | ||
177 | |||
178 | init.parent_names = parent_name; | ||
179 | init.num_parents = i; | ||
180 | socfpga_clk->hw.hw.init = &init; | ||
181 | |||
182 | clk = clk_register(NULL, &socfpga_clk->hw.hw); | ||
183 | if (WARN_ON(IS_ERR(clk))) { | ||
184 | kfree(socfpga_clk); | ||
185 | return; | ||
186 | } | ||
187 | rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); | ||
188 | if (WARN_ON(rc)) | ||
189 | return; | ||
190 | } | ||
191 | |||
192 | void __init socfpga_gate_init(struct device_node *node) | ||
193 | { | ||
194 | __socfpga_gate_init(node, &gateclk_ops); | ||
195 | } | ||
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..362004e1e6fe --- /dev/null +++ b/drivers/clk/socfpga/clk-pll.c | |||
@@ -0,0 +1,111 @@ | |||
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 to_socfpga_clk(p) container_of(p, struct socfpga_pll, hw.hw) | ||
42 | |||
43 | static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk, | ||
44 | unsigned long parent_rate) | ||
45 | { | ||
46 | struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk); | ||
47 | unsigned long divf, divq, vco_freq, reg; | ||
48 | unsigned long bypass; | ||
49 | |||
50 | reg = readl(socfpgaclk->hw.reg); | ||
51 | bypass = readl(clk_mgr_base_addr + CLKMGR_BYPASS); | ||
52 | if (bypass & MAINPLL_BYPASS) | ||
53 | return parent_rate; | ||
54 | |||
55 | divf = (reg & SOCFPGA_PLL_DIVF_MASK) >> SOCFPGA_PLL_DIVF_SHIFT; | ||
56 | divq = (reg & SOCFPGA_PLL_DIVQ_MASK) >> SOCFPGA_PLL_DIVQ_SHIFT; | ||
57 | vco_freq = parent_rate * (divf + 1); | ||
58 | return vco_freq / (1 + divq); | ||
59 | } | ||
60 | |||
61 | static struct clk_ops clk_pll_ops = { | ||
62 | .recalc_rate = clk_pll_recalc_rate, | ||
63 | }; | ||
64 | |||
65 | static __init struct clk *__socfpga_pll_init(struct device_node *node, | ||
66 | const struct clk_ops *ops) | ||
67 | { | ||
68 | u32 reg; | ||
69 | struct clk *clk; | ||
70 | struct socfpga_pll *pll_clk; | ||
71 | const char *clk_name = node->name; | ||
72 | const char *parent_name; | ||
73 | struct clk_init_data init; | ||
74 | int rc; | ||
75 | |||
76 | of_property_read_u32(node, "reg", ®); | ||
77 | |||
78 | pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL); | ||
79 | if (WARN_ON(!pll_clk)) | ||
80 | return NULL; | ||
81 | |||
82 | pll_clk->hw.reg = clk_mgr_base_addr + reg; | ||
83 | |||
84 | of_property_read_string(node, "clock-output-names", &clk_name); | ||
85 | |||
86 | init.name = clk_name; | ||
87 | init.ops = ops; | ||
88 | init.flags = 0; | ||
89 | parent_name = of_clk_get_parent_name(node, 0); | ||
90 | init.parent_names = parent_name ? &parent_name : NULL; | ||
91 | init.num_parents = parent_name ? 1 : 0; | ||
92 | |||
93 | pll_clk->hw.hw.init = &init; | ||
94 | |||
95 | pll_clk->hw.bit_idx = SOCFPGA_PLL_EXT_ENA; | ||
96 | clk_pll_ops.enable = clk_gate_ops.enable; | ||
97 | clk_pll_ops.disable = clk_gate_ops.disable; | ||
98 | |||
99 | clk = clk_register(NULL, &pll_clk->hw.hw); | ||
100 | if (WARN_ON(IS_ERR(clk))) { | ||
101 | kfree(pll_clk); | ||
102 | return NULL; | ||
103 | } | ||
104 | rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); | ||
105 | return clk; | ||
106 | } | ||
107 | |||
108 | void __init socfpga_pll_init(struct device_node *node) | ||
109 | { | ||
110 | __socfpga_pll_init(node, &clk_pll_ops); | ||
111 | } | ||
diff --git a/drivers/clk/socfpga/clk.c b/drivers/clk/socfpga/clk.c index 61aa29debdc8..6217d5dc6644 100644 --- a/drivers/clk/socfpga/clk.c +++ b/drivers/clk/socfpga/clk.c | |||
@@ -24,313 +24,9 @@ | |||
24 | #include <linux/of.h> | 24 | #include <linux/of.h> |
25 | #include <linux/of_address.h> | 25 | #include <linux/of_address.h> |
26 | 26 | ||
27 | /* Clock Manager offsets */ | 27 | #include "clk.h" |
28 | #define CLKMGR_CTRL 0x0 | ||
29 | #define CLKMGR_BYPASS 0x4 | ||
30 | #define CLKMGR_L4SRC 0x70 | ||
31 | #define CLKMGR_PERPLL_SRC 0xAC | ||
32 | 28 | ||
33 | /* Clock bypass bits */ | 29 | void __iomem *clk_mgr_base_addr; |
34 | #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) | ||
39 | |||
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 | ||
47 | #define SOCFPGA_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" | ||
53 | #define SOCFPGA_MMC_CLK "sdmmc_clk" | ||
54 | #define SOCFPGA_GPIO_DB_CLK_OFFSET 0xA8 | ||
55 | |||
56 | #define div_mask(width) ((1 << (width)) - 1) | ||
57 | #define streq(a, b) (strcmp((a), (b)) == 0) | ||
58 | |||
59 | static void __iomem *clk_mgr_base_addr; | ||
60 | |||
61 | struct socfpga_clk { | ||
62 | struct clk_gate hw; | ||
63 | char *parent_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 | /* Check for GPIO_DB_CLK by its offset */ | ||
237 | if ((int)socfpgaclk->div_reg & SOCFPGA_GPIO_DB_CLK_OFFSET) | ||
238 | div = val + 1; | ||
239 | else | ||
240 | div = (1 << val); | ||
241 | } | ||
242 | |||
243 | return parent_rate / div; | ||
244 | } | ||
245 | |||
246 | static 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 | |||
252 | static 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[SOCFPGA_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 { | ||
294 | socfpga_clk->div_reg = NULL; | ||
295 | } | ||
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 < SOCFPGA_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 | |||
320 | static void __init socfpga_pll_init(struct device_node *node) | ||
321 | { | ||
322 | socfpga_clk_init(node, &clk_pll_ops); | ||
323 | } | ||
324 | |||
325 | static void __init socfpga_periph_init(struct device_node *node) | ||
326 | { | ||
327 | socfpga_clk_init(node, &periclk_ops); | ||
328 | } | ||
329 | |||
330 | static void __init socfpga_gate_init(struct device_node *node) | ||
331 | { | ||
332 | socfpga_gate_clk_init(node, &gateclk_ops); | ||
333 | } | ||
334 | 30 | ||
335 | static struct of_device_id socfpga_child_clocks[] = { | 31 | static struct of_device_id socfpga_child_clocks[] = { |
336 | { .compatible = "altr,socfpga-pll-clock", socfpga_pll_init, }, | 32 | { .compatible = "altr,socfpga-pll-clock", socfpga_pll_init, }, |
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 */ | ||