Commit | Line | Data |
---|---|---|
aafd900c TK |
1 | /* |
2 | * OMAP3 Clock init | |
3 | * | |
4 | * Copyright (C) 2013 Texas Instruments, Inc | |
5 | * Tero Kristo (t-kristo@ti.com) | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU General Public License as | |
9 | * published by the Free Software Foundation version 2. | |
10 | * | |
11 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | |
12 | * kind, whether express or implied; without even the implied warranty | |
13 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | */ | |
16 | ||
17 | #include <linux/kernel.h> | |
18 | #include <linux/list.h> | |
1b29e601 | 19 | #include <linux/clk.h> |
aafd900c TK |
20 | #include <linux/clk-provider.h> |
21 | #include <linux/clk/ti.h> | |
22 | ||
a5aa8a60 | 23 | #include "clock.h" |
aafd900c | 24 | |
f2671d5c TK |
25 | #define OMAP3430ES2_ST_DSS_IDLE_SHIFT 1 |
26 | #define OMAP3430ES2_ST_HSOTGUSB_IDLE_SHIFT 5 | |
27 | #define OMAP3430ES2_ST_SSI_IDLE_SHIFT 8 | |
28 | ||
29 | #define OMAP34XX_CM_IDLEST_VAL 1 | |
30 | ||
c9a58b0a TK |
31 | /* |
32 | * In AM35xx IPSS, the {ICK,FCK} enable bits for modules are exported | |
33 | * in the same register at a bit offset of 0x8. The EN_ACK for ICK is | |
34 | * at an offset of 4 from ICK enable bit. | |
35 | */ | |
36 | #define AM35XX_IPSS_ICK_MASK 0xF | |
37 | #define AM35XX_IPSS_ICK_EN_ACK_OFFSET 0x4 | |
38 | #define AM35XX_IPSS_ICK_FCK_OFFSET 0x8 | |
39 | #define AM35XX_IPSS_CLK_IDLEST_VAL 0 | |
40 | ||
41 | #define AM35XX_ST_IPSS_SHIFT 5 | |
42 | ||
f2671d5c TK |
43 | /** |
44 | * omap3430es2_clk_ssi_find_idlest - return CM_IDLEST info for SSI | |
45 | * @clk: struct clk * being enabled | |
46 | * @idlest_reg: void __iomem ** to store CM_IDLEST reg address into | |
47 | * @idlest_bit: pointer to a u8 to store the CM_IDLEST bit shift into | |
48 | * @idlest_val: pointer to a u8 to store the CM_IDLEST indicator | |
49 | * | |
50 | * The OMAP3430ES2 SSI target CM_IDLEST bit is at a different shift | |
51 | * from the CM_{I,F}CLKEN bit. Pass back the correct info via | |
52 | * @idlest_reg and @idlest_bit. No return value. | |
53 | */ | |
54 | static void omap3430es2_clk_ssi_find_idlest(struct clk_hw_omap *clk, | |
6c0afb50 | 55 | struct clk_omap_reg *idlest_reg, |
f2671d5c TK |
56 | u8 *idlest_bit, |
57 | u8 *idlest_val) | |
58 | { | |
6c0afb50 TK |
59 | memcpy(idlest_reg, &clk->enable_reg, sizeof(*idlest_reg)); |
60 | idlest_reg->offset &= ~0xf0; | |
61 | idlest_reg->offset |= 0x20; | |
f2671d5c TK |
62 | *idlest_bit = OMAP3430ES2_ST_SSI_IDLE_SHIFT; |
63 | *idlest_val = OMAP34XX_CM_IDLEST_VAL; | |
64 | } | |
65 | ||
f2671d5c TK |
66 | const struct clk_hw_omap_ops clkhwops_omap3430es2_iclk_ssi_wait = { |
67 | .allow_idle = omap2_clkt_iclk_allow_idle, | |
68 | .deny_idle = omap2_clkt_iclk_deny_idle, | |
69 | .find_idlest = omap3430es2_clk_ssi_find_idlest, | |
70 | .find_companion = omap2_clk_dflt_find_companion, | |
71 | }; | |
72 | ||
73 | /** | |
74 | * omap3430es2_clk_dss_usbhost_find_idlest - CM_IDLEST info for DSS, USBHOST | |
75 | * @clk: struct clk * being enabled | |
76 | * @idlest_reg: void __iomem ** to store CM_IDLEST reg address into | |
77 | * @idlest_bit: pointer to a u8 to store the CM_IDLEST bit shift into | |
78 | * @idlest_val: pointer to a u8 to store the CM_IDLEST indicator | |
79 | * | |
80 | * Some OMAP modules on OMAP3 ES2+ chips have both initiator and | |
81 | * target IDLEST bits. For our purposes, we are concerned with the | |
82 | * target IDLEST bits, which exist at a different bit position than | |
83 | * the *CLKEN bit position for these modules (DSS and USBHOST) (The | |
84 | * default find_idlest code assumes that they are at the same | |
85 | * position.) No return value. | |
86 | */ | |
6c0afb50 TK |
87 | static void |
88 | omap3430es2_clk_dss_usbhost_find_idlest(struct clk_hw_omap *clk, | |
89 | struct clk_omap_reg *idlest_reg, | |
90 | u8 *idlest_bit, u8 *idlest_val) | |
f2671d5c | 91 | { |
6c0afb50 | 92 | memcpy(idlest_reg, &clk->enable_reg, sizeof(*idlest_reg)); |
f2671d5c | 93 | |
6c0afb50 TK |
94 | idlest_reg->offset &= ~0xf0; |
95 | idlest_reg->offset |= 0x20; | |
f2671d5c TK |
96 | /* USBHOST_IDLE has same shift */ |
97 | *idlest_bit = OMAP3430ES2_ST_DSS_IDLE_SHIFT; | |
98 | *idlest_val = OMAP34XX_CM_IDLEST_VAL; | |
99 | } | |
100 | ||
101 | const struct clk_hw_omap_ops clkhwops_omap3430es2_dss_usbhost_wait = { | |
102 | .find_idlest = omap3430es2_clk_dss_usbhost_find_idlest, | |
103 | .find_companion = omap2_clk_dflt_find_companion, | |
104 | }; | |
105 | ||
106 | const struct clk_hw_omap_ops clkhwops_omap3430es2_iclk_dss_usbhost_wait = { | |
107 | .allow_idle = omap2_clkt_iclk_allow_idle, | |
108 | .deny_idle = omap2_clkt_iclk_deny_idle, | |
109 | .find_idlest = omap3430es2_clk_dss_usbhost_find_idlest, | |
110 | .find_companion = omap2_clk_dflt_find_companion, | |
111 | }; | |
112 | ||
113 | /** | |
114 | * omap3430es2_clk_hsotgusb_find_idlest - return CM_IDLEST info for HSOTGUSB | |
115 | * @clk: struct clk * being enabled | |
116 | * @idlest_reg: void __iomem ** to store CM_IDLEST reg address into | |
117 | * @idlest_bit: pointer to a u8 to store the CM_IDLEST bit shift into | |
118 | * @idlest_val: pointer to a u8 to store the CM_IDLEST indicator | |
119 | * | |
120 | * The OMAP3430ES2 HSOTGUSB target CM_IDLEST bit is at a different | |
121 | * shift from the CM_{I,F}CLKEN bit. Pass back the correct info via | |
122 | * @idlest_reg and @idlest_bit. No return value. | |
123 | */ | |
6c0afb50 TK |
124 | static void |
125 | omap3430es2_clk_hsotgusb_find_idlest(struct clk_hw_omap *clk, | |
126 | struct clk_omap_reg *idlest_reg, | |
127 | u8 *idlest_bit, | |
128 | u8 *idlest_val) | |
f2671d5c | 129 | { |
6c0afb50 TK |
130 | memcpy(idlest_reg, &clk->enable_reg, sizeof(*idlest_reg)); |
131 | idlest_reg->offset &= ~0xf0; | |
132 | idlest_reg->offset |= 0x20; | |
f2671d5c TK |
133 | *idlest_bit = OMAP3430ES2_ST_HSOTGUSB_IDLE_SHIFT; |
134 | *idlest_val = OMAP34XX_CM_IDLEST_VAL; | |
135 | } | |
136 | ||
137 | const struct clk_hw_omap_ops clkhwops_omap3430es2_iclk_hsotgusb_wait = { | |
138 | .allow_idle = omap2_clkt_iclk_allow_idle, | |
139 | .deny_idle = omap2_clkt_iclk_deny_idle, | |
140 | .find_idlest = omap3430es2_clk_hsotgusb_find_idlest, | |
141 | .find_companion = omap2_clk_dflt_find_companion, | |
142 | }; | |
143 | ||
c9a58b0a TK |
144 | /** |
145 | * am35xx_clk_find_idlest - return clock ACK info for AM35XX IPSS | |
146 | * @clk: struct clk * being enabled | |
147 | * @idlest_reg: void __iomem ** to store CM_IDLEST reg address into | |
148 | * @idlest_bit: pointer to a u8 to store the CM_IDLEST bit shift into | |
149 | * @idlest_val: pointer to a u8 to store the CM_IDLEST indicator | |
150 | * | |
151 | * The interface clocks on AM35xx IPSS reflects the clock idle status | |
152 | * in the enable register itsel at a bit offset of 4 from the enable | |
153 | * bit. A value of 1 indicates that clock is enabled. | |
154 | */ | |
155 | static void am35xx_clk_find_idlest(struct clk_hw_omap *clk, | |
6c0afb50 | 156 | struct clk_omap_reg *idlest_reg, |
c9a58b0a TK |
157 | u8 *idlest_bit, |
158 | u8 *idlest_val) | |
159 | { | |
6c0afb50 | 160 | memcpy(idlest_reg, &clk->enable_reg, sizeof(*idlest_reg)); |
c9a58b0a TK |
161 | *idlest_bit = clk->enable_bit + AM35XX_IPSS_ICK_EN_ACK_OFFSET; |
162 | *idlest_val = AM35XX_IPSS_CLK_IDLEST_VAL; | |
163 | } | |
164 | ||
165 | /** | |
166 | * am35xx_clk_find_companion - find companion clock to @clk | |
167 | * @clk: struct clk * to find the companion clock of | |
168 | * @other_reg: void __iomem ** to return the companion clock CM_*CLKEN va in | |
169 | * @other_bit: u8 ** to return the companion clock bit shift in | |
170 | * | |
171 | * Some clocks don't have companion clocks. For example, modules with | |
172 | * only an interface clock (such as HECC) don't have a companion | |
173 | * clock. Right now, this code relies on the hardware exporting a bit | |
174 | * in the correct companion register that indicates that the | |
175 | * nonexistent 'companion clock' is active. Future patches will | |
176 | * associate this type of code with per-module data structures to | |
177 | * avoid this issue, and remove the casts. No return value. | |
178 | */ | |
179 | static void am35xx_clk_find_companion(struct clk_hw_omap *clk, | |
6c0afb50 | 180 | struct clk_omap_reg *other_reg, |
c9a58b0a TK |
181 | u8 *other_bit) |
182 | { | |
6c0afb50 | 183 | memcpy(other_reg, &clk->enable_reg, sizeof(*other_reg)); |
c9a58b0a TK |
184 | if (clk->enable_bit & AM35XX_IPSS_ICK_MASK) |
185 | *other_bit = clk->enable_bit + AM35XX_IPSS_ICK_FCK_OFFSET; | |
186 | else | |
187 | *other_bit = clk->enable_bit - AM35XX_IPSS_ICK_FCK_OFFSET; | |
188 | } | |
189 | ||
190 | const struct clk_hw_omap_ops clkhwops_am35xx_ipss_module_wait = { | |
191 | .find_idlest = am35xx_clk_find_idlest, | |
192 | .find_companion = am35xx_clk_find_companion, | |
193 | }; | |
194 | ||
195 | /** | |
196 | * am35xx_clk_ipss_find_idlest - return CM_IDLEST info for IPSS | |
197 | * @clk: struct clk * being enabled | |
198 | * @idlest_reg: void __iomem ** to store CM_IDLEST reg address into | |
199 | * @idlest_bit: pointer to a u8 to store the CM_IDLEST bit shift into | |
200 | * @idlest_val: pointer to a u8 to store the CM_IDLEST indicator | |
201 | * | |
202 | * The IPSS target CM_IDLEST bit is at a different shift from the | |
203 | * CM_{I,F}CLKEN bit. Pass back the correct info via @idlest_reg | |
204 | * and @idlest_bit. No return value. | |
205 | */ | |
206 | static void am35xx_clk_ipss_find_idlest(struct clk_hw_omap *clk, | |
6c0afb50 | 207 | struct clk_omap_reg *idlest_reg, |
c9a58b0a TK |
208 | u8 *idlest_bit, |
209 | u8 *idlest_val) | |
210 | { | |
6c0afb50 | 211 | memcpy(idlest_reg, &clk->enable_reg, sizeof(*idlest_reg)); |
c9a58b0a | 212 | |
6c0afb50 TK |
213 | idlest_reg->offset &= ~0xf0; |
214 | idlest_reg->offset |= 0x20; | |
c9a58b0a TK |
215 | *idlest_bit = AM35XX_ST_IPSS_SHIFT; |
216 | *idlest_val = OMAP34XX_CM_IDLEST_VAL; | |
217 | } | |
218 | ||
219 | const struct clk_hw_omap_ops clkhwops_am35xx_ipss_wait = { | |
220 | .allow_idle = omap2_clkt_iclk_allow_idle, | |
221 | .deny_idle = omap2_clkt_iclk_deny_idle, | |
222 | .find_idlest = am35xx_clk_ipss_find_idlest, | |
223 | .find_companion = omap2_clk_dflt_find_companion, | |
224 | }; | |
225 | ||
aafd900c | 226 | static struct ti_dt_clk omap3xxx_clks[] = { |
aafd900c TK |
227 | DT_CLK(NULL, "timer_32k_ck", "omap_32k_fck"), |
228 | DT_CLK(NULL, "timer_sys_ck", "sys_ck"), | |
aafd900c TK |
229 | { .node_name = NULL }, |
230 | }; | |
231 | ||
232 | static struct ti_dt_clk omap36xx_omap3430es2plus_clks[] = { | |
233 | DT_CLK(NULL, "ssi_ssr_fck", "ssi_ssr_fck_3430es2"), | |
234 | DT_CLK(NULL, "ssi_sst_fck", "ssi_sst_fck_3430es2"), | |
aafd900c TK |
235 | DT_CLK(NULL, "hsotgusb_ick", "hsotgusb_ick_3430es2"), |
236 | DT_CLK(NULL, "ssi_ick", "ssi_ick_3430es2"), | |
aafd900c TK |
237 | { .node_name = NULL }, |
238 | }; | |
239 | ||
240 | static struct ti_dt_clk omap3430es1_clks[] = { | |
aafd900c TK |
241 | DT_CLK(NULL, "ssi_ssr_fck", "ssi_ssr_fck_3430es1"), |
242 | DT_CLK(NULL, "ssi_sst_fck", "ssi_sst_fck_3430es1"), | |
aafd900c | 243 | DT_CLK(NULL, "hsotgusb_ick", "hsotgusb_ick_3430es1"), |
aafd900c | 244 | DT_CLK(NULL, "ssi_ick", "ssi_ick_3430es1"), |
aafd900c | 245 | DT_CLK(NULL, "dss1_alwon_fck", "dss1_alwon_fck_3430es1"), |
aafd900c TK |
246 | DT_CLK(NULL, "dss_ick", "dss_ick_3430es1"), |
247 | { .node_name = NULL }, | |
248 | }; | |
249 | ||
250 | static struct ti_dt_clk omap36xx_am35xx_omap3430es2plus_clks[] = { | |
aafd900c | 251 | DT_CLK(NULL, "dss1_alwon_fck", "dss1_alwon_fck_3430es2"), |
aafd900c | 252 | DT_CLK(NULL, "dss_ick", "dss_ick_3430es2"), |
aafd900c TK |
253 | { .node_name = NULL }, |
254 | }; | |
255 | ||
256 | static struct ti_dt_clk am35xx_clks[] = { | |
aafd900c TK |
257 | DT_CLK(NULL, "hsotgusb_ick", "hsotgusb_ick_am35xx"), |
258 | DT_CLK(NULL, "hsotgusb_fck", "hsotgusb_fck_am35xx"), | |
aafd900c TK |
259 | DT_CLK(NULL, "uart4_ick", "uart4_ick_am35xx"), |
260 | DT_CLK(NULL, "uart4_fck", "uart4_fck_am35xx"), | |
261 | { .node_name = NULL }, | |
262 | }; | |
263 | ||
aafd900c TK |
264 | static const char *enable_init_clks[] = { |
265 | "sdrc_ick", | |
266 | "gpmc_fck", | |
267 | "omapctrl_ick", | |
268 | }; | |
269 | ||
270 | enum { | |
271 | OMAP3_SOC_AM35XX, | |
272 | OMAP3_SOC_OMAP3430_ES1, | |
273 | OMAP3_SOC_OMAP3430_ES2_PLUS, | |
274 | OMAP3_SOC_OMAP3630, | |
aafd900c TK |
275 | }; |
276 | ||
0565fb16 TK |
277 | /** |
278 | * omap3_clk_lock_dpll5 - locks DPLL5 | |
279 | * | |
280 | * Locks DPLL5 to a pre-defined frequency. This is required for proper | |
281 | * operation of USB. | |
282 | */ | |
283 | void __init omap3_clk_lock_dpll5(void) | |
284 | { | |
285 | struct clk *dpll5_clk; | |
286 | struct clk *dpll5_m2_clk; | |
287 | ||
035cd485 RW |
288 | /* |
289 | * Errata sprz319f advisory 2.1 documents a USB host clock drift issue | |
290 | * that can be worked around using specially crafted dpll5 settings | |
291 | * with a dpll5_m2 divider set to 8. Set the dpll5 rate to 8x the USB | |
292 | * host clock rate, its .set_rate handler() will detect that frequency | |
293 | * and use the errata settings. | |
294 | */ | |
0565fb16 | 295 | dpll5_clk = clk_get(NULL, "dpll5_ck"); |
035cd485 | 296 | clk_set_rate(dpll5_clk, OMAP3_DPLL5_FREQ_FOR_USBHOST * 8); |
0565fb16 TK |
297 | clk_prepare_enable(dpll5_clk); |
298 | ||
035cd485 | 299 | /* Program dpll5_m2_clk divider */ |
0565fb16 TK |
300 | dpll5_m2_clk = clk_get(NULL, "dpll5_m2_ck"); |
301 | clk_prepare_enable(dpll5_m2_clk); | |
035cd485 | 302 | clk_set_rate(dpll5_m2_clk, OMAP3_DPLL5_FREQ_FOR_USBHOST); |
0565fb16 TK |
303 | |
304 | clk_disable_unprepare(dpll5_m2_clk); | |
305 | clk_disable_unprepare(dpll5_clk); | |
306 | } | |
307 | ||
aafd900c TK |
308 | static int __init omap3xxx_dt_clk_init(int soc_type) |
309 | { | |
310 | if (soc_type == OMAP3_SOC_AM35XX || soc_type == OMAP3_SOC_OMAP3630 || | |
311 | soc_type == OMAP3_SOC_OMAP3430_ES1 || | |
312 | soc_type == OMAP3_SOC_OMAP3430_ES2_PLUS) | |
313 | ti_dt_clocks_register(omap3xxx_clks); | |
314 | ||
315 | if (soc_type == OMAP3_SOC_AM35XX) | |
316 | ti_dt_clocks_register(am35xx_clks); | |
317 | ||
318 | if (soc_type == OMAP3_SOC_OMAP3630 || soc_type == OMAP3_SOC_AM35XX || | |
319 | soc_type == OMAP3_SOC_OMAP3430_ES2_PLUS) | |
320 | ti_dt_clocks_register(omap36xx_am35xx_omap3430es2plus_clks); | |
321 | ||
322 | if (soc_type == OMAP3_SOC_OMAP3430_ES1) | |
323 | ti_dt_clocks_register(omap3430es1_clks); | |
324 | ||
325 | if (soc_type == OMAP3_SOC_OMAP3430_ES2_PLUS || | |
326 | soc_type == OMAP3_SOC_OMAP3630) | |
327 | ti_dt_clocks_register(omap36xx_omap3430es2plus_clks); | |
328 | ||
aafd900c TK |
329 | omap2_clk_disable_autoidle_all(); |
330 | ||
0ed266d7 TK |
331 | ti_clk_add_aliases(); |
332 | ||
aafd900c TK |
333 | omap2_clk_enable_init_clocks(enable_init_clks, |
334 | ARRAY_SIZE(enable_init_clks)); | |
335 | ||
336 | pr_info("Clocking rate (Crystal/Core/MPU): %ld.%01ld/%ld/%ld MHz\n", | |
337 | (clk_get_rate(clk_get_sys(NULL, "osc_sys_ck")) / 1000000), | |
338 | (clk_get_rate(clk_get_sys(NULL, "osc_sys_ck")) / 100000) % 10, | |
339 | (clk_get_rate(clk_get_sys(NULL, "core_ck")) / 1000000), | |
340 | (clk_get_rate(clk_get_sys(NULL, "arm_fck")) / 1000000)); | |
341 | ||
1a34275d | 342 | if (soc_type != OMAP3_SOC_OMAP3430_ES1) |
aafd900c TK |
343 | omap3_clk_lock_dpll5(); |
344 | ||
345 | return 0; | |
346 | } | |
347 | ||
348 | int __init omap3430_dt_clk_init(void) | |
349 | { | |
350 | return omap3xxx_dt_clk_init(OMAP3_SOC_OMAP3430_ES2_PLUS); | |
351 | } | |
352 | ||
353 | int __init omap3630_dt_clk_init(void) | |
354 | { | |
355 | return omap3xxx_dt_clk_init(OMAP3_SOC_OMAP3630); | |
356 | } | |
357 | ||
358 | int __init am35xx_dt_clk_init(void) | |
359 | { | |
360 | return omap3xxx_dt_clk_init(OMAP3_SOC_AM35XX); | |
361 | } |