Commit | Line | Data |
---|---|---|
e7300d04 MB |
1 | /* |
2 | * This file is subject to the terms and conditions of the GNU General Public | |
3 | * License. See the file "COPYING" in the main directory of this archive | |
4 | * for more details. | |
5 | * | |
6 | * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr> | |
7 | */ | |
8 | ||
9 | #include <linux/module.h> | |
10 | #include <linux/mutex.h> | |
11 | #include <linux/err.h> | |
12 | #include <linux/clk.h> | |
04712f3f | 13 | #include <linux/delay.h> |
e7300d04 MB |
14 | #include <bcm63xx_cpu.h> |
15 | #include <bcm63xx_io.h> | |
16 | #include <bcm63xx_regs.h> | |
17 | #include <bcm63xx_clk.h> | |
18 | ||
19 | static DEFINE_MUTEX(clocks_mutex); | |
20 | ||
21 | ||
22 | static void clk_enable_unlocked(struct clk *clk) | |
23 | { | |
24 | if (clk->set && (clk->usage++) == 0) | |
25 | clk->set(clk, 1); | |
26 | } | |
27 | ||
28 | static void clk_disable_unlocked(struct clk *clk) | |
29 | { | |
30 | if (clk->set && (--clk->usage) == 0) | |
31 | clk->set(clk, 0); | |
32 | } | |
33 | ||
34 | static void bcm_hwclock_set(u32 mask, int enable) | |
35 | { | |
36 | u32 reg; | |
37 | ||
38 | reg = bcm_perf_readl(PERF_CKCTL_REG); | |
39 | if (enable) | |
40 | reg |= mask; | |
41 | else | |
42 | reg &= ~mask; | |
43 | bcm_perf_writel(reg, PERF_CKCTL_REG); | |
44 | } | |
45 | ||
46 | /* | |
47 | * Ethernet MAC "misc" clock: dma clocks and main clock on 6348 | |
48 | */ | |
49 | static void enet_misc_set(struct clk *clk, int enable) | |
50 | { | |
51 | u32 mask; | |
52 | ||
53 | if (BCMCPU_IS_6338()) | |
54 | mask = CKCTL_6338_ENET_EN; | |
55 | else if (BCMCPU_IS_6345()) | |
56 | mask = CKCTL_6345_ENET_EN; | |
57 | else if (BCMCPU_IS_6348()) | |
58 | mask = CKCTL_6348_ENET_EN; | |
59 | else | |
60 | /* BCMCPU_IS_6358 */ | |
61 | mask = CKCTL_6358_EMUSB_EN; | |
62 | bcm_hwclock_set(mask, enable); | |
63 | } | |
64 | ||
65 | static struct clk clk_enet_misc = { | |
66 | .set = enet_misc_set, | |
67 | }; | |
68 | ||
69 | /* | |
70 | * Ethernet MAC clocks: only revelant on 6358, silently enable misc | |
71 | * clocks | |
72 | */ | |
73 | static void enetx_set(struct clk *clk, int enable) | |
74 | { | |
75 | if (enable) | |
76 | clk_enable_unlocked(&clk_enet_misc); | |
77 | else | |
78 | clk_disable_unlocked(&clk_enet_misc); | |
79 | ||
80 | if (BCMCPU_IS_6358()) { | |
81 | u32 mask; | |
82 | ||
83 | if (clk->id == 0) | |
84 | mask = CKCTL_6358_ENET0_EN; | |
85 | else | |
86 | mask = CKCTL_6358_ENET1_EN; | |
87 | bcm_hwclock_set(mask, enable); | |
88 | } | |
89 | } | |
90 | ||
91 | static struct clk clk_enet0 = { | |
92 | .id = 0, | |
93 | .set = enetx_set, | |
94 | }; | |
95 | ||
96 | static struct clk clk_enet1 = { | |
97 | .id = 1, | |
98 | .set = enetx_set, | |
99 | }; | |
100 | ||
101 | /* | |
102 | * Ethernet PHY clock | |
103 | */ | |
104 | static void ephy_set(struct clk *clk, int enable) | |
105 | { | |
106 | if (!BCMCPU_IS_6358()) | |
107 | return; | |
108 | bcm_hwclock_set(CKCTL_6358_EPHY_EN, enable); | |
109 | } | |
110 | ||
111 | ||
112 | static struct clk clk_ephy = { | |
113 | .set = ephy_set, | |
114 | }; | |
115 | ||
04712f3f MB |
116 | /* |
117 | * Ethernet switch clock | |
118 | */ | |
119 | static void enetsw_set(struct clk *clk, int enable) | |
120 | { | |
121 | if (!BCMCPU_IS_6368()) | |
122 | return; | |
123 | bcm_hwclock_set(CKCTL_6368_ROBOSW_CLK_EN | | |
124 | CKCTL_6368_SWPKT_USB_EN | | |
125 | CKCTL_6368_SWPKT_SAR_EN, enable); | |
126 | if (enable) { | |
127 | u32 val; | |
128 | ||
129 | /* reset switch core afer clock change */ | |
130 | val = bcm_perf_readl(PERF_SOFTRESET_6368_REG); | |
131 | val &= ~SOFTRESET_6368_ENETSW_MASK; | |
132 | bcm_perf_writel(val, PERF_SOFTRESET_6368_REG); | |
133 | msleep(10); | |
134 | val |= SOFTRESET_6368_ENETSW_MASK; | |
135 | bcm_perf_writel(val, PERF_SOFTRESET_6368_REG); | |
136 | msleep(10); | |
137 | } | |
138 | } | |
139 | ||
140 | static struct clk clk_enetsw = { | |
141 | .set = enetsw_set, | |
142 | }; | |
143 | ||
e7300d04 MB |
144 | /* |
145 | * PCM clock | |
146 | */ | |
147 | static void pcm_set(struct clk *clk, int enable) | |
148 | { | |
149 | if (!BCMCPU_IS_6358()) | |
150 | return; | |
151 | bcm_hwclock_set(CKCTL_6358_PCM_EN, enable); | |
152 | } | |
153 | ||
154 | static struct clk clk_pcm = { | |
155 | .set = pcm_set, | |
156 | }; | |
157 | ||
158 | /* | |
159 | * USB host clock | |
160 | */ | |
161 | static void usbh_set(struct clk *clk, int enable) | |
162 | { | |
04712f3f MB |
163 | if (BCMCPU_IS_6348()) |
164 | bcm_hwclock_set(CKCTL_6348_USBH_EN, enable); | |
165 | else if (BCMCPU_IS_6368()) | |
166 | bcm_hwclock_set(CKCTL_6368_USBH_CLK_EN, enable); | |
e7300d04 MB |
167 | } |
168 | ||
169 | static struct clk clk_usbh = { | |
170 | .set = usbh_set, | |
171 | }; | |
172 | ||
173 | /* | |
174 | * SPI clock | |
175 | */ | |
176 | static void spi_set(struct clk *clk, int enable) | |
177 | { | |
178 | u32 mask; | |
179 | ||
180 | if (BCMCPU_IS_6338()) | |
181 | mask = CKCTL_6338_SPI_EN; | |
182 | else if (BCMCPU_IS_6348()) | |
183 | mask = CKCTL_6348_SPI_EN; | |
184 | else | |
185 | /* BCMCPU_IS_6358 */ | |
186 | mask = CKCTL_6358_SPI_EN; | |
187 | bcm_hwclock_set(mask, enable); | |
188 | } | |
189 | ||
190 | static struct clk clk_spi = { | |
191 | .set = spi_set, | |
192 | }; | |
193 | ||
04712f3f MB |
194 | /* |
195 | * XTM clock | |
196 | */ | |
197 | static void xtm_set(struct clk *clk, int enable) | |
198 | { | |
199 | if (!BCMCPU_IS_6368()) | |
200 | return; | |
201 | ||
202 | bcm_hwclock_set(CKCTL_6368_SAR_CLK_EN | | |
203 | CKCTL_6368_SWPKT_SAR_EN, enable); | |
204 | ||
205 | if (enable) { | |
206 | u32 val; | |
207 | ||
208 | /* reset sar core afer clock change */ | |
209 | val = bcm_perf_readl(PERF_SOFTRESET_6368_REG); | |
210 | val &= ~SOFTRESET_6368_SAR_MASK; | |
211 | bcm_perf_writel(val, PERF_SOFTRESET_6368_REG); | |
212 | mdelay(1); | |
213 | val |= SOFTRESET_6368_SAR_MASK; | |
214 | bcm_perf_writel(val, PERF_SOFTRESET_6368_REG); | |
215 | mdelay(1); | |
216 | } | |
217 | } | |
218 | ||
219 | ||
220 | static struct clk clk_xtm = { | |
221 | .set = xtm_set, | |
222 | }; | |
223 | ||
e7300d04 MB |
224 | /* |
225 | * Internal peripheral clock | |
226 | */ | |
227 | static struct clk clk_periph = { | |
228 | .rate = (50 * 1000 * 1000), | |
229 | }; | |
230 | ||
231 | ||
232 | /* | |
233 | * Linux clock API implementation | |
234 | */ | |
235 | int clk_enable(struct clk *clk) | |
236 | { | |
237 | mutex_lock(&clocks_mutex); | |
238 | clk_enable_unlocked(clk); | |
239 | mutex_unlock(&clocks_mutex); | |
240 | return 0; | |
241 | } | |
242 | ||
243 | EXPORT_SYMBOL(clk_enable); | |
244 | ||
245 | void clk_disable(struct clk *clk) | |
246 | { | |
247 | mutex_lock(&clocks_mutex); | |
248 | clk_disable_unlocked(clk); | |
249 | mutex_unlock(&clocks_mutex); | |
250 | } | |
251 | ||
252 | EXPORT_SYMBOL(clk_disable); | |
253 | ||
254 | unsigned long clk_get_rate(struct clk *clk) | |
255 | { | |
256 | return clk->rate; | |
257 | } | |
258 | ||
259 | EXPORT_SYMBOL(clk_get_rate); | |
260 | ||
261 | struct clk *clk_get(struct device *dev, const char *id) | |
262 | { | |
263 | if (!strcmp(id, "enet0")) | |
264 | return &clk_enet0; | |
265 | if (!strcmp(id, "enet1")) | |
266 | return &clk_enet1; | |
04712f3f MB |
267 | if (!strcmp(id, "enetsw")) |
268 | return &clk_enetsw; | |
e7300d04 MB |
269 | if (!strcmp(id, "ephy")) |
270 | return &clk_ephy; | |
271 | if (!strcmp(id, "usbh")) | |
272 | return &clk_usbh; | |
273 | if (!strcmp(id, "spi")) | |
274 | return &clk_spi; | |
04712f3f MB |
275 | if (!strcmp(id, "xtm")) |
276 | return &clk_xtm; | |
e7300d04 MB |
277 | if (!strcmp(id, "periph")) |
278 | return &clk_periph; | |
279 | if (BCMCPU_IS_6358() && !strcmp(id, "pcm")) | |
280 | return &clk_pcm; | |
281 | return ERR_PTR(-ENOENT); | |
282 | } | |
283 | ||
284 | EXPORT_SYMBOL(clk_get); | |
285 | ||
286 | void clk_put(struct clk *clk) | |
287 | { | |
288 | } | |
289 | ||
290 | EXPORT_SYMBOL(clk_put); |