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