Commit | Line | Data |
---|---|---|
8369ae33 RM |
1 | /* |
2 | * Broadcom specific AMBA | |
3 | * ChipCommon Power Management Unit driver | |
4 | * | |
eb032b98 | 5 | * Copyright 2009, Michael Buesch <m@bues.ch> |
8369ae33 RM |
6 | * Copyright 2007, Broadcom Corporation |
7 | * | |
8 | * Licensed under the GNU/GPL. See COPYING for details. | |
9 | */ | |
10 | ||
11 | #include "bcma_private.h" | |
12 | #include <linux/bcma/bcma.h> | |
13 | ||
908debc8 HM |
14 | static u32 bcma_chipco_pll_read(struct bcma_drv_cc *cc, u32 offset) |
15 | { | |
16 | bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset); | |
17 | bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR); | |
18 | return bcma_cc_read32(cc, BCMA_CC_PLLCTL_DATA); | |
19 | } | |
20 | ||
3861b2c5 | 21 | void bcma_chipco_pll_write(struct bcma_drv_cc *cc, u32 offset, u32 value) |
8369ae33 | 22 | { |
3861b2c5 RM |
23 | bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset); |
24 | bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR); | |
25 | bcma_cc_write32(cc, BCMA_CC_PLLCTL_DATA, value); | |
26 | } | |
27 | EXPORT_SYMBOL_GPL(bcma_chipco_pll_write); | |
8369ae33 | 28 | |
3861b2c5 RM |
29 | void bcma_chipco_pll_maskset(struct bcma_drv_cc *cc, u32 offset, u32 mask, |
30 | u32 set) | |
31 | { | |
32 | bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset); | |
33 | bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR); | |
34 | bcma_cc_maskset32(cc, BCMA_CC_PLLCTL_DATA, mask, set); | |
35 | } | |
36 | EXPORT_SYMBOL_GPL(bcma_chipco_pll_maskset); | |
37 | ||
38 | void bcma_chipco_chipctl_maskset(struct bcma_drv_cc *cc, | |
39 | u32 offset, u32 mask, u32 set) | |
40 | { | |
8369ae33 RM |
41 | bcma_cc_write32(cc, BCMA_CC_CHIPCTL_ADDR, offset); |
42 | bcma_cc_read32(cc, BCMA_CC_CHIPCTL_ADDR); | |
3861b2c5 RM |
43 | bcma_cc_maskset32(cc, BCMA_CC_CHIPCTL_DATA, mask, set); |
44 | } | |
45 | EXPORT_SYMBOL_GPL(bcma_chipco_chipctl_maskset); | |
46 | ||
47 | void bcma_chipco_regctl_maskset(struct bcma_drv_cc *cc, u32 offset, u32 mask, | |
48 | u32 set) | |
49 | { | |
50 | bcma_cc_write32(cc, BCMA_CC_REGCTL_ADDR, offset); | |
51 | bcma_cc_read32(cc, BCMA_CC_REGCTL_ADDR); | |
52 | bcma_cc_maskset32(cc, BCMA_CC_REGCTL_DATA, mask, set); | |
8369ae33 | 53 | } |
3861b2c5 | 54 | EXPORT_SYMBOL_GPL(bcma_chipco_regctl_maskset); |
8369ae33 RM |
55 | |
56 | static void bcma_pmu_pll_init(struct bcma_drv_cc *cc) | |
57 | { | |
58 | struct bcma_bus *bus = cc->core->bus; | |
59 | ||
60 | switch (bus->chipinfo.id) { | |
61 | case 0x4313: | |
62 | case 0x4331: | |
63 | case 43224: | |
64 | case 43225: | |
65 | break; | |
66 | default: | |
67 | pr_err("PLL init unknown for device 0x%04X\n", | |
68 | bus->chipinfo.id); | |
69 | } | |
70 | } | |
71 | ||
72 | static void bcma_pmu_resources_init(struct bcma_drv_cc *cc) | |
73 | { | |
74 | struct bcma_bus *bus = cc->core->bus; | |
75 | u32 min_msk = 0, max_msk = 0; | |
76 | ||
77 | switch (bus->chipinfo.id) { | |
78 | case 0x4313: | |
79 | min_msk = 0x200D; | |
80 | max_msk = 0xFFFF; | |
81 | break; | |
82 | case 43224: | |
91fa4b0a | 83 | case 43225: |
8369ae33 RM |
84 | break; |
85 | default: | |
86 | pr_err("PMU resource config unknown for device 0x%04X\n", | |
87 | bus->chipinfo.id); | |
88 | } | |
89 | ||
90 | /* Set the resource masks. */ | |
91 | if (min_msk) | |
92 | bcma_cc_write32(cc, BCMA_CC_PMU_MINRES_MSK, min_msk); | |
93 | if (max_msk) | |
94 | bcma_cc_write32(cc, BCMA_CC_PMU_MAXRES_MSK, max_msk); | |
95 | } | |
96 | ||
97 | void bcma_pmu_swreg_init(struct bcma_drv_cc *cc) | |
98 | { | |
99 | struct bcma_bus *bus = cc->core->bus; | |
100 | ||
101 | switch (bus->chipinfo.id) { | |
102 | case 0x4313: | |
103 | case 0x4331: | |
104 | case 43224: | |
91fa4b0a | 105 | case 43225: |
8369ae33 RM |
106 | break; |
107 | default: | |
108 | pr_err("PMU switch/regulators init unknown for device " | |
109 | "0x%04X\n", bus->chipinfo.id); | |
110 | } | |
111 | } | |
112 | ||
984e5bef RM |
113 | /* Disable to allow reading SPROM. Don't know the adventages of enabling it. */ |
114 | void bcma_chipco_bcm4331_ext_pa_lines_ctl(struct bcma_drv_cc *cc, bool enable) | |
115 | { | |
116 | struct bcma_bus *bus = cc->core->bus; | |
117 | u32 val; | |
118 | ||
119 | val = bcma_cc_read32(cc, BCMA_CC_CHIPCTL); | |
120 | if (enable) { | |
121 | val |= BCMA_CHIPCTL_4331_EXTPA_EN; | |
122 | if (bus->chipinfo.pkg == 9 || bus->chipinfo.pkg == 11) | |
123 | val |= BCMA_CHIPCTL_4331_EXTPA_ON_GPIO2_5; | |
124 | } else { | |
125 | val &= ~BCMA_CHIPCTL_4331_EXTPA_EN; | |
126 | val &= ~BCMA_CHIPCTL_4331_EXTPA_ON_GPIO2_5; | |
127 | } | |
128 | bcma_cc_write32(cc, BCMA_CC_CHIPCTL, val); | |
129 | } | |
130 | ||
8369ae33 RM |
131 | void bcma_pmu_workarounds(struct bcma_drv_cc *cc) |
132 | { | |
133 | struct bcma_bus *bus = cc->core->bus; | |
134 | ||
135 | switch (bus->chipinfo.id) { | |
136 | case 0x4313: | |
137 | bcma_chipco_chipctl_maskset(cc, 0, ~0, 0x7); | |
138 | break; | |
139 | case 0x4331: | |
984e5bef | 140 | /* BCM4331 workaround is SPROM-related, we put it in sprom.c */ |
8369ae33 RM |
141 | break; |
142 | case 43224: | |
143 | if (bus->chipinfo.rev == 0) { | |
144 | pr_err("Workarounds for 43224 rev 0 not fully " | |
145 | "implemented\n"); | |
898f699e | 146 | bcma_chipco_chipctl_maskset(cc, 0, ~0, 0x00F000F0); |
8369ae33 RM |
147 | } else { |
148 | bcma_chipco_chipctl_maskset(cc, 0, ~0, 0xF0); | |
149 | } | |
150 | break; | |
91fa4b0a RM |
151 | case 43225: |
152 | break; | |
8369ae33 RM |
153 | default: |
154 | pr_err("Workarounds unknown for device 0x%04X\n", | |
155 | bus->chipinfo.id); | |
156 | } | |
157 | } | |
158 | ||
159 | void bcma_pmu_init(struct bcma_drv_cc *cc) | |
160 | { | |
161 | u32 pmucap; | |
162 | ||
163 | pmucap = bcma_cc_read32(cc, BCMA_CC_PMU_CAP); | |
164 | cc->pmu.rev = (pmucap & BCMA_CC_PMU_CAP_REVISION); | |
165 | ||
166 | pr_debug("Found rev %u PMU (capabilities 0x%08X)\n", cc->pmu.rev, | |
167 | pmucap); | |
168 | ||
169 | if (cc->pmu.rev == 1) | |
170 | bcma_cc_mask32(cc, BCMA_CC_PMU_CTL, | |
171 | ~BCMA_CC_PMU_CTL_NOILPONW); | |
172 | else | |
173 | bcma_cc_set32(cc, BCMA_CC_PMU_CTL, | |
174 | BCMA_CC_PMU_CTL_NOILPONW); | |
175 | ||
176 | if (cc->core->id.id == 0x4329 && cc->core->id.rev == 2) | |
177 | pr_err("Fix for 4329b0 bad LPOM state not implemented!\n"); | |
178 | ||
179 | bcma_pmu_pll_init(cc); | |
180 | bcma_pmu_resources_init(cc); | |
181 | bcma_pmu_swreg_init(cc); | |
182 | bcma_pmu_workarounds(cc); | |
183 | } | |
e3afe0e5 HM |
184 | |
185 | u32 bcma_pmu_alp_clock(struct bcma_drv_cc *cc) | |
186 | { | |
187 | struct bcma_bus *bus = cc->core->bus; | |
188 | ||
189 | switch (bus->chipinfo.id) { | |
190 | case 0x4716: | |
191 | case 0x4748: | |
192 | case 47162: | |
193 | case 0x4313: | |
194 | case 0x5357: | |
195 | case 0x4749: | |
196 | case 53572: | |
197 | /* always 20Mhz */ | |
198 | return 20000 * 1000; | |
199 | case 0x5356: | |
200 | case 0x5300: | |
201 | /* always 25Mhz */ | |
202 | return 25000 * 1000; | |
203 | default: | |
204 | pr_warn("No ALP clock specified for %04X device, " | |
205 | "pmu rev. %d, using default %d Hz\n", | |
206 | bus->chipinfo.id, cc->pmu.rev, BCMA_CC_PMU_ALP_CLOCK); | |
207 | } | |
208 | return BCMA_CC_PMU_ALP_CLOCK; | |
209 | } | |
908debc8 HM |
210 | |
211 | /* Find the output of the "m" pll divider given pll controls that start with | |
212 | * pllreg "pll0" i.e. 12 for main 6 for phy, 0 for misc. | |
213 | */ | |
214 | static u32 bcma_pmu_clock(struct bcma_drv_cc *cc, u32 pll0, u32 m) | |
215 | { | |
216 | u32 tmp, div, ndiv, p1, p2, fc; | |
217 | struct bcma_bus *bus = cc->core->bus; | |
218 | ||
219 | BUG_ON((pll0 & 3) || (pll0 > BCMA_CC_PMU4716_MAINPLL_PLL0)); | |
220 | ||
221 | BUG_ON(!m || m > 4); | |
222 | ||
223 | if (bus->chipinfo.id == 0x5357 || bus->chipinfo.id == 0x4749) { | |
224 | /* Detect failure in clock setting */ | |
225 | tmp = bcma_cc_read32(cc, BCMA_CC_CHIPSTAT); | |
226 | if (tmp & 0x40000) | |
227 | return 133 * 1000000; | |
228 | } | |
229 | ||
230 | tmp = bcma_chipco_pll_read(cc, pll0 + BCMA_CC_PPL_P1P2_OFF); | |
231 | p1 = (tmp & BCMA_CC_PPL_P1_MASK) >> BCMA_CC_PPL_P1_SHIFT; | |
232 | p2 = (tmp & BCMA_CC_PPL_P2_MASK) >> BCMA_CC_PPL_P2_SHIFT; | |
233 | ||
234 | tmp = bcma_chipco_pll_read(cc, pll0 + BCMA_CC_PPL_M14_OFF); | |
235 | div = (tmp >> ((m - 1) * BCMA_CC_PPL_MDIV_WIDTH)) & | |
236 | BCMA_CC_PPL_MDIV_MASK; | |
237 | ||
238 | tmp = bcma_chipco_pll_read(cc, pll0 + BCMA_CC_PPL_NM5_OFF); | |
239 | ndiv = (tmp & BCMA_CC_PPL_NDIV_MASK) >> BCMA_CC_PPL_NDIV_SHIFT; | |
240 | ||
241 | /* Do calculation in Mhz */ | |
242 | fc = bcma_pmu_alp_clock(cc) / 1000000; | |
243 | fc = (p1 * ndiv * fc) / p2; | |
244 | ||
245 | /* Return clock in Hertz */ | |
246 | return (fc / div) * 1000000; | |
247 | } | |
248 | ||
249 | /* query bus clock frequency for PMU-enabled chipcommon */ | |
250 | u32 bcma_pmu_get_clockcontrol(struct bcma_drv_cc *cc) | |
251 | { | |
252 | struct bcma_bus *bus = cc->core->bus; | |
253 | ||
254 | switch (bus->chipinfo.id) { | |
255 | case 0x4716: | |
256 | case 0x4748: | |
257 | case 47162: | |
258 | return bcma_pmu_clock(cc, BCMA_CC_PMU4716_MAINPLL_PLL0, | |
259 | BCMA_CC_PMU5_MAINPLL_SSB); | |
260 | case 0x5356: | |
261 | return bcma_pmu_clock(cc, BCMA_CC_PMU5356_MAINPLL_PLL0, | |
262 | BCMA_CC_PMU5_MAINPLL_SSB); | |
263 | case 0x5357: | |
264 | case 0x4749: | |
265 | return bcma_pmu_clock(cc, BCMA_CC_PMU5357_MAINPLL_PLL0, | |
266 | BCMA_CC_PMU5_MAINPLL_SSB); | |
267 | case 0x5300: | |
268 | return bcma_pmu_clock(cc, BCMA_CC_PMU4706_MAINPLL_PLL0, | |
269 | BCMA_CC_PMU5_MAINPLL_SSB); | |
270 | case 53572: | |
271 | return 75000000; | |
272 | default: | |
273 | pr_warn("No backplane clock specified for %04X device, " | |
274 | "pmu rev. %d, using default %d Hz\n", | |
275 | bus->chipinfo.id, cc->pmu.rev, BCMA_CC_PMU_HT_CLOCK); | |
276 | } | |
277 | return BCMA_CC_PMU_HT_CLOCK; | |
278 | } | |
279 | ||
280 | /* query cpu clock frequency for PMU-enabled chipcommon */ | |
281 | u32 bcma_pmu_get_clockcpu(struct bcma_drv_cc *cc) | |
282 | { | |
283 | struct bcma_bus *bus = cc->core->bus; | |
284 | ||
285 | if (bus->chipinfo.id == 53572) | |
286 | return 300000000; | |
287 | ||
288 | if (cc->pmu.rev >= 5) { | |
289 | u32 pll; | |
290 | switch (bus->chipinfo.id) { | |
291 | case 0x5356: | |
292 | pll = BCMA_CC_PMU5356_MAINPLL_PLL0; | |
293 | break; | |
294 | case 0x5357: | |
295 | case 0x4749: | |
296 | pll = BCMA_CC_PMU5357_MAINPLL_PLL0; | |
297 | break; | |
298 | default: | |
299 | pll = BCMA_CC_PMU4716_MAINPLL_PLL0; | |
300 | break; | |
301 | } | |
302 | ||
303 | /* TODO: if (bus->chipinfo.id == 0x5300) | |
304 | return si_4706_pmu_clock(sih, osh, cc, PMU4706_MAINPLL_PLL0, PMU5_MAINPLL_CPU); */ | |
305 | return bcma_pmu_clock(cc, pll, BCMA_CC_PMU5_MAINPLL_CPU); | |
306 | } | |
307 | ||
308 | return bcma_pmu_get_clockcontrol(cc); | |
309 | } |