2 * Low-Level PCI and SB support for BCM47xx
4 * Copyright 2004, Broadcom Corporation
7 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
8 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
9 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
10 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
21 #include <bcmendian.h>
27 /* Can free sbpci_init() memory after boot */
32 /* Emulated configuration space */
33 static pci_config_regs sb_config_regs
[SB_MAXCORES
];
36 static uint16 pci_ban
[32] = { 0 };
37 static uint pci_banned
= 0;
40 static bool cardbus
= FALSE
;
42 /* Disable PCI host core */
43 static bool pci_disabled
= FALSE
;
46 * Functions for accessing external PCI configuration space
49 /* Assume one-hot slot wiring */
50 #define PCI_SLOT_MAX 16
53 config_cmd(void *sbh
, uint bus
, uint dev
, uint func
, uint off
)
59 /* CardBusMode supports only one device */
60 if (cardbus
&& dev
> 1)
63 coreidx
= sb_coreidx(sbh
);
64 regs
= (sbpciregs_t
*) sb_setcore(sbh
, SB_PCI
, 0);
66 /* Type 0 transaction */
68 /* Skip unwired slots */
69 if (dev
< PCI_SLOT_MAX
) {
70 /* Slide the PCI window to the appropriate slot */
71 W_REG(®s
->sbtopci1
, SBTOPCI_CFG0
| ((1 << (dev
+ 16)) & SBTOPCI1_MASK
));
72 addr
= SB_PCI_CFG
| ((1 << (dev
+ 16)) & ~SBTOPCI1_MASK
) |
73 (func
<< 8) | (off
& ~3);
77 /* Type 1 transaction */
79 W_REG(®s
->sbtopci1
, SBTOPCI_CFG1
);
80 addr
= SB_PCI_CFG
| (bus
<< 16) | (dev
<< 11) | (func
<< 8) | (off
& ~3);
83 sb_setcoreidx(sbh
, coreidx
);
89 extpci_read_config(void *sbh
, uint bus
, uint dev
, uint func
, uint off
, void *buf
, int len
)
91 uint32 addr
, *reg
= NULL
, val
;
95 !(addr
= config_cmd(sbh
, bus
, dev
, func
, off
)) ||
96 !(reg
= (uint32
*) REG_MAP(addr
, len
)) ||
100 val
>>= 8 * (off
& 3);
102 *((uint32
*) buf
) = val
;
104 *((uint16
*) buf
) = (uint16
) val
;
106 *((uint8
*) buf
) = (uint8
) val
;
117 extpci_write_config(void *sbh
, uint bus
, uint dev
, uint func
, uint off
, void *buf
, int len
)
119 uint32 addr
, *reg
= NULL
, val
;
123 !(addr
= config_cmd(sbh
, bus
, dev
, func
, off
)) ||
124 !(reg
= (uint32
*) REG_MAP(addr
, len
)) ||
129 val
= *((uint32
*) buf
);
131 val
&= ~(0xffff << (8 * (off
& 3)));
132 val
|= *((uint16
*) buf
) << (8 * (off
& 3));
133 } else if (len
== 1) {
134 val
&= ~(0xff << (8 * (off
& 3)));
135 val
|= *((uint8
*) buf
) << (8 * (off
& 3));
149 * Functions for accessing translated SB configuration space
153 sb_read_config(void *sbh
, uint bus
, uint dev
, uint func
, uint off
, void *buf
, int len
)
155 pci_config_regs
*cfg
;
157 if (dev
>= SB_MAXCORES
|| (off
+ len
) > sizeof(pci_config_regs
))
159 cfg
= &sb_config_regs
[dev
];
161 ASSERT(ISALIGNED(off
, len
));
162 ASSERT(ISALIGNED(buf
, len
));
165 *((uint32
*) buf
) = ltoh32(*((uint32
*)((ulong
) cfg
+ off
)));
167 *((uint16
*) buf
) = ltoh16(*((uint16
*)((ulong
) cfg
+ off
)));
169 *((uint8
*) buf
) = *((uint8
*)((ulong
) cfg
+ off
));
177 sb_write_config(void *sbh
, uint bus
, uint dev
, uint func
, uint off
, void *buf
, int len
)
182 pci_config_regs
*cfg
;
184 if (dev
>= SB_MAXCORES
|| (off
+ len
) > sizeof(pci_config_regs
))
186 cfg
= &sb_config_regs
[dev
];
188 ASSERT(ISALIGNED(off
, len
));
189 ASSERT(ISALIGNED(buf
, len
));
191 /* Emulate BAR sizing */
192 if (off
>= OFFSETOF(pci_config_regs
, base
[0]) && off
<= OFFSETOF(pci_config_regs
, base
[3]) &&
193 len
== 4 && *((uint32
*) buf
) == ~0) {
194 coreidx
= sb_coreidx(sbh
);
195 if ((regs
= sb_setcoreidx(sbh
, dev
))) {
196 sb
= (sbconfig_t
*)((ulong
) regs
+ SBCONFIGOFF
);
197 /* Highest numbered address match register */
198 n
= (R_REG(&sb
->sbidlow
) & SBIDL_AR_MASK
) >> SBIDL_AR_SHIFT
;
199 if (off
== OFFSETOF(pci_config_regs
, base
[0]))
200 cfg
->base
[0] = ~(sb_size(R_REG(&sb
->sbadmatch0
)) - 1);
201 else if (off
== OFFSETOF(pci_config_regs
, base
[1]) && n
>= 1)
202 cfg
->base
[1] = ~(sb_size(R_REG(&sb
->sbadmatch1
)) - 1);
203 else if (off
== OFFSETOF(pci_config_regs
, base
[2]) && n
>= 2)
204 cfg
->base
[2] = ~(sb_size(R_REG(&sb
->sbadmatch2
)) - 1);
205 else if (off
== OFFSETOF(pci_config_regs
, base
[3]) && n
>= 3)
206 cfg
->base
[3] = ~(sb_size(R_REG(&sb
->sbadmatch3
)) - 1);
208 sb_setcoreidx(sbh
, coreidx
);
213 *((uint32
*)((ulong
) cfg
+ off
)) = htol32(*((uint32
*) buf
));
215 *((uint16
*)((ulong
) cfg
+ off
)) = htol16(*((uint16
*) buf
));
217 *((uint8
*)((ulong
) cfg
+ off
)) = *((uint8
*) buf
);
225 sbpci_read_config(void *sbh
, uint bus
, uint dev
, uint func
, uint off
, void *buf
, int len
)
228 return sb_read_config(sbh
, bus
, dev
, func
, off
, buf
, len
);
230 return extpci_read_config(sbh
, bus
, dev
, func
, off
, buf
, len
);
234 sbpci_write_config(void *sbh
, uint bus
, uint dev
, uint func
, uint off
, void *buf
, int len
)
237 return sb_write_config(sbh
, bus
, dev
, func
, off
, buf
, len
);
239 return extpci_write_config(sbh
, bus
, dev
, func
, off
, buf
, len
);
243 sbpci_ban(uint16 core
)
245 if (pci_banned
< ARRAYSIZE(pci_ban
))
246 pci_ban
[pci_banned
++] = core
;
250 sbpci_init(void *sbh
)
252 uint chip
, chiprev
, chippkg
, coreidx
, host
, i
;
256 pci_config_regs
*cfg
;
261 uint8
class, subclass
, progif
;
263 uint32 sbips_int_mask
[] = { 0, SBIPS_INT1_MASK
, SBIPS_INT2_MASK
, SBIPS_INT3_MASK
, SBIPS_INT4_MASK
};
264 uint32 sbips_int_shift
[] = { 0, 0, SBIPS_INT2_SHIFT
, SBIPS_INT3_SHIFT
, SBIPS_INT4_SHIFT
};
267 chiprev
= sb_chiprev(sbh
);
268 chippkg
= sb_chippkg(sbh
);
269 coreidx
= sb_coreidx(sbh
);
271 if (!(pci
= (sbpciregs_t
*) sb_setcore(sbh
, SB_PCI
, 0)))
273 sb_core_reset(sbh
, 0);
275 boardflags
= (uint32
) getintvar(NULL
, "boardflags");
277 if ((chip
== BCM4310_DEVICE_ID
) && (chiprev
== 0))
281 * The 200-pin BCM4712 package does not bond out PCI. Even when
282 * PCI is bonded out, some boards may leave the pins
285 if (((chip
== BCM4712_DEVICE_ID
) && (chippkg
== BCM4712SMALL_PKG_ID
)) ||
286 (boardflags
& BFL_NOPCI
))
290 * If the PCI core should not be touched (disabled, not bonded
291 * out, or pins floating), do not even attempt to access core
292 * registers. Otherwise, try to determine if it is in host
298 host
= !BUSPROBE(val
, &pci
->control
);
301 /* Disable PCI interrupts in client mode */
302 sb
= (sbconfig_t
*)((ulong
) pci
+ SBCONFIGOFF
);
303 W_REG(&sb
->sbintvec
, 0);
305 /* Disable the PCI bridge in client mode */
307 printf("PCI: Disabled\n");
309 /* Reset the external PCI bus and enable the clock */
310 W_REG(&pci
->control
, 0x5); /* enable the tristate drivers */
311 W_REG(&pci
->control
, 0xd); /* enable the PCI clock */
312 OSL_DELAY(150); /* delay > 100 us */
313 W_REG(&pci
->control
, 0xf); /* deassert PCI reset */
314 W_REG(&pci
->arbcontrol
, PCI_INT_ARB
); /* use internal arbiter */
315 OSL_DELAY(1); /* delay 1 us */
317 /* Enable CardBusMode */
318 cardbus
= nvram_match("cardbus", "1");
320 printf("PCI: Enabling CardBus\n");
321 /* GPIO 1 resets the CardBus device on bcm94710ap */
322 sb_gpioout(sbh
, 1, 1);
323 sb_gpioouten(sbh
, 1, 1);
324 W_REG(&pci
->sprom
[0], R_REG(&pci
->sprom
[0]) | 0x400);
327 /* 64 MB I/O access window */
328 W_REG(&pci
->sbtopci0
, SBTOPCI_IO
);
329 /* 64 MB configuration access window */
330 W_REG(&pci
->sbtopci1
, SBTOPCI_CFG0
);
331 /* 1 GB memory access window */
332 W_REG(&pci
->sbtopci2
, SBTOPCI_MEM
| SB_PCI_DMA
);
334 /* Enable PCI bridge BAR0 prefetch and burst */
336 sbpci_write_config(sbh
, 1, 0, 0, PCI_CFG_CMD
, &val
, sizeof(val
));
338 /* Enable PCI interrupts */
339 W_REG(&pci
->intmask
, PCI_INTA
);
342 /* Scan the SB bus */
343 bzero(sb_config_regs
, sizeof(sb_config_regs
));
344 for (cfg
= sb_config_regs
; cfg
< &sb_config_regs
[SB_MAXCORES
]; cfg
++) {
345 cfg
->vendor
= 0xffff;
346 if (!(regs
= sb_setcoreidx(sbh
, cfg
- sb_config_regs
)))
348 sb
= (sbconfig_t
*)((ulong
) regs
+ SBCONFIGOFF
);
350 /* Read ID register and parse vendor and core */
351 val
= R_REG(&sb
->sbidhigh
);
352 vendor
= (val
& SBIDH_VC_MASK
) >> SBIDH_VC_SHIFT
;
353 core
= (val
& SBIDH_CC_MASK
) >> SBIDH_CC_SHIFT
;
356 /* Check if this core is banned */
357 for (i
= 0; i
< pci_banned
; i
++)
358 if (core
== pci_ban
[i
])
363 /* Known vendor translations */
366 vendor
= VENDOR_BROADCOM
;
370 /* Determine class based on known core codes */
373 class = PCI_CLASS_NET
;
374 subclass
= PCI_NET_ETHER
;
375 core
= BCM47XX_ILINE_ID
;
378 class = PCI_CLASS_NET
;
379 subclass
= PCI_NET_ETHER
;
380 core
= BCM4610_ILINE_ID
;
383 class = PCI_CLASS_NET
;
384 subclass
= PCI_NET_ETHER
;
385 core
= BCM47XX_ENET_ID
;
389 class = PCI_CLASS_MEMORY
;
390 subclass
= PCI_MEMORY_RAM
;
393 class = PCI_CLASS_BRIDGE
;
394 subclass
= PCI_BRIDGE_PCI
;
398 class = PCI_CLASS_CPU
;
399 subclass
= PCI_CPU_MIPS
;
402 class = PCI_CLASS_COMM
;
403 subclass
= PCI_COMM_MODEM
;
404 core
= BCM47XX_V90_ID
;
407 class = PCI_CLASS_SERIAL
;
408 subclass
= PCI_SERIAL_USB
;
409 progif
= 0x10; /* OHCI */
410 core
= BCM47XX_USB_ID
;
413 class = PCI_CLASS_SERIAL
;
414 subclass
= PCI_SERIAL_USB
;
415 progif
= 0x10; /* OHCI */
416 core
= BCM47XX_USBH_ID
;
419 class = PCI_CLASS_SERIAL
;
420 subclass
= PCI_SERIAL_USB
;
421 core
= BCM47XX_USBD_ID
;
424 class = PCI_CLASS_CRYPT
;
425 subclass
= PCI_CRYPT_NETWORK
;
426 core
= BCM47XX_IPSEC_ID
;
430 class = PCI_CLASS_MEMORY
;
431 subclass
= PCI_MEMORY_FLASH
;
434 class = PCI_CLASS_NET
;
435 subclass
= PCI_NET_OTHER
;
436 /* Let an nvram variable override this */
437 sprintf(varname
, "wl%did", wlidx
);
439 if ((core
= getintvar(NULL
, varname
)) == 0) {
440 if (chip
== BCM4712_DEVICE_ID
) {
441 if (chippkg
== BCM4712SMALL_PKG_ID
)
442 core
= BCM4306_D11G_ID
;
444 core
= BCM4306_D11DUAL_ID
;
447 core
= BCM4310_D11B_ID
;
453 class = subclass
= progif
= 0xff;
457 /* Supported translations */
458 cfg
->vendor
= htol16(vendor
);
459 cfg
->device
= htol16(core
);
460 cfg
->rev_id
= chiprev
;
461 cfg
->prog_if
= progif
;
462 cfg
->sub_class
= subclass
;
463 cfg
->base_class
= class;
464 cfg
->base
[0] = htol32(sb_base(R_REG(&sb
->sbadmatch0
)));
465 cfg
->base
[1] = htol32(sb_base(R_REG(&sb
->sbadmatch1
)));
466 cfg
->base
[2] = htol32(sb_base(R_REG(&sb
->sbadmatch2
)));
467 cfg
->base
[3] = htol32(sb_base(R_REG(&sb
->sbadmatch3
)));
470 if (class == PCI_CLASS_BRIDGE
&& subclass
== PCI_BRIDGE_PCI
)
471 cfg
->header_type
= PCI_HEADER_BRIDGE
;
473 cfg
->header_type
= PCI_HEADER_NORMAL
;
474 /* Save core interrupt flag */
475 cfg
->int_pin
= R_REG(&sb
->sbtpsflag
) & SBTPS_NUM0_MASK
;
476 /* Default to MIPS shared interrupt 0 */
478 /* MIPS sbipsflag maps core interrupt flags to interrupts 1 through 4 */
479 if ((regs
= sb_setcore(sbh
, SB_MIPS
, 0)) ||
480 (regs
= sb_setcore(sbh
, SB_MIPS33
, 0))) {
481 sb
= (sbconfig_t
*)((ulong
) regs
+ SBCONFIGOFF
);
482 val
= R_REG(&sb
->sbipsflag
);
483 for (cfg
->int_line
= 1; cfg
->int_line
<= 4; cfg
->int_line
++) {
484 if (((val
& sbips_int_mask
[cfg
->int_line
]) >> sbips_int_shift
[cfg
->int_line
]) == cfg
->int_pin
)
487 if (cfg
->int_line
> 4)
491 *((uint32
*) &cfg
->sprom_control
) = 0xffffffff;
494 sb_setcoreidx(sbh
, coreidx
);
499 sbpci_check(void *sbh
)
504 uint32 buf
[64], *ptr
, i
;
508 coreidx
= sb_coreidx(sbh
);
509 pci
= (sbpciregs_t
*) sb_setcore(sbh
, SB_PCI
, 0);
511 /* Clear the test array */
512 pa
= (ulong
) DMA_MAP(NULL
, buf
, sizeof(buf
), DMA_RX
, NULL
);
513 ptr
= (uint32
*) OSL_UNCACHED(&buf
[0]);
514 memset(ptr
, 0, sizeof(buf
));
516 /* Point PCI window 1 to memory */
517 sbtopci1
= R_REG(&pci
->sbtopci1
);
518 W_REG(&pci
->sbtopci1
, SBTOPCI_MEM
| (pa
& SBTOPCI1_MASK
));
520 /* Fill the test array via PCI window 1 */
521 ptr
= (uint32
*) REG_MAP(SB_PCI_CFG
+ (pa
& ~SBTOPCI1_MASK
), sizeof(buf
));
522 for (i
= 0; i
< ARRAYSIZE(buf
); i
++) {
523 for (j
= 0; j
< 2; j
++);
528 /* Restore PCI window 1 */
529 W_REG(&pci
->sbtopci1
, sbtopci1
);
531 /* Check the test array */
532 DMA_UNMAP(NULL
, pa
, sizeof(buf
), DMA_RX
, NULL
);
533 ptr
= (uint32
*) OSL_UNCACHED(&buf
[0]);
534 for (i
= 0; i
< ARRAYSIZE(buf
); i
++) {
539 /* Change the clock if the test fails */
540 if (i
< ARRAYSIZE(buf
)) {
544 printf("PCI: Test failed at %d MHz\n", (cur
+ 500000) / 1000000);
545 for (req
= 104000000; req
< 176000000; req
+= 4000000) {
546 printf("PCI: Resetting to %d MHz\n", (req
+ 500000) / 1000000);
547 /* This will only reset if the clocks are valid and have changed */
548 sb_mips_setclock(sbh
, req
, 0, 0);
550 /* Should not reach here */
554 sb_setcoreidx(sbh
, coreidx
);