]>
Commit | Line | Data |
---|---|---|
cf2b4488 HP |
1 | /* |
2 | * Copyright (c) 2010 Broadcom Corporation | |
3 | * | |
4 | * Permission to use, copy, modify, and/or distribute this software for any | |
5 | * purpose with or without fee is hereby granted, provided that the above | |
6 | * copyright notice and this permission notice appear in all copies. | |
7 | * | |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | |
11 | * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | |
13 | * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |
14 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 | */ | |
16 | ||
a1c16ed2 | 17 | #include <linux/types.h> |
cf2b4488 | 18 | #include <bcmdefs.h> |
c6ac24e9 BR |
19 | #ifdef BRCM_FULLMAC |
20 | #include <linux/netdevice.h> | |
21 | #endif | |
cf2b4488 HP |
22 | #include <osl.h> |
23 | #include <bcmutils.h> | |
24 | #include <siutils.h> | |
25 | #include <bcmdevs.h> | |
26 | #include <hndsoc.h> | |
27 | #include <sbchipc.h> | |
28 | #include <pci_core.h> | |
29 | #include <pcicfg.h> | |
30 | #include <sbpcmcia.h> | |
31 | #include "siutils_priv.h" | |
32 | ||
33 | /* local prototypes */ | |
66cbd3ab GKH |
34 | static uint _sb_coreidx(si_info_t *sii, u32 sba); |
35 | static uint _sb_scan(si_info_t *sii, u32 sba, void *regs, uint bus, | |
36 | u32 sbba, uint ncores); | |
37 | static u32 _sb_coresba(si_info_t *sii); | |
cf2b4488 HP |
38 | static void *_sb_setcoreidx(si_info_t *sii, uint coreidx); |
39 | ||
40 | #define SET_SBREG(sii, r, mask, val) \ | |
41 | W_SBREG((sii), (r), ((R_SBREG((sii), (r)) & ~(mask)) | (val))) | |
562c8850 | 42 | #define REGS2SB(va) (sbconfig_t *) ((s8 *)(va) + SBCONFIGOFF) |
cf2b4488 HP |
43 | |
44 | /* sonicsrev */ | |
45 | #define SONICS_2_2 (SBIDL_RV_2_2 >> SBIDL_RV_SHIFT) | |
46 | #define SONICS_2_3 (SBIDL_RV_2_3 >> SBIDL_RV_SHIFT) | |
47 | ||
48 | #define R_SBREG(sii, sbr) sb_read_sbreg((sii), (sbr)) | |
49 | #define W_SBREG(sii, sbr, v) sb_write_sbreg((sii), (sbr), (v)) | |
50 | #define AND_SBREG(sii, sbr, v) \ | |
51 | W_SBREG((sii), (sbr), (R_SBREG((sii), (sbr)) & (v))) | |
52 | #define OR_SBREG(sii, sbr, v) \ | |
53 | W_SBREG((sii), (sbr), (R_SBREG((sii), (sbr)) | (v))) | |
54 | ||
66cbd3ab | 55 | static u32 sb_read_sbreg(si_info_t *sii, volatile u32 *sbr) |
cf2b4488 HP |
56 | { |
57 | return R_REG(sii->osh, sbr); | |
58 | } | |
59 | ||
66cbd3ab | 60 | static void sb_write_sbreg(si_info_t *sii, volatile u32 *sbr, u32 v) |
cf2b4488 HP |
61 | { |
62 | W_REG(sii->osh, sbr, v); | |
63 | } | |
64 | ||
65 | uint sb_coreid(si_t *sih) | |
66 | { | |
67 | si_info_t *sii; | |
68 | sbconfig_t *sb; | |
69 | ||
70 | sii = SI_INFO(sih); | |
71 | sb = REGS2SB(sii->curmap); | |
72 | ||
73 | return (R_SBREG(sii, &sb->sbidhigh) & SBIDH_CC_MASK) >> | |
74 | SBIDH_CC_SHIFT; | |
75 | } | |
76 | ||
77 | /* return core index of the core with address 'sba' */ | |
0d2f0724 | 78 | static uint _sb_coreidx(si_info_t *sii, u32 sba) |
cf2b4488 HP |
79 | { |
80 | uint i; | |
81 | ||
82 | for (i = 0; i < sii->numcores; i++) | |
83 | if (sba == sii->coresba[i]) | |
84 | return i; | |
85 | return BADIDX; | |
86 | } | |
87 | ||
88 | /* return core address of the current core */ | |
0d2f0724 | 89 | static u32 _sb_coresba(si_info_t *sii) |
cf2b4488 | 90 | { |
66cbd3ab | 91 | u32 sbaddr = 0; |
cf2b4488 | 92 | |
fa7a1db2 | 93 | switch (sii->pub.bustype) { |
cf2b4488 HP |
94 | case SPI_BUS: |
95 | case SDIO_BUS: | |
f024c48a | 96 | sbaddr = (u32)(unsigned long)sii->curmap; |
cf2b4488 HP |
97 | break; |
98 | default: | |
99 | ASSERT(0); | |
100 | break; | |
101 | } | |
102 | ||
103 | return sbaddr; | |
104 | } | |
105 | ||
106 | uint sb_corerev(si_t *sih) | |
107 | { | |
108 | si_info_t *sii; | |
109 | sbconfig_t *sb; | |
110 | uint sbidh; | |
111 | ||
112 | sii = SI_INFO(sih); | |
113 | sb = REGS2SB(sii->curmap); | |
114 | sbidh = R_SBREG(sii, &sb->sbidhigh); | |
115 | ||
116 | return SBCOREREV(sbidh); | |
117 | } | |
118 | ||
119 | bool sb_iscoreup(si_t *sih) | |
120 | { | |
121 | si_info_t *sii; | |
122 | sbconfig_t *sb; | |
123 | ||
124 | sii = SI_INFO(sih); | |
125 | sb = REGS2SB(sii->curmap); | |
126 | ||
127 | return (R_SBREG(sii, &sb->sbtmstatelow) & | |
128 | (SBTML_RESET | SBTML_REJ_MASK | | |
129 | (SICF_CLOCK_EN << SBTML_SICF_SHIFT))) == | |
130 | (SICF_CLOCK_EN << SBTML_SICF_SHIFT); | |
131 | } | |
132 | ||
133 | /* | |
134 | * Switch to 'coreidx', issue a single arbitrary 32bit | |
135 | * register mask&set operation, | |
136 | * switch back to the original core, and return the new value. | |
137 | * | |
138 | * When using the silicon backplane, no fidleing with interrupts | |
139 | * or core switches are needed. | |
140 | * | |
141 | * Also, when using pci/pcie, we can optimize away the core switching | |
142 | * for pci registers | |
143 | * and (on newer pci cores) chipcommon registers. | |
144 | */ | |
145 | uint sb_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val) | |
146 | { | |
147 | uint origidx = 0; | |
66cbd3ab | 148 | u32 *r = NULL; |
cf2b4488 HP |
149 | uint w; |
150 | uint intr_val = 0; | |
0965ae88 | 151 | bool fast = false; |
cf2b4488 HP |
152 | si_info_t *sii; |
153 | ||
154 | sii = SI_INFO(sih); | |
155 | ||
156 | ASSERT(GOODIDX(coreidx)); | |
157 | ASSERT(regoff < SI_CORE_SIZE); | |
158 | ASSERT((val & ~mask) == 0); | |
159 | ||
160 | if (coreidx >= SI_MAXCORES) | |
161 | return 0; | |
162 | ||
163 | if (!fast) { | |
164 | INTR_OFF(sii, intr_val); | |
165 | ||
166 | /* save current core index */ | |
167 | origidx = si_coreidx(&sii->pub); | |
168 | ||
169 | /* switch core */ | |
66cbd3ab | 170 | r = (u32 *) ((unsigned char *) sb_setcoreidx(&sii->pub, coreidx) + |
cf2b4488 HP |
171 | regoff); |
172 | } | |
173 | ASSERT(r != NULL); | |
174 | ||
175 | /* mask and set */ | |
176 | if (mask || val) { | |
177 | if (regoff >= SBCONFIGOFF) { | |
178 | w = (R_SBREG(sii, r) & ~mask) | val; | |
179 | W_SBREG(sii, r, w); | |
180 | } else { | |
181 | w = (R_REG(sii->osh, r) & ~mask) | val; | |
182 | W_REG(sii->osh, r, w); | |
183 | } | |
184 | } | |
185 | ||
186 | /* readback */ | |
187 | if (regoff >= SBCONFIGOFF) | |
188 | w = R_SBREG(sii, r); | |
189 | else | |
190 | w = R_REG(sii->osh, r); | |
191 | ||
192 | if (!fast) { | |
193 | /* restore core index */ | |
194 | if (origidx != coreidx) | |
195 | sb_setcoreidx(&sii->pub, origidx); | |
196 | ||
197 | INTR_RESTORE(sii, intr_val); | |
198 | } | |
199 | ||
200 | return w; | |
201 | } | |
202 | ||
203 | /* Scan the enumeration space to find all cores starting from the given | |
204 | * bus 'sbba'. Append coreid and other info to the lists in 'si'. 'sba' | |
205 | * is the default core address at chip POR time and 'regs' is the virtual | |
206 | * address that the default core is mapped at. 'ncores' is the number of | |
207 | * cores expected on bus 'sbba'. It returns the total number of cores | |
208 | * starting from bus 'sbba', inclusive. | |
209 | */ | |
210 | #define SB_MAXBUSES 2 | |
0d2f0724 GKH |
211 | static uint _sb_scan(si_info_t *sii, u32 sba, void *regs, uint bus, u32 sbba, |
212 | uint numcores) | |
213 | { | |
cf2b4488 HP |
214 | uint next; |
215 | uint ncc = 0; | |
216 | uint i; | |
217 | ||
218 | if (bus >= SB_MAXBUSES) { | |
219 | SI_ERROR(("_sb_scan: bus 0x%08x at level %d is too deep to " | |
220 | "scan\n", sbba, bus)); | |
221 | return 0; | |
222 | } | |
223 | SI_MSG(("_sb_scan: scan bus 0x%08x assume %u cores\n", | |
224 | sbba, numcores)); | |
225 | ||
226 | /* Scan all cores on the bus starting from core 0. | |
227 | * Core addresses must be contiguous on each bus. | |
228 | */ | |
229 | for (i = 0, next = sii->numcores; | |
230 | i < numcores && next < SB_BUS_MAXCORES; i++, next++) { | |
231 | sii->coresba[next] = sbba + (i * SI_CORE_SIZE); | |
232 | ||
233 | /* change core to 'next' and read its coreid */ | |
234 | sii->curmap = _sb_setcoreidx(sii, next); | |
235 | sii->curidx = next; | |
236 | ||
237 | sii->coreid[next] = sb_coreid(&sii->pub); | |
238 | ||
239 | /* core specific processing... */ | |
240 | /* chipc provides # cores */ | |
241 | if (sii->coreid[next] == CC_CORE_ID) { | |
242 | chipcregs_t *cc = (chipcregs_t *) sii->curmap; | |
66cbd3ab | 243 | u32 ccrev = sb_corerev(&sii->pub); |
cf2b4488 HP |
244 | |
245 | /* determine numcores - this is the | |
246 | total # cores in the chip */ | |
247 | if (((ccrev == 4) || (ccrev >= 6))) | |
248 | numcores = | |
249 | (R_REG(sii->osh, &cc->chipid) & CID_CC_MASK) | |
250 | >> CID_CC_SHIFT; | |
251 | else { | |
252 | /* Older chips */ | |
253 | SI_ERROR(("sb_chip2numcores: unsupported chip " | |
254 | "0x%x\n", CHIPID(sii->pub.chip))); | |
255 | ASSERT(0); | |
256 | numcores = 1; | |
257 | } | |
258 | ||
259 | SI_VMSG(("_sb_scan: %u cores in the chip %s\n", | |
260 | numcores, sii->pub.issim ? "QT" : "")); | |
261 | } | |
262 | /* scan bridged SB(s) and add results to the end of the list */ | |
263 | else if (sii->coreid[next] == OCP_CORE_ID) { | |
264 | sbconfig_t *sb = REGS2SB(sii->curmap); | |
66cbd3ab | 265 | u32 nsbba = R_SBREG(sii, &sb->sbadmatch1); |
cf2b4488 HP |
266 | uint nsbcc; |
267 | ||
268 | sii->numcores = next + 1; | |
269 | ||
270 | if ((nsbba & 0xfff00000) != SI_ENUM_BASE) | |
271 | continue; | |
272 | nsbba &= 0xfffff000; | |
273 | if (_sb_coreidx(sii, nsbba) != BADIDX) | |
274 | continue; | |
275 | ||
276 | nsbcc = | |
277 | (R_SBREG(sii, &sb->sbtmstatehigh) & 0x000f0000) >> | |
278 | 16; | |
279 | nsbcc = _sb_scan(sii, sba, regs, bus + 1, nsbba, nsbcc); | |
280 | if (sbba == SI_ENUM_BASE) | |
281 | numcores -= nsbcc; | |
282 | ncc += nsbcc; | |
283 | } | |
284 | } | |
285 | ||
286 | SI_MSG(("_sb_scan: found %u cores on bus 0x%08x\n", i, sbba)); | |
287 | ||
288 | sii->numcores = i + ncc; | |
289 | return sii->numcores; | |
290 | } | |
291 | ||
292 | /* scan the sb enumerated space to identify all cores */ | |
0d2f0724 | 293 | void sb_scan(si_t *sih, void *regs, uint devid) |
cf2b4488 HP |
294 | { |
295 | si_info_t *sii; | |
66cbd3ab | 296 | u32 origsba; |
cf2b4488 HP |
297 | sbconfig_t *sb; |
298 | ||
299 | sii = SI_INFO(sih); | |
300 | sb = REGS2SB(sii->curmap); | |
301 | ||
302 | sii->pub.socirev = | |
303 | (R_SBREG(sii, &sb->sbidlow) & SBIDL_RV_MASK) >> SBIDL_RV_SHIFT; | |
304 | ||
305 | /* Save the current core info and validate it later till we know | |
306 | * for sure what is good and what is bad. | |
307 | */ | |
308 | origsba = _sb_coresba(sii); | |
309 | ||
310 | /* scan all SB(s) starting from SI_ENUM_BASE */ | |
311 | sii->numcores = _sb_scan(sii, origsba, regs, 0, SI_ENUM_BASE, 1); | |
312 | } | |
313 | ||
314 | /* | |
315 | * This function changes logical "focus" to the indicated core; | |
316 | * must be called with interrupts off. | |
317 | * Moreover, callers should keep interrupts off during switching out of | |
318 | * and back to d11 core | |
319 | */ | |
320 | void *sb_setcoreidx(si_t *sih, uint coreidx) | |
321 | { | |
322 | si_info_t *sii; | |
323 | ||
324 | sii = SI_INFO(sih); | |
325 | ||
326 | if (coreidx >= sii->numcores) | |
327 | return NULL; | |
328 | ||
329 | /* | |
330 | * If the user has provided an interrupt mask enabled function, | |
331 | * then assert interrupts are disabled before switching the core. | |
332 | */ | |
333 | ASSERT((sii->intrsenabled_fn == NULL) | |
334 | || !(*(sii)->intrsenabled_fn) ((sii)->intr_arg)); | |
335 | ||
336 | sii->curmap = _sb_setcoreidx(sii, coreidx); | |
337 | sii->curidx = coreidx; | |
338 | ||
339 | return sii->curmap; | |
340 | } | |
341 | ||
342 | /* This function changes the logical "focus" to the indicated core. | |
343 | * Return the current core's virtual address. | |
344 | */ | |
345 | static void *_sb_setcoreidx(si_info_t *sii, uint coreidx) | |
346 | { | |
66cbd3ab | 347 | u32 sbaddr = sii->coresba[coreidx]; |
cf2b4488 HP |
348 | void *regs; |
349 | ||
fa7a1db2 | 350 | switch (sii->pub.bustype) { |
cf2b4488 HP |
351 | #ifdef BCMSDIO |
352 | case SPI_BUS: | |
353 | case SDIO_BUS: | |
354 | /* map new one */ | |
355 | if (!sii->regs[coreidx]) { | |
c03b63c1 | 356 | sii->regs[coreidx] = (void *)sbaddr; |
cf2b4488 HP |
357 | ASSERT(GOODREGS(sii->regs[coreidx])); |
358 | } | |
359 | regs = sii->regs[coreidx]; | |
360 | break; | |
361 | #endif /* BCMSDIO */ | |
362 | default: | |
363 | ASSERT(0); | |
364 | regs = NULL; | |
365 | break; | |
366 | } | |
367 | ||
368 | return regs; | |
369 | } | |
370 | ||
371 | /* traverse all cores to find and clear source of serror */ | |
372 | static void sb_serr_clear(si_info_t *sii) | |
373 | { | |
374 | sbconfig_t *sb; | |
375 | uint origidx; | |
376 | uint i, intr_val = 0; | |
377 | void *corereg = NULL; | |
378 | ||
379 | INTR_OFF(sii, intr_val); | |
380 | origidx = si_coreidx(&sii->pub); | |
381 | ||
382 | for (i = 0; i < sii->numcores; i++) { | |
383 | corereg = sb_setcoreidx(&sii->pub, i); | |
384 | if (NULL != corereg) { | |
385 | sb = REGS2SB(corereg); | |
386 | if ((R_SBREG(sii, &sb->sbtmstatehigh)) & SBTMH_SERR) { | |
387 | AND_SBREG(sii, &sb->sbtmstatehigh, ~SBTMH_SERR); | |
388 | SI_ERROR(("sb_serr_clear: SError core 0x%x\n", | |
389 | sb_coreid(&sii->pub))); | |
390 | } | |
391 | } | |
392 | } | |
393 | ||
394 | sb_setcoreidx(&sii->pub, origidx); | |
395 | INTR_RESTORE(sii, intr_val); | |
396 | } | |
397 | ||
398 | /* | |
399 | * Check if any inband, outband or timeout errors has happened and clear them. | |
400 | * Must be called with chip clk on ! | |
401 | */ | |
402 | bool sb_taclear(si_t *sih, bool details) | |
403 | { | |
404 | si_info_t *sii; | |
405 | sbconfig_t *sb; | |
406 | uint origidx; | |
407 | uint intr_val = 0; | |
0965ae88 | 408 | bool rc = false; |
66cbd3ab | 409 | u32 inband = 0, serror = 0, timeout = 0; |
cf2b4488 | 410 | void *corereg = NULL; |
66cbd3ab | 411 | volatile u32 imstate, tmstate; |
cf2b4488 HP |
412 | |
413 | sii = SI_INFO(sih); | |
414 | ||
fa7a1db2 BR |
415 | if ((sii->pub.bustype == SDIO_BUS) || |
416 | (sii->pub.bustype == SPI_BUS)) { | |
cf2b4488 HP |
417 | |
418 | INTR_OFF(sii, intr_val); | |
419 | origidx = si_coreidx(sih); | |
420 | ||
421 | corereg = si_setcore(sih, PCMCIA_CORE_ID, 0); | |
422 | if (NULL == corereg) | |
423 | corereg = si_setcore(sih, SDIOD_CORE_ID, 0); | |
424 | if (NULL != corereg) { | |
425 | sb = REGS2SB(corereg); | |
426 | ||
427 | imstate = R_SBREG(sii, &sb->sbimstate); | |
428 | if ((imstate != 0xffffffff) | |
429 | && (imstate & (SBIM_IBE | SBIM_TO))) { | |
430 | AND_SBREG(sii, &sb->sbimstate, | |
431 | ~(SBIM_IBE | SBIM_TO)); | |
432 | /* inband = imstate & SBIM_IBE; cmd error */ | |
433 | timeout = imstate & SBIM_TO; | |
434 | } | |
435 | tmstate = R_SBREG(sii, &sb->sbtmstatehigh); | |
436 | if ((tmstate != 0xffffffff) | |
437 | && (tmstate & SBTMH_INT_STATUS)) { | |
438 | sb_serr_clear(sii); | |
439 | serror = 1; | |
440 | OR_SBREG(sii, &sb->sbtmstatelow, SBTML_INT_ACK); | |
441 | AND_SBREG(sii, &sb->sbtmstatelow, | |
442 | ~SBTML_INT_ACK); | |
443 | } | |
444 | } | |
445 | ||
446 | sb_setcoreidx(sih, origidx); | |
447 | INTR_RESTORE(sii, intr_val); | |
448 | } | |
449 | ||
450 | if (inband | timeout | serror) { | |
0f0881b0 | 451 | rc = true; |
cf2b4488 HP |
452 | SI_ERROR(("sb_taclear: inband 0x%x, serror 0x%x, timeout " |
453 | "0x%x!\n", inband, serror, timeout)); | |
454 | } | |
455 | ||
456 | return rc; | |
457 | } | |
458 | ||
66cbd3ab | 459 | void sb_core_disable(si_t *sih, u32 bits) |
cf2b4488 HP |
460 | { |
461 | si_info_t *sii; | |
66cbd3ab | 462 | volatile u32 dummy; |
cf2b4488 HP |
463 | sbconfig_t *sb; |
464 | ||
465 | sii = SI_INFO(sih); | |
466 | ||
467 | ASSERT(GOODREGS(sii->curmap)); | |
468 | sb = REGS2SB(sii->curmap); | |
469 | ||
470 | /* if core is already in reset, just return */ | |
471 | if (R_SBREG(sii, &sb->sbtmstatelow) & SBTML_RESET) | |
472 | return; | |
473 | ||
474 | /* if clocks are not enabled, put into reset and return */ | |
475 | if ((R_SBREG(sii, &sb->sbtmstatelow) & | |
476 | (SICF_CLOCK_EN << SBTML_SICF_SHIFT)) == 0) | |
477 | goto disable; | |
478 | ||
479 | /* set target reject and spin until busy is clear | |
480 | (preserve core-specific bits) */ | |
481 | OR_SBREG(sii, &sb->sbtmstatelow, SBTML_REJ); | |
482 | dummy = R_SBREG(sii, &sb->sbtmstatelow); | |
7383141b | 483 | udelay(1); |
cf2b4488 HP |
484 | SPINWAIT((R_SBREG(sii, &sb->sbtmstatehigh) & SBTMH_BUSY), 100000); |
485 | if (R_SBREG(sii, &sb->sbtmstatehigh) & SBTMH_BUSY) | |
486 | SI_ERROR(("%s: target state still busy\n", __func__)); | |
487 | ||
488 | if (R_SBREG(sii, &sb->sbidlow) & SBIDL_INIT) { | |
489 | OR_SBREG(sii, &sb->sbimstate, SBIM_RJ); | |
490 | dummy = R_SBREG(sii, &sb->sbimstate); | |
7383141b | 491 | udelay(1); |
cf2b4488 HP |
492 | SPINWAIT((R_SBREG(sii, &sb->sbimstate) & SBIM_BY), 100000); |
493 | } | |
494 | ||
495 | /* set reset and reject while enabling the clocks */ | |
496 | W_SBREG(sii, &sb->sbtmstatelow, | |
497 | (((bits | SICF_FGC | SICF_CLOCK_EN) << SBTML_SICF_SHIFT) | | |
498 | SBTML_REJ | SBTML_RESET)); | |
499 | dummy = R_SBREG(sii, &sb->sbtmstatelow); | |
7383141b | 500 | udelay(10); |
cf2b4488 HP |
501 | |
502 | /* don't forget to clear the initiator reject bit */ | |
503 | if (R_SBREG(sii, &sb->sbidlow) & SBIDL_INIT) | |
504 | AND_SBREG(sii, &sb->sbimstate, ~SBIM_RJ); | |
505 | ||
506 | disable: | |
507 | /* leave reset and reject asserted */ | |
508 | W_SBREG(sii, &sb->sbtmstatelow, | |
509 | ((bits << SBTML_SICF_SHIFT) | SBTML_REJ | SBTML_RESET)); | |
7383141b | 510 | udelay(1); |
cf2b4488 HP |
511 | } |
512 | ||
513 | /* reset and re-enable a core | |
514 | * inputs: | |
515 | * bits - core specific bits that are set during and after reset sequence | |
516 | * resetbits - core specific bits that are set only during reset sequence | |
517 | */ | |
66cbd3ab | 518 | void sb_core_reset(si_t *sih, u32 bits, u32 resetbits) |
cf2b4488 HP |
519 | { |
520 | si_info_t *sii; | |
521 | sbconfig_t *sb; | |
66cbd3ab | 522 | volatile u32 dummy; |
cf2b4488 HP |
523 | |
524 | sii = SI_INFO(sih); | |
525 | ASSERT(GOODREGS(sii->curmap)); | |
526 | sb = REGS2SB(sii->curmap); | |
527 | ||
528 | /* | |
529 | * Must do the disable sequence first to work for | |
530 | * arbitrary current core state. | |
531 | */ | |
532 | sb_core_disable(sih, (bits | resetbits)); | |
533 | ||
534 | /* | |
535 | * Now do the initialization sequence. | |
536 | */ | |
537 | ||
538 | /* set reset while enabling the clock and | |
539 | forcing them on throughout the core */ | |
540 | W_SBREG(sii, &sb->sbtmstatelow, | |
541 | (((bits | resetbits | SICF_FGC | SICF_CLOCK_EN) << | |
542 | SBTML_SICF_SHIFT) | SBTML_RESET)); | |
543 | dummy = R_SBREG(sii, &sb->sbtmstatelow); | |
7383141b | 544 | udelay(1); |
cf2b4488 HP |
545 | |
546 | if (R_SBREG(sii, &sb->sbtmstatehigh) & SBTMH_SERR) | |
547 | W_SBREG(sii, &sb->sbtmstatehigh, 0); | |
548 | ||
eb7f37b3 JC |
549 | dummy = R_SBREG(sii, &sb->sbimstate); |
550 | if (dummy & (SBIM_IBE | SBIM_TO)) | |
cf2b4488 HP |
551 | AND_SBREG(sii, &sb->sbimstate, ~(SBIM_IBE | SBIM_TO)); |
552 | ||
553 | /* clear reset and allow it to propagate throughout the core */ | |
554 | W_SBREG(sii, &sb->sbtmstatelow, | |
555 | ((bits | resetbits | SICF_FGC | SICF_CLOCK_EN) << | |
556 | SBTML_SICF_SHIFT)); | |
557 | dummy = R_SBREG(sii, &sb->sbtmstatelow); | |
7383141b | 558 | udelay(1); |
cf2b4488 HP |
559 | |
560 | /* leave clock enabled */ | |
561 | W_SBREG(sii, &sb->sbtmstatelow, | |
562 | ((bits | SICF_CLOCK_EN) << SBTML_SICF_SHIFT)); | |
563 | dummy = R_SBREG(sii, &sb->sbtmstatelow); | |
7383141b | 564 | udelay(1); |
cf2b4488 HP |
565 | } |
566 | ||
66cbd3ab | 567 | u32 sb_base(u32 admatch) |
cf2b4488 | 568 | { |
66cbd3ab | 569 | u32 base; |
cf2b4488 HP |
570 | uint type; |
571 | ||
572 | type = admatch & SBAM_TYPE_MASK; | |
573 | ASSERT(type < 3); | |
574 | ||
575 | base = 0; | |
576 | ||
577 | if (type == 0) { | |
578 | base = admatch & SBAM_BASE0_MASK; | |
579 | } else if (type == 1) { | |
580 | ASSERT(!(admatch & SBAM_ADNEG)); /* neg not supported */ | |
581 | base = admatch & SBAM_BASE1_MASK; | |
582 | } else if (type == 2) { | |
583 | ASSERT(!(admatch & SBAM_ADNEG)); /* neg not supported */ | |
584 | base = admatch & SBAM_BASE2_MASK; | |
585 | } | |
586 | ||
587 | return base; | |
588 | } |