]>
Commit | Line | Data |
---|---|---|
61e115a5 MB |
1 | /* |
2 | * Sonics Silicon Backplane | |
3 | * Broadcom ChipCommon core driver | |
4 | * | |
5 | * Copyright 2005, Broadcom Corporation | |
eb032b98 | 6 | * Copyright 2006, 2007, Michael Buesch <[email protected]> |
7ffbffe3 | 7 | * Copyright 2012, Hauke Mehrtens <[email protected]> |
61e115a5 MB |
8 | * |
9 | * Licensed under the GNU/GPL. See COPYING for details. | |
10 | */ | |
11 | ||
12 | #include <linux/ssb/ssb.h> | |
13 | #include <linux/ssb/ssb_regs.h> | |
1014c22e | 14 | #include <linux/export.h> |
61e115a5 | 15 | #include <linux/pci.h> |
7ffbffe3 | 16 | #include <linux/bcm47xx_wdt.h> |
61e115a5 MB |
17 | |
18 | #include "ssb_private.h" | |
19 | ||
20 | ||
21 | /* Clock sources */ | |
22 | enum ssb_clksrc { | |
23 | /* PCI clock */ | |
24 | SSB_CHIPCO_CLKSRC_PCI, | |
25 | /* Crystal slow clock oscillator */ | |
26 | SSB_CHIPCO_CLKSRC_XTALOS, | |
27 | /* Low power oscillator */ | |
28 | SSB_CHIPCO_CLKSRC_LOPWROS, | |
29 | }; | |
30 | ||
31 | ||
c2bcbe65 MB |
32 | static inline u32 chipco_write32_masked(struct ssb_chipcommon *cc, u16 offset, |
33 | u32 mask, u32 value) | |
61e115a5 MB |
34 | { |
35 | value &= mask; | |
36 | value |= chipco_read32(cc, offset) & ~mask; | |
37 | chipco_write32(cc, offset, value); | |
c2bcbe65 MB |
38 | |
39 | return value; | |
61e115a5 MB |
40 | } |
41 | ||
42 | void ssb_chipco_set_clockmode(struct ssb_chipcommon *cc, | |
43 | enum ssb_clkmode mode) | |
44 | { | |
45 | struct ssb_device *ccdev = cc->dev; | |
46 | struct ssb_bus *bus; | |
47 | u32 tmp; | |
48 | ||
49 | if (!ccdev) | |
50 | return; | |
51 | bus = ccdev->bus; | |
0ca69955 RM |
52 | |
53 | /* We support SLOW only on 6..9 */ | |
54 | if (ccdev->id.revision >= 10 && mode == SSB_CLKMODE_SLOW) | |
55 | mode = SSB_CLKMODE_DYNAMIC; | |
56 | ||
57 | if (cc->capabilities & SSB_CHIPCO_CAP_PMU) | |
58 | return; /* PMU controls clockmode, separated function needed */ | |
59 | SSB_WARN_ON(ccdev->id.revision >= 20); | |
60 | ||
61e115a5 MB |
61 | /* chipcommon cores prior to rev6 don't support dynamic clock control */ |
62 | if (ccdev->id.revision < 6) | |
63 | return; | |
0ca69955 RM |
64 | |
65 | /* ChipCommon cores rev10+ need testing */ | |
61e115a5 MB |
66 | if (ccdev->id.revision >= 10) |
67 | return; | |
0ca69955 | 68 | |
61e115a5 MB |
69 | if (!(cc->capabilities & SSB_CHIPCO_CAP_PCTL)) |
70 | return; | |
71 | ||
72 | switch (mode) { | |
0ca69955 | 73 | case SSB_CLKMODE_SLOW: /* For revs 6..9 only */ |
61e115a5 MB |
74 | tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL); |
75 | tmp |= SSB_CHIPCO_SLOWCLKCTL_FSLOW; | |
76 | chipco_write32(cc, SSB_CHIPCO_SLOWCLKCTL, tmp); | |
77 | break; | |
78 | case SSB_CLKMODE_FAST: | |
0ca69955 RM |
79 | if (ccdev->id.revision < 10) { |
80 | ssb_pci_xtal(bus, SSB_GPIO_XTAL, 1); /* Force crystal on */ | |
81 | tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL); | |
82 | tmp &= ~SSB_CHIPCO_SLOWCLKCTL_FSLOW; | |
83 | tmp |= SSB_CHIPCO_SLOWCLKCTL_IPLL; | |
84 | chipco_write32(cc, SSB_CHIPCO_SLOWCLKCTL, tmp); | |
85 | } else { | |
86 | chipco_write32(cc, SSB_CHIPCO_SYSCLKCTL, | |
87 | (chipco_read32(cc, SSB_CHIPCO_SYSCLKCTL) | | |
88 | SSB_CHIPCO_SYSCLKCTL_FORCEHT)); | |
89 | /* udelay(150); TODO: not available in early init */ | |
90 | } | |
61e115a5 MB |
91 | break; |
92 | case SSB_CLKMODE_DYNAMIC: | |
0ca69955 RM |
93 | if (ccdev->id.revision < 10) { |
94 | tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL); | |
95 | tmp &= ~SSB_CHIPCO_SLOWCLKCTL_FSLOW; | |
96 | tmp &= ~SSB_CHIPCO_SLOWCLKCTL_IPLL; | |
97 | tmp &= ~SSB_CHIPCO_SLOWCLKCTL_ENXTAL; | |
98 | if ((tmp & SSB_CHIPCO_SLOWCLKCTL_SRC) != | |
99 | SSB_CHIPCO_SLOWCLKCTL_SRC_XTAL) | |
100 | tmp |= SSB_CHIPCO_SLOWCLKCTL_ENXTAL; | |
101 | chipco_write32(cc, SSB_CHIPCO_SLOWCLKCTL, tmp); | |
61e115a5 | 102 | |
0ca69955 RM |
103 | /* For dynamic control, we have to release our xtal_pu |
104 | * "force on" */ | |
105 | if (tmp & SSB_CHIPCO_SLOWCLKCTL_ENXTAL) | |
106 | ssb_pci_xtal(bus, SSB_GPIO_XTAL, 0); | |
107 | } else { | |
108 | chipco_write32(cc, SSB_CHIPCO_SYSCLKCTL, | |
109 | (chipco_read32(cc, SSB_CHIPCO_SYSCLKCTL) & | |
110 | ~SSB_CHIPCO_SYSCLKCTL_FORCEHT)); | |
111 | } | |
61e115a5 MB |
112 | break; |
113 | default: | |
114 | SSB_WARN_ON(1); | |
115 | } | |
116 | } | |
117 | ||
118 | /* Get the Slow Clock Source */ | |
119 | static enum ssb_clksrc chipco_pctl_get_slowclksrc(struct ssb_chipcommon *cc) | |
120 | { | |
121 | struct ssb_bus *bus = cc->dev->bus; | |
122 | u32 uninitialized_var(tmp); | |
123 | ||
124 | if (cc->dev->id.revision < 6) { | |
125 | if (bus->bustype == SSB_BUSTYPE_SSB || | |
126 | bus->bustype == SSB_BUSTYPE_PCMCIA) | |
127 | return SSB_CHIPCO_CLKSRC_XTALOS; | |
128 | if (bus->bustype == SSB_BUSTYPE_PCI) { | |
129 | pci_read_config_dword(bus->host_pci, SSB_GPIO_OUT, &tmp); | |
130 | if (tmp & 0x10) | |
131 | return SSB_CHIPCO_CLKSRC_PCI; | |
132 | return SSB_CHIPCO_CLKSRC_XTALOS; | |
133 | } | |
134 | } | |
135 | if (cc->dev->id.revision < 10) { | |
136 | tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL); | |
137 | tmp &= 0x7; | |
138 | if (tmp == 0) | |
139 | return SSB_CHIPCO_CLKSRC_LOPWROS; | |
140 | if (tmp == 1) | |
141 | return SSB_CHIPCO_CLKSRC_XTALOS; | |
142 | if (tmp == 2) | |
143 | return SSB_CHIPCO_CLKSRC_PCI; | |
144 | } | |
145 | ||
146 | return SSB_CHIPCO_CLKSRC_XTALOS; | |
147 | } | |
148 | ||
149 | /* Get maximum or minimum (depending on get_max flag) slowclock frequency. */ | |
150 | static int chipco_pctl_clockfreqlimit(struct ssb_chipcommon *cc, int get_max) | |
151 | { | |
152 | int uninitialized_var(limit); | |
153 | enum ssb_clksrc clocksrc; | |
154 | int divisor = 1; | |
155 | u32 tmp; | |
156 | ||
157 | clocksrc = chipco_pctl_get_slowclksrc(cc); | |
158 | if (cc->dev->id.revision < 6) { | |
159 | switch (clocksrc) { | |
160 | case SSB_CHIPCO_CLKSRC_PCI: | |
161 | divisor = 64; | |
162 | break; | |
163 | case SSB_CHIPCO_CLKSRC_XTALOS: | |
164 | divisor = 32; | |
165 | break; | |
166 | default: | |
167 | SSB_WARN_ON(1); | |
168 | } | |
169 | } else if (cc->dev->id.revision < 10) { | |
170 | switch (clocksrc) { | |
171 | case SSB_CHIPCO_CLKSRC_LOPWROS: | |
172 | break; | |
173 | case SSB_CHIPCO_CLKSRC_XTALOS: | |
174 | case SSB_CHIPCO_CLKSRC_PCI: | |
175 | tmp = chipco_read32(cc, SSB_CHIPCO_SLOWCLKCTL); | |
176 | divisor = (tmp >> 16) + 1; | |
177 | divisor *= 4; | |
178 | break; | |
179 | } | |
180 | } else { | |
181 | tmp = chipco_read32(cc, SSB_CHIPCO_SYSCLKCTL); | |
182 | divisor = (tmp >> 16) + 1; | |
183 | divisor *= 4; | |
184 | } | |
185 | ||
186 | switch (clocksrc) { | |
187 | case SSB_CHIPCO_CLKSRC_LOPWROS: | |
188 | if (get_max) | |
189 | limit = 43000; | |
190 | else | |
191 | limit = 25000; | |
192 | break; | |
193 | case SSB_CHIPCO_CLKSRC_XTALOS: | |
194 | if (get_max) | |
195 | limit = 20200000; | |
196 | else | |
197 | limit = 19800000; | |
198 | break; | |
199 | case SSB_CHIPCO_CLKSRC_PCI: | |
200 | if (get_max) | |
201 | limit = 34000000; | |
202 | else | |
203 | limit = 25000000; | |
204 | break; | |
205 | } | |
206 | limit /= divisor; | |
207 | ||
208 | return limit; | |
209 | } | |
210 | ||
211 | static void chipco_powercontrol_init(struct ssb_chipcommon *cc) | |
212 | { | |
213 | struct ssb_bus *bus = cc->dev->bus; | |
214 | ||
215 | if (bus->chip_id == 0x4321) { | |
216 | if (bus->chip_rev == 0) | |
217 | chipco_write32(cc, SSB_CHIPCO_CHIPCTL, 0x3A4); | |
218 | else if (bus->chip_rev == 1) | |
219 | chipco_write32(cc, SSB_CHIPCO_CHIPCTL, 0xA4); | |
220 | } | |
221 | ||
222 | if (!(cc->capabilities & SSB_CHIPCO_CAP_PCTL)) | |
223 | return; | |
224 | ||
225 | if (cc->dev->id.revision >= 10) { | |
226 | /* Set Idle Power clock rate to 1Mhz */ | |
227 | chipco_write32(cc, SSB_CHIPCO_SYSCLKCTL, | |
228 | (chipco_read32(cc, SSB_CHIPCO_SYSCLKCTL) & | |
229 | 0x0000FFFF) | 0x00040000); | |
230 | } else { | |
231 | int maxfreq; | |
232 | ||
233 | maxfreq = chipco_pctl_clockfreqlimit(cc, 1); | |
234 | chipco_write32(cc, SSB_CHIPCO_PLLONDELAY, | |
235 | (maxfreq * 150 + 999999) / 1000000); | |
236 | chipco_write32(cc, SSB_CHIPCO_FREFSELDELAY, | |
237 | (maxfreq * 15 + 999999) / 1000000); | |
238 | } | |
239 | } | |
240 | ||
fd515941 RM |
241 | /* http://bcm-v4.sipsolutions.net/802.11/PmuFastPwrupDelay */ |
242 | static u16 pmu_fast_powerup_delay(struct ssb_chipcommon *cc) | |
243 | { | |
244 | struct ssb_bus *bus = cc->dev->bus; | |
245 | ||
246 | switch (bus->chip_id) { | |
247 | case 0x4312: | |
248 | case 0x4322: | |
249 | case 0x4328: | |
250 | return 7000; | |
251 | case 0x4325: | |
252 | /* TODO: */ | |
253 | default: | |
254 | return 15000; | |
255 | } | |
256 | } | |
257 | ||
258 | /* http://bcm-v4.sipsolutions.net/802.11/ClkctlFastPwrupDelay */ | |
61e115a5 MB |
259 | static void calc_fast_powerup_delay(struct ssb_chipcommon *cc) |
260 | { | |
261 | struct ssb_bus *bus = cc->dev->bus; | |
262 | int minfreq; | |
263 | unsigned int tmp; | |
264 | u32 pll_on_delay; | |
265 | ||
266 | if (bus->bustype != SSB_BUSTYPE_PCI) | |
267 | return; | |
fd515941 RM |
268 | |
269 | if (cc->capabilities & SSB_CHIPCO_CAP_PMU) { | |
270 | cc->fast_pwrup_delay = pmu_fast_powerup_delay(cc); | |
271 | return; | |
272 | } | |
273 | ||
61e115a5 MB |
274 | if (!(cc->capabilities & SSB_CHIPCO_CAP_PCTL)) |
275 | return; | |
276 | ||
277 | minfreq = chipco_pctl_clockfreqlimit(cc, 0); | |
278 | pll_on_delay = chipco_read32(cc, SSB_CHIPCO_PLLONDELAY); | |
279 | tmp = (((pll_on_delay + 2) * 1000000) + (minfreq - 1)) / minfreq; | |
280 | SSB_WARN_ON(tmp & ~0xFFFF); | |
281 | ||
282 | cc->fast_pwrup_delay = tmp; | |
283 | } | |
284 | ||
f924e1e9 HM |
285 | static u32 ssb_chipco_alp_clock(struct ssb_chipcommon *cc) |
286 | { | |
287 | if (cc->capabilities & SSB_CHIPCO_CAP_PMU) | |
288 | return ssb_pmu_get_alp_clock(cc); | |
289 | ||
290 | return 20000000; | |
291 | } | |
292 | ||
26107309 HM |
293 | static u32 ssb_chipco_watchdog_get_max_timer(struct ssb_chipcommon *cc) |
294 | { | |
295 | u32 nb; | |
296 | ||
297 | if (cc->capabilities & SSB_CHIPCO_CAP_PMU) { | |
298 | if (cc->dev->id.revision < 26) | |
299 | nb = 16; | |
300 | else | |
301 | nb = (cc->dev->id.revision >= 37) ? 32 : 24; | |
302 | } else { | |
303 | nb = 28; | |
304 | } | |
305 | if (nb == 32) | |
306 | return 0xffffffff; | |
307 | else | |
308 | return (1 << nb) - 1; | |
309 | } | |
310 | ||
7ffbffe3 HM |
311 | u32 ssb_chipco_watchdog_timer_set_wdt(struct bcm47xx_wdt *wdt, u32 ticks) |
312 | { | |
313 | struct ssb_chipcommon *cc = bcm47xx_wdt_get_drvdata(wdt); | |
314 | ||
315 | if (cc->dev->bus->bustype != SSB_BUSTYPE_SSB) | |
316 | return 0; | |
317 | ||
318 | return ssb_chipco_watchdog_timer_set(cc, ticks); | |
319 | } | |
320 | ||
321 | u32 ssb_chipco_watchdog_timer_set_ms(struct bcm47xx_wdt *wdt, u32 ms) | |
322 | { | |
323 | struct ssb_chipcommon *cc = bcm47xx_wdt_get_drvdata(wdt); | |
324 | u32 ticks; | |
325 | ||
326 | if (cc->dev->bus->bustype != SSB_BUSTYPE_SSB) | |
327 | return 0; | |
328 | ||
329 | ticks = ssb_chipco_watchdog_timer_set(cc, cc->ticks_per_ms * ms); | |
330 | return ticks / cc->ticks_per_ms; | |
331 | } | |
332 | ||
333 | static int ssb_chipco_watchdog_ticks_per_ms(struct ssb_chipcommon *cc) | |
334 | { | |
335 | struct ssb_bus *bus = cc->dev->bus; | |
336 | ||
337 | if (cc->capabilities & SSB_CHIPCO_CAP_PMU) { | |
338 | /* based on 32KHz ILP clock */ | |
339 | return 32; | |
340 | } else { | |
341 | if (cc->dev->id.revision < 18) | |
342 | return ssb_clockspeed(bus) / 1000; | |
343 | else | |
344 | return ssb_chipco_alp_clock(cc) / 1000; | |
345 | } | |
346 | } | |
347 | ||
61e115a5 MB |
348 | void ssb_chipcommon_init(struct ssb_chipcommon *cc) |
349 | { | |
350 | if (!cc->dev) | |
351 | return; /* We don't have a ChipCommon */ | |
394bc7e3 HM |
352 | |
353 | spin_lock_init(&cc->gpio_lock); | |
354 | ||
d53cdbb9 JL |
355 | if (cc->dev->id.revision >= 11) |
356 | cc->status = chipco_read32(cc, SSB_CHIPCO_CHIPSTAT); | |
33a606ac | 357 | ssb_dbg("chipcommon status is 0x%x\n", cc->status); |
9835a30e RM |
358 | |
359 | if (cc->dev->id.revision >= 20) { | |
360 | chipco_write32(cc, SSB_CHIPCO_GPIOPULLUP, 0); | |
361 | chipco_write32(cc, SSB_CHIPCO_GPIOPULLDOWN, 0); | |
362 | } | |
363 | ||
c9703146 | 364 | ssb_pmu_init(cc); |
61e115a5 MB |
365 | chipco_powercontrol_init(cc); |
366 | ssb_chipco_set_clockmode(cc, SSB_CLKMODE_FAST); | |
367 | calc_fast_powerup_delay(cc); | |
7ffbffe3 HM |
368 | |
369 | if (cc->dev->bus->bustype == SSB_BUSTYPE_SSB) { | |
370 | cc->ticks_per_ms = ssb_chipco_watchdog_ticks_per_ms(cc); | |
371 | cc->max_timer_ms = ssb_chipco_watchdog_get_max_timer(cc) / cc->ticks_per_ms; | |
372 | } | |
61e115a5 MB |
373 | } |
374 | ||
8fe2b65a | 375 | void ssb_chipco_suspend(struct ssb_chipcommon *cc) |
61e115a5 MB |
376 | { |
377 | if (!cc->dev) | |
378 | return; | |
379 | ssb_chipco_set_clockmode(cc, SSB_CLKMODE_SLOW); | |
380 | } | |
381 | ||
382 | void ssb_chipco_resume(struct ssb_chipcommon *cc) | |
383 | { | |
384 | if (!cc->dev) | |
385 | return; | |
386 | chipco_powercontrol_init(cc); | |
387 | ssb_chipco_set_clockmode(cc, SSB_CLKMODE_FAST); | |
388 | } | |
389 | ||
390 | /* Get the processor clock */ | |
391 | void ssb_chipco_get_clockcpu(struct ssb_chipcommon *cc, | |
392 | u32 *plltype, u32 *n, u32 *m) | |
393 | { | |
394 | *n = chipco_read32(cc, SSB_CHIPCO_CLOCK_N); | |
395 | *plltype = (cc->capabilities & SSB_CHIPCO_CAP_PLLT); | |
396 | switch (*plltype) { | |
397 | case SSB_PLLTYPE_2: | |
398 | case SSB_PLLTYPE_4: | |
399 | case SSB_PLLTYPE_6: | |
400 | case SSB_PLLTYPE_7: | |
401 | *m = chipco_read32(cc, SSB_CHIPCO_CLOCK_MIPS); | |
402 | break; | |
403 | case SSB_PLLTYPE_3: | |
404 | /* 5350 uses m2 to control mips */ | |
405 | *m = chipco_read32(cc, SSB_CHIPCO_CLOCK_M2); | |
406 | break; | |
407 | default: | |
408 | *m = chipco_read32(cc, SSB_CHIPCO_CLOCK_SB); | |
409 | break; | |
410 | } | |
411 | } | |
412 | ||
413 | /* Get the bus clock */ | |
414 | void ssb_chipco_get_clockcontrol(struct ssb_chipcommon *cc, | |
415 | u32 *plltype, u32 *n, u32 *m) | |
416 | { | |
417 | *n = chipco_read32(cc, SSB_CHIPCO_CLOCK_N); | |
418 | *plltype = (cc->capabilities & SSB_CHIPCO_CAP_PLLT); | |
419 | switch (*plltype) { | |
420 | case SSB_PLLTYPE_6: /* 100/200 or 120/240 only */ | |
421 | *m = chipco_read32(cc, SSB_CHIPCO_CLOCK_MIPS); | |
422 | break; | |
423 | case SSB_PLLTYPE_3: /* 25Mhz, 2 dividers */ | |
424 | if (cc->dev->bus->chip_id != 0x5365) { | |
425 | *m = chipco_read32(cc, SSB_CHIPCO_CLOCK_M2); | |
426 | break; | |
427 | } | |
428 | /* Fallthough */ | |
429 | default: | |
430 | *m = chipco_read32(cc, SSB_CHIPCO_CLOCK_SB); | |
431 | } | |
432 | } | |
433 | ||
434 | void ssb_chipco_timing_init(struct ssb_chipcommon *cc, | |
435 | unsigned long ns) | |
436 | { | |
437 | struct ssb_device *dev = cc->dev; | |
438 | struct ssb_bus *bus = dev->bus; | |
439 | u32 tmp; | |
440 | ||
441 | /* set register for external IO to control LED. */ | |
442 | chipco_write32(cc, SSB_CHIPCO_PROG_CFG, 0x11); | |
443 | tmp = DIV_ROUND_UP(10, ns) << SSB_PROG_WCNT_3_SHIFT; /* Waitcount-3 = 10ns */ | |
444 | tmp |= DIV_ROUND_UP(40, ns) << SSB_PROG_WCNT_1_SHIFT; /* Waitcount-1 = 40ns */ | |
445 | tmp |= DIV_ROUND_UP(240, ns); /* Waitcount-0 = 240ns */ | |
446 | chipco_write32(cc, SSB_CHIPCO_PROG_WAITCNT, tmp); /* 0x01020a0c for a 100Mhz clock */ | |
447 | ||
448 | /* Set timing for the flash */ | |
449 | tmp = DIV_ROUND_UP(10, ns) << SSB_FLASH_WCNT_3_SHIFT; /* Waitcount-3 = 10nS */ | |
450 | tmp |= DIV_ROUND_UP(10, ns) << SSB_FLASH_WCNT_1_SHIFT; /* Waitcount-1 = 10nS */ | |
451 | tmp |= DIV_ROUND_UP(120, ns); /* Waitcount-0 = 120nS */ | |
452 | if ((bus->chip_id == 0x5365) || | |
453 | (dev->id.revision < 9)) | |
454 | chipco_write32(cc, SSB_CHIPCO_FLASH_WAITCNT, tmp); | |
455 | if ((bus->chip_id == 0x5365) || | |
456 | (dev->id.revision < 9) || | |
457 | ((bus->chip_id == 0x5350) && (bus->chip_rev == 0))) | |
458 | chipco_write32(cc, SSB_CHIPCO_PCMCIA_MEMWAIT, tmp); | |
459 | ||
460 | if (bus->chip_id == 0x5350) { | |
461 | /* Enable EXTIF */ | |
462 | tmp = DIV_ROUND_UP(10, ns) << SSB_PROG_WCNT_3_SHIFT; /* Waitcount-3 = 10ns */ | |
463 | tmp |= DIV_ROUND_UP(20, ns) << SSB_PROG_WCNT_2_SHIFT; /* Waitcount-2 = 20ns */ | |
464 | tmp |= DIV_ROUND_UP(100, ns) << SSB_PROG_WCNT_1_SHIFT; /* Waitcount-1 = 100ns */ | |
465 | tmp |= DIV_ROUND_UP(120, ns); /* Waitcount-0 = 120ns */ | |
466 | chipco_write32(cc, SSB_CHIPCO_PROG_WAITCNT, tmp); /* 0x01020a0c for a 100Mhz clock */ | |
467 | } | |
468 | } | |
469 | ||
470 | /* Set chip watchdog reset timer to fire in 'ticks' backplane cycles */ | |
7ffbffe3 | 471 | u32 ssb_chipco_watchdog_timer_set(struct ssb_chipcommon *cc, u32 ticks) |
61e115a5 | 472 | { |
26107309 HM |
473 | u32 maxt; |
474 | enum ssb_clkmode clkmode; | |
475 | ||
476 | maxt = ssb_chipco_watchdog_get_max_timer(cc); | |
477 | if (cc->capabilities & SSB_CHIPCO_CAP_PMU) { | |
478 | if (ticks == 1) | |
479 | ticks = 2; | |
480 | else if (ticks > maxt) | |
481 | ticks = maxt; | |
482 | chipco_write32(cc, SSB_CHIPCO_PMU_WATCHDOG, ticks); | |
483 | } else { | |
484 | clkmode = ticks ? SSB_CLKMODE_FAST : SSB_CLKMODE_DYNAMIC; | |
485 | ssb_chipco_set_clockmode(cc, clkmode); | |
486 | if (ticks > maxt) | |
487 | ticks = maxt; | |
488 | /* instant NMI */ | |
489 | chipco_write32(cc, SSB_CHIPCO_WATCHDOG, ticks); | |
490 | } | |
7ffbffe3 | 491 | return ticks; |
61e115a5 MB |
492 | } |
493 | ||
28de57d1 AJ |
494 | void ssb_chipco_irq_mask(struct ssb_chipcommon *cc, u32 mask, u32 value) |
495 | { | |
496 | chipco_write32_masked(cc, SSB_CHIPCO_IRQMASK, mask, value); | |
497 | } | |
498 | ||
499 | u32 ssb_chipco_irq_status(struct ssb_chipcommon *cc, u32 mask) | |
500 | { | |
501 | return chipco_read32(cc, SSB_CHIPCO_IRQSTAT) & mask; | |
502 | } | |
503 | ||
61e115a5 MB |
504 | u32 ssb_chipco_gpio_in(struct ssb_chipcommon *cc, u32 mask) |
505 | { | |
506 | return chipco_read32(cc, SSB_CHIPCO_GPIOIN) & mask; | |
507 | } | |
c2bcbe65 MB |
508 | |
509 | u32 ssb_chipco_gpio_out(struct ssb_chipcommon *cc, u32 mask, u32 value) | |
510 | { | |
394bc7e3 HM |
511 | unsigned long flags; |
512 | u32 res = 0; | |
513 | ||
514 | spin_lock_irqsave(&cc->gpio_lock, flags); | |
515 | res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOOUT, mask, value); | |
516 | spin_unlock_irqrestore(&cc->gpio_lock, flags); | |
517 | ||
518 | return res; | |
c2bcbe65 | 519 | } |
c2bcbe65 MB |
520 | |
521 | u32 ssb_chipco_gpio_outen(struct ssb_chipcommon *cc, u32 mask, u32 value) | |
522 | { | |
394bc7e3 HM |
523 | unsigned long flags; |
524 | u32 res = 0; | |
525 | ||
526 | spin_lock_irqsave(&cc->gpio_lock, flags); | |
527 | res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOOUTEN, mask, value); | |
528 | spin_unlock_irqrestore(&cc->gpio_lock, flags); | |
529 | ||
530 | return res; | |
c2bcbe65 | 531 | } |
c2bcbe65 MB |
532 | |
533 | u32 ssb_chipco_gpio_control(struct ssb_chipcommon *cc, u32 mask, u32 value) | |
534 | { | |
394bc7e3 HM |
535 | unsigned long flags; |
536 | u32 res = 0; | |
537 | ||
538 | spin_lock_irqsave(&cc->gpio_lock, flags); | |
539 | res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOCTL, mask, value); | |
540 | spin_unlock_irqrestore(&cc->gpio_lock, flags); | |
541 | ||
542 | return res; | |
c2bcbe65 | 543 | } |
85373ee8 | 544 | EXPORT_SYMBOL(ssb_chipco_gpio_control); |
61e115a5 | 545 | |
c2bcbe65 | 546 | u32 ssb_chipco_gpio_intmask(struct ssb_chipcommon *cc, u32 mask, u32 value) |
61e115a5 | 547 | { |
394bc7e3 HM |
548 | unsigned long flags; |
549 | u32 res = 0; | |
550 | ||
551 | spin_lock_irqsave(&cc->gpio_lock, flags); | |
552 | res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOIRQ, mask, value); | |
553 | spin_unlock_irqrestore(&cc->gpio_lock, flags); | |
554 | ||
555 | return res; | |
61e115a5 MB |
556 | } |
557 | ||
c2bcbe65 | 558 | u32 ssb_chipco_gpio_polarity(struct ssb_chipcommon *cc, u32 mask, u32 value) |
61e115a5 | 559 | { |
394bc7e3 HM |
560 | unsigned long flags; |
561 | u32 res = 0; | |
562 | ||
563 | spin_lock_irqsave(&cc->gpio_lock, flags); | |
564 | res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOPOL, mask, value); | |
565 | spin_unlock_irqrestore(&cc->gpio_lock, flags); | |
566 | ||
567 | return res; | |
61e115a5 MB |
568 | } |
569 | ||
da22f22e HM |
570 | u32 ssb_chipco_gpio_pullup(struct ssb_chipcommon *cc, u32 mask, u32 value) |
571 | { | |
394bc7e3 HM |
572 | unsigned long flags; |
573 | u32 res = 0; | |
574 | ||
da22f22e HM |
575 | if (cc->dev->id.revision < 20) |
576 | return 0xffffffff; | |
577 | ||
394bc7e3 HM |
578 | spin_lock_irqsave(&cc->gpio_lock, flags); |
579 | res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOPULLUP, mask, value); | |
580 | spin_unlock_irqrestore(&cc->gpio_lock, flags); | |
581 | ||
582 | return res; | |
da22f22e HM |
583 | } |
584 | ||
585 | u32 ssb_chipco_gpio_pulldown(struct ssb_chipcommon *cc, u32 mask, u32 value) | |
586 | { | |
394bc7e3 HM |
587 | unsigned long flags; |
588 | u32 res = 0; | |
589 | ||
da22f22e HM |
590 | if (cc->dev->id.revision < 20) |
591 | return 0xffffffff; | |
592 | ||
394bc7e3 HM |
593 | spin_lock_irqsave(&cc->gpio_lock, flags); |
594 | res = chipco_write32_masked(cc, SSB_CHIPCO_GPIOPULLDOWN, mask, value); | |
595 | spin_unlock_irqrestore(&cc->gpio_lock, flags); | |
596 | ||
597 | return res; | |
61e115a5 MB |
598 | } |
599 | ||
600 | #ifdef CONFIG_SSB_SERIAL | |
601 | int ssb_chipco_serial_init(struct ssb_chipcommon *cc, | |
602 | struct ssb_serial_port *ports) | |
603 | { | |
604 | struct ssb_bus *bus = cc->dev->bus; | |
605 | int nr_ports = 0; | |
606 | u32 plltype; | |
607 | unsigned int irq; | |
608 | u32 baud_base, div; | |
609 | u32 i, n; | |
58ff70d4 | 610 | unsigned int ccrev = cc->dev->id.revision; |
61e115a5 MB |
611 | |
612 | plltype = (cc->capabilities & SSB_CHIPCO_CAP_PLLT); | |
613 | irq = ssb_mips_irq(cc->dev); | |
614 | ||
615 | if (plltype == SSB_PLLTYPE_1) { | |
616 | /* PLL clock */ | |
617 | baud_base = ssb_calc_clock_rate(plltype, | |
618 | chipco_read32(cc, SSB_CHIPCO_CLOCK_N), | |
619 | chipco_read32(cc, SSB_CHIPCO_CLOCK_M2)); | |
620 | div = 1; | |
621 | } else { | |
58ff70d4 MB |
622 | if (ccrev == 20) { |
623 | /* BCM5354 uses constant 25MHz clock */ | |
624 | baud_base = 25000000; | |
625 | div = 48; | |
626 | /* Set the override bit so we don't divide it */ | |
627 | chipco_write32(cc, SSB_CHIPCO_CORECTL, | |
628 | chipco_read32(cc, SSB_CHIPCO_CORECTL) | |
629 | | SSB_CHIPCO_CORECTL_UARTCLK0); | |
630 | } else if ((ccrev >= 11) && (ccrev != 15)) { | |
f924e1e9 | 631 | baud_base = ssb_chipco_alp_clock(cc); |
61e115a5 | 632 | div = 1; |
58ff70d4 MB |
633 | if (ccrev >= 21) { |
634 | /* Turn off UART clock before switching clocksource. */ | |
635 | chipco_write32(cc, SSB_CHIPCO_CORECTL, | |
636 | chipco_read32(cc, SSB_CHIPCO_CORECTL) | |
637 | & ~SSB_CHIPCO_CORECTL_UARTCLKEN); | |
638 | } | |
61e115a5 MB |
639 | /* Set the override bit so we don't divide it */ |
640 | chipco_write32(cc, SSB_CHIPCO_CORECTL, | |
58ff70d4 MB |
641 | chipco_read32(cc, SSB_CHIPCO_CORECTL) |
642 | | SSB_CHIPCO_CORECTL_UARTCLK0); | |
643 | if (ccrev >= 21) { | |
644 | /* Re-enable the UART clock. */ | |
645 | chipco_write32(cc, SSB_CHIPCO_CORECTL, | |
646 | chipco_read32(cc, SSB_CHIPCO_CORECTL) | |
647 | | SSB_CHIPCO_CORECTL_UARTCLKEN); | |
648 | } | |
649 | } else if (ccrev >= 3) { | |
61e115a5 MB |
650 | /* Internal backplane clock */ |
651 | baud_base = ssb_clockspeed(bus); | |
652 | div = chipco_read32(cc, SSB_CHIPCO_CLKDIV) | |
653 | & SSB_CHIPCO_CLKDIV_UART; | |
654 | } else { | |
655 | /* Fixed internal backplane clock */ | |
656 | baud_base = 88000000; | |
657 | div = 48; | |
658 | } | |
659 | ||
660 | /* Clock source depends on strapping if UartClkOverride is unset */ | |
58ff70d4 | 661 | if ((ccrev > 0) && |
61e115a5 MB |
662 | !(chipco_read32(cc, SSB_CHIPCO_CORECTL) & SSB_CHIPCO_CORECTL_UARTCLK0)) { |
663 | if ((cc->capabilities & SSB_CHIPCO_CAP_UARTCLK) == | |
664 | SSB_CHIPCO_CAP_UARTCLK_INT) { | |
665 | /* Internal divided backplane clock */ | |
666 | baud_base /= div; | |
667 | } else { | |
668 | /* Assume external clock of 1.8432 MHz */ | |
669 | baud_base = 1843200; | |
670 | } | |
671 | } | |
672 | } | |
673 | ||
674 | /* Determine the registers of the UARTs */ | |
675 | n = (cc->capabilities & SSB_CHIPCO_CAP_NRUART); | |
676 | for (i = 0; i < n; i++) { | |
677 | void __iomem *cc_mmio; | |
678 | void __iomem *uart_regs; | |
679 | ||
680 | cc_mmio = cc->dev->bus->mmio + (cc->dev->core_index * SSB_CORE_SIZE); | |
681 | uart_regs = cc_mmio + SSB_CHIPCO_UART0_DATA; | |
682 | /* Offset changed at after rev 0 */ | |
58ff70d4 | 683 | if (ccrev == 0) |
61e115a5 MB |
684 | uart_regs += (i * 8); |
685 | else | |
686 | uart_regs += (i * 256); | |
687 | ||
688 | nr_ports++; | |
689 | ports[i].regs = uart_regs; | |
690 | ports[i].irq = irq; | |
691 | ports[i].baud_base = baud_base; | |
692 | ports[i].reg_shift = 0; | |
693 | } | |
694 | ||
695 | return nr_ports; | |
696 | } | |
697 | #endif /* CONFIG_SSB_SERIAL */ |