Commit | Line | Data |
---|---|---|
4029813c EM |
1 | /* |
2 | * linux/arch/arm/mach-pxa/clock-pxa3xx.c | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License version 2 as | |
6 | * published by the Free Software Foundation. | |
7 | */ | |
8 | ||
9 | #include <linux/module.h> | |
10 | #include <linux/kernel.h> | |
11 | #include <linux/init.h> | |
c085052b | 12 | #include <linux/io.h> |
2eaa03b5 | 13 | #include <linux/syscore_ops.h> |
4029813c | 14 | |
c085052b | 15 | #include <mach/smemc.h> |
4029813c EM |
16 | #include <mach/pxa3xx-regs.h> |
17 | ||
18 | #include "clock.h" | |
19 | ||
20 | /* Crystal clock: 13MHz */ | |
21 | #define BASE_CLK 13000000 | |
22 | ||
23 | /* Ring Oscillator Clock: 60MHz */ | |
24 | #define RO_CLK 60000000 | |
25 | ||
26 | #define ACCR_D0CS (1 << 26) | |
27 | #define ACCR_PCCE (1 << 11) | |
28 | ||
4029813c EM |
29 | /* crystal frequency to HSIO bus frequency multiplier (HSS) */ |
30 | static unsigned char hss_mult[4] = { 8, 12, 16, 24 }; | |
31 | ||
32 | /* | |
33 | * Get the clock frequency as reflected by CCSR and the turbo flag. | |
34 | * We assume these values have been applied via a fcs. | |
35 | * If info is not 0 we also display the current settings. | |
36 | */ | |
37 | unsigned int pxa3xx_get_clk_frequency_khz(int info) | |
38 | { | |
39 | unsigned long acsr, xclkcfg; | |
40 | unsigned int t, xl, xn, hss, ro, XL, XN, CLK, HSS; | |
41 | ||
42 | /* Read XCLKCFG register turbo bit */ | |
43 | __asm__ __volatile__("mrc\tp14, 0, %0, c6, c0, 0" : "=r"(xclkcfg)); | |
44 | t = xclkcfg & 0x1; | |
45 | ||
46 | acsr = ACSR; | |
47 | ||
48 | xl = acsr & 0x1f; | |
49 | xn = (acsr >> 8) & 0x7; | |
50 | hss = (acsr >> 14) & 0x3; | |
51 | ||
52 | XL = xl * BASE_CLK; | |
53 | XN = xn * XL; | |
54 | ||
55 | ro = acsr & ACCR_D0CS; | |
56 | ||
57 | CLK = (ro) ? RO_CLK : ((t) ? XN : XL); | |
58 | HSS = (ro) ? RO_CLK : hss_mult[hss] * BASE_CLK; | |
59 | ||
60 | if (info) { | |
61 | pr_info("RO Mode clock: %d.%02dMHz (%sactive)\n", | |
62 | RO_CLK / 1000000, (RO_CLK % 1000000) / 10000, | |
63 | (ro) ? "" : "in"); | |
64 | pr_info("Run Mode clock: %d.%02dMHz (*%d)\n", | |
65 | XL / 1000000, (XL % 1000000) / 10000, xl); | |
66 | pr_info("Turbo Mode clock: %d.%02dMHz (*%d, %sactive)\n", | |
67 | XN / 1000000, (XN % 1000000) / 10000, xn, | |
68 | (t) ? "" : "in"); | |
69 | pr_info("HSIO bus clock: %d.%02dMHz\n", | |
70 | HSS / 1000000, (HSS % 1000000) / 10000); | |
71 | } | |
72 | ||
73 | return CLK / 1000; | |
74 | } | |
75 | ||
76 | /* | |
77 | * Return the current AC97 clock frequency. | |
78 | */ | |
79 | static unsigned long clk_pxa3xx_ac97_getrate(struct clk *clk) | |
80 | { | |
81 | unsigned long rate = 312000000; | |
82 | unsigned long ac97_div; | |
83 | ||
84 | ac97_div = AC97_DIV; | |
85 | ||
86 | /* This may loose precision for some rates but won't for the | |
87 | * standard 24.576MHz. | |
88 | */ | |
89 | rate /= (ac97_div >> 12) & 0x7fff; | |
90 | rate *= (ac97_div & 0xfff); | |
91 | ||
92 | return rate; | |
93 | } | |
94 | ||
95 | /* | |
96 | * Return the current HSIO bus clock frequency | |
97 | */ | |
98 | static unsigned long clk_pxa3xx_hsio_getrate(struct clk *clk) | |
99 | { | |
100 | unsigned long acsr; | |
101 | unsigned int hss, hsio_clk; | |
102 | ||
103 | acsr = ACSR; | |
104 | ||
105 | hss = (acsr >> 14) & 0x3; | |
106 | hsio_clk = (acsr & ACCR_D0CS) ? RO_CLK : hss_mult[hss] * BASE_CLK; | |
107 | ||
108 | return hsio_clk; | |
109 | } | |
110 | ||
c085052b EM |
111 | /* crystal frequency to static memory controller multiplier (SMCFS) */ |
112 | static unsigned int smcfs_mult[8] = { 6, 0, 8, 0, 0, 16, }; | |
113 | static unsigned int df_clkdiv[4] = { 1, 2, 4, 1 }; | |
114 | ||
115 | static unsigned long clk_pxa3xx_smemc_getrate(struct clk *clk) | |
116 | { | |
117 | unsigned long acsr = ACSR; | |
118 | unsigned long memclkcfg = __raw_readl(MEMCLKCFG); | |
c085052b EM |
119 | |
120 | return BASE_CLK * smcfs_mult[(acsr >> 23) & 0x7] / | |
121 | df_clkdiv[(memclkcfg >> 16) & 0x3]; | |
122 | } | |
123 | ||
4029813c EM |
124 | void clk_pxa3xx_cken_enable(struct clk *clk) |
125 | { | |
126 | unsigned long mask = 1ul << (clk->cken & 0x1f); | |
127 | ||
128 | if (clk->cken < 32) | |
129 | CKENA |= mask; | |
cd0a4a95 | 130 | else if (clk->cken < 64) |
4029813c | 131 | CKENB |= mask; |
cd0a4a95 CX |
132 | else |
133 | CKENC |= mask; | |
4029813c EM |
134 | } |
135 | ||
136 | void clk_pxa3xx_cken_disable(struct clk *clk) | |
137 | { | |
138 | unsigned long mask = 1ul << (clk->cken & 0x1f); | |
139 | ||
140 | if (clk->cken < 32) | |
141 | CKENA &= ~mask; | |
cd0a4a95 | 142 | else if (clk->cken < 64) |
4029813c | 143 | CKENB &= ~mask; |
cd0a4a95 CX |
144 | else |
145 | CKENC &= ~mask; | |
4029813c EM |
146 | } |
147 | ||
148 | const struct clkops clk_pxa3xx_cken_ops = { | |
149 | .enable = clk_pxa3xx_cken_enable, | |
150 | .disable = clk_pxa3xx_cken_disable, | |
151 | }; | |
152 | ||
153 | const struct clkops clk_pxa3xx_hsio_ops = { | |
154 | .enable = clk_pxa3xx_cken_enable, | |
155 | .disable = clk_pxa3xx_cken_disable, | |
156 | .getrate = clk_pxa3xx_hsio_getrate, | |
157 | }; | |
158 | ||
159 | const struct clkops clk_pxa3xx_ac97_ops = { | |
160 | .enable = clk_pxa3xx_cken_enable, | |
161 | .disable = clk_pxa3xx_cken_disable, | |
162 | .getrate = clk_pxa3xx_ac97_getrate, | |
163 | }; | |
164 | ||
c085052b EM |
165 | const struct clkops clk_pxa3xx_smemc_ops = { |
166 | .enable = clk_pxa3xx_cken_enable, | |
167 | .disable = clk_pxa3xx_cken_disable, | |
168 | .getrate = clk_pxa3xx_smemc_getrate, | |
169 | }; | |
170 | ||
4029813c EM |
171 | static void clk_pout_enable(struct clk *clk) |
172 | { | |
173 | OSCC |= OSCC_PEN; | |
174 | } | |
175 | ||
176 | static void clk_pout_disable(struct clk *clk) | |
177 | { | |
178 | OSCC &= ~OSCC_PEN; | |
179 | } | |
180 | ||
181 | const struct clkops clk_pxa3xx_pout_ops = { | |
182 | .enable = clk_pout_enable, | |
183 | .disable = clk_pout_disable, | |
184 | }; | |
aae8224d EM |
185 | |
186 | #ifdef CONFIG_PM | |
187 | static uint32_t cken[2]; | |
188 | static uint32_t accr; | |
189 | ||
2eaa03b5 | 190 | static int pxa3xx_clock_suspend(void) |
aae8224d EM |
191 | { |
192 | cken[0] = CKENA; | |
193 | cken[1] = CKENB; | |
194 | accr = ACCR; | |
195 | return 0; | |
196 | } | |
197 | ||
2eaa03b5 | 198 | static void pxa3xx_clock_resume(void) |
aae8224d EM |
199 | { |
200 | ACCR = accr; | |
201 | CKENA = cken[0]; | |
202 | CKENB = cken[1]; | |
aae8224d EM |
203 | } |
204 | #else | |
205 | #define pxa3xx_clock_suspend NULL | |
206 | #define pxa3xx_clock_resume NULL | |
207 | #endif | |
208 | ||
2eaa03b5 | 209 | struct syscore_ops pxa3xx_clock_syscore_ops = { |
aae8224d EM |
210 | .suspend = pxa3xx_clock_suspend, |
211 | .resume = pxa3xx_clock_resume, | |
212 | }; |