#include <sys/param.h>
#include <sys/device.h>
#include <net/if.h>
#include <net/if_ether.h>
#define _SPARC_BUS_DMA_PRIVATE
#include <sys/bus.h>
#include <machine/autoconf.h>
#include <machine/promlib.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcidevs.h>
#include <dev/ofw/ofw_pci.h>
#include <sparc/sparc/msiiepreg.h>
#include <sparc/sparc/msiiepvar.h>
#include <sparc/sparc/pci_fixup.h>
static void mspcic_pci_fixup(int, pcitag_t, int *, uint32_t *, uint32_t *,
uint32_t, uint32_t memtop);
#define MAX_DEVFUN 256
#define DF_NEXTDEV(i) (i + 7 - (i % 8))
#define MAP_TOP(map) (map.pcibase + map.size)
#define RND_IO_START(t, m) (((t & m) == t) ? t : \
((t + PCI_MAPREG_IO_SIZE(m)) & m))
#define RND_MEM_START(t, m) (((t & m) == t) ? t : \
((t + PCI_MAPREG_MEM_SIZE(m)) & m))
void
mspcic_pci_scan(int root)
{
int i, j, node, bus, dev, fun, maxbus, len, class;
struct ofw_pci_register reg;
pcitag_t tag;
pcireg_t val, saved;
uint32_t io[IOMAP_SIZE], mem[MEMMAP_SIZE];
#ifdef SPARC_PCI_FIXUP_DEBUG
char name[80];
memset(name, 0, sizeof(name));
#endif
maxbus = 1;
for (i = 0; i < IOMAP_SIZE; i++)
io[i] = mspcic_pci_iomap[i].pcibase;
for (i = 0; i < MEMMAP_SIZE; i++)
mem[i] = mspcic_pci_memmap[i].pcibase;
node = OF_child(root);
#ifdef SPARC_PCI_FIXUP_DEBUG
printf("mspcic_pci_scan start:\n");
printf(" max bus %d\n", maxbus);
for (i = 0; i < IOMAP_SIZE; i++)
printf(" PCI I/O %d %08x to %08x\n",
i, io[i], MAP_TOP(mspcic_pci_iomap[i]) - 1);
for (i = 0; i < MEMMAP_SIZE; i++)
printf(" PCI Mem %d %08x to %08x\n",
i, mem[i], MAP_TOP(mspcic_pci_memmap[i]) - 1);
#endif
while(node) {
uint32_t busrange[2];
#ifdef SPARC_PCI_FIXUP_DEBUG
OF_getprop(node, "name", &name, sizeof(name));
printf("> checking node %x: %s\n", node, name);
#endif
len = OF_getproplen(node, "reg");
if (len < sizeof(reg))
continue;
if (OF_getprop(node, "reg", (void *)®, sizeof(reg)) != len)
panic("pci_probe_bus: OF_getprop len botch");
bus = OFW_PCI_PHYS_HI_BUS(reg.phys_hi);
dev = OFW_PCI_PHYS_HI_DEVICE(reg.phys_hi);
fun = OFW_PCI_PHYS_HI_FUNCTION(reg.phys_hi);
tag = PCITAG_CREATE(node, bus, dev, fun);
#ifdef SPARC_PCI_FIXUP_DEBUG
printf("> bus %2d, dev %2d, fun %2d\n",
PCITAG_BUS(tag), PCITAG_DEV(tag), PCITAG_FUN(tag));
#endif
pci_conf_write(NULL, tag, PCI_COMMAND_STATUS_REG,
PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE |
PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_PARITY_ENABLE);
if ((OF_getprop(node, "bus-range", (void *)&busrange,
sizeof(busrange)) == sizeof(busrange)))
{
if (busrange[1] > maxbus)
maxbus = busrange[1] + 1;
if (maxbus > 255)
panic("mspcic_pci_scan: maxbus > 255");
node = OF_child(node);
continue;
}
if (OF_peer(node))
node = OF_peer(node);
else if (OF_parent(node) != root) {
node = OF_parent(node);
node = OF_peer(node);
} else
node = 0;
val = pci_conf_read(NULL, tag, PCI_BHLC_REG);
if (PCI_HDRTYPE_TYPE(val))
continue;
for (i = 0; i < 6; i++) {
saved = pci_conf_read(NULL, tag, PCI_BAR(i));
pci_conf_write(NULL, tag, PCI_BAR(i), (pcireg_t) ~0x0);
val = pci_conf_read(NULL, tag, PCI_BAR(i));
pci_conf_write(NULL, tag, PCI_BAR(i), saved);
if (!val)
continue;
saved &= 0xfffffffe;
#ifdef SPARC_PCI_FIXUP_DEBUG
printf("> BAR %02x: value %08x mask %08x\n",
PCI_BAR(i), saved, val);
#endif
if (PCI_MAPREG_TYPE(val) == PCI_MAPREG_TYPE_IO) {
saved = saved + PCI_MAPREG_IO_SIZE(val);
for (j = 0; j < IOMAP_SIZE; j++)
if (saved > io[j] && saved <=
MAP_TOP(mspcic_pci_iomap[j]))
io[j] = saved;
} else {
saved = saved + PCI_MAPREG_MEM_SIZE(val);
for (j = 0; j < MEMMAP_SIZE; j++)
if (saved > mem[j] && saved <=
MAP_TOP(mspcic_pci_memmap[j]))
mem[j] = saved;
}
}
saved = pci_conf_read(NULL, tag, PCI_MAPREG_ROM);
pci_conf_write(NULL, tag, PCI_MAPREG_ROM, (pcireg_t) ~0x0);
val = pci_conf_read(NULL, tag, PCI_MAPREG_ROM);
pci_conf_write(NULL, tag, PCI_MAPREG_ROM, saved);
if (val) {
#ifdef SPARC_PCI_FIXUP_DEBUG
printf("> ROM: start %08x mask %08x\n", saved, val);
#endif
saved = saved + PCI_MAPREG_MEM_SIZE(val);
for (j = 0; j < MEMMAP_SIZE; j++)
if (saved > mem[j] && saved <=
MAP_TOP(mspcic_pci_memmap[j]))
mem[j] = saved;
}
}
#ifdef SPARC_PCI_FIXUP_DEBUG
printf("mspcic_pci_scan finish:\n");
printf(" max bus %d\n", maxbus);
for (i = 0; i < IOMAP_SIZE; i++)
if (io[i] < MAP_TOP(mspcic_pci_iomap[i]) - 1)
printf(" PCI I/O free %d %08x to %08x\n",
i, io[i], MAP_TOP(mspcic_pci_iomap[i]) - 1);
else
printf(" PCI I/O %d full %08x to %08x\n",
i, mspcic_pci_iomap[i].sysbase,
MAP_TOP(mspcic_pci_iomap[i]) - 1);
for (i = 0; i < MEMMAP_SIZE; i++)
if (mem[i] < MAP_TOP(mspcic_pci_memmap[i]) - 1)
printf(" PCI Mem free %d %08x to %08x\n",
i, mem[i], MAP_TOP(mspcic_pci_memmap[i]) - 1);
else
printf(" PCI %d Mem full %08x to %08x\n",
i, mspcic_pci_memmap[i].sysbase,
MAP_TOP(mspcic_pci_memmap[i]) - 1);
#endif
node = OF_child(root);
while(node) {
int next, k;
if (OF_peer(node))
next = OF_peer(node);
else if (OF_parent(node) != root) {
next = OF_parent(node);
next = OF_peer(node);
} else
next = 0;
len = OF_getproplen(node, "class-code");
if (!len) {
node = next;
continue;
}
OF_getprop(node, "class-code", &class, len);
if (!IS_PCI_BRIDGE(class)) {
node = next;
continue;
}
len = OF_getproplen(node, "reg");
if (len < sizeof(reg))
continue;
if (OF_getprop(node, "reg", (void *)®, sizeof(reg)) != len)
panic("pci_probe_bus: OF_getprop len botch");
bus = OFW_PCI_PHYS_HI_BUS(reg.phys_hi);
dev = OFW_PCI_PHYS_HI_DEVICE(reg.phys_hi);
fun = OFW_PCI_PHYS_HI_FUNCTION(reg.phys_hi);
tag = PCITAG_CREATE(node, bus, dev, fun);
i = 0;
j = 0;
for (k = 1; k < IOMAP_SIZE; k++) {
io[k] = RND_IO_START(io[k], 0xf000);
if (MAP_TOP(mspcic_pci_iomap[k]) - io[k] >
MAP_TOP(mspcic_pci_iomap[i]) - io[i])
i = k;
}
for (k = 1; k < MEMMAP_SIZE; k++) {
mem[k] = RND_MEM_START(mem[k], 0xfff00000);
if (MAP_TOP(mspcic_pci_memmap[k]) - mem[k] >
MAP_TOP(mspcic_pci_memmap[j]) - mem[j])
j = k;
}
mspcic_pci_fixup(1, tag, &maxbus, &io[i], &mem[j],
MAP_TOP(mspcic_pci_iomap[i]),
MAP_TOP(mspcic_pci_memmap[j]));
node = next;
}
}
static void
mspcic_pci_fixup(int depth, pcitag_t starttag, int *maxbus, uint32_t *io,
uint32_t *mem, uint32_t iotop, uint32_t memtop)
{
int i, j, startbus;
uint32_t startio, startmem;
pcitag_t tag;
pcireg_t val, size, start;
startbus = *maxbus;
startio = *io;
startmem = *mem;
#ifdef SPARC_PCI_FIXUP_DEBUG
printf("mspcic_pci_fixup start:\n");
printf(" bridge at (%d %d %d), depth %d\n", PCITAG_BUS(starttag),
PCITAG_DEV(starttag), PCITAG_FUN(starttag), depth);
printf(" start bus %d\n", startbus);
printf(" io free %08x to %08x\n", startio, iotop - 1);
printf(" mem free %08x to %08x\n", startmem, memtop - 1);
#endif
pci_conf_write(NULL, starttag, PCI_COMMAND_STATUS_REG,
PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE
| PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_PARITY_ENABLE);
pci_conf_write(NULL, starttag, PCI_BRIDGE_CONTROL_REG, 0);
pci_conf_write(NULL, starttag, PCI_BRIDGE_BUS_REG,
__SHIFTIN(startbus & 0xff, PCI_BRIDGE_BUS_SECONDARY) |
__SHIFTIN(0xff, PCI_BRIDGE_BUS_SUBORDINATE));
for (i = 0; i < MAX_DEVFUN; i++) {
tag = PCITAG_CREATE(0, startbus, i / 8, i % 8);
pci_conf_write(NULL, tag, PCI_COMMAND_STATUS_REG,
PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_MEM_ENABLE
| PCI_COMMAND_IO_ENABLE | PCI_COMMAND_PARITY_ENABLE);
val = pci_conf_read(NULL, tag, PCI_ID_REG);
if (PCI_VENDOR(val) == PCI_VENDOR_INVALID) {
i = DF_NEXTDEV(i);
continue;
}
#ifdef SPARC_PCI_FIXUP_DEBUG
printf("> Found %04x:%04x at (%d %d %d)\n", PCI_VENDOR(val),
PCI_PRODUCT(val), startbus, i / 8, i % 8);
#endif
val = pci_conf_read(NULL, tag, PCI_INTERRUPT_REG);
if (PCI_INTERRUPT_PIN(val)) {
val = (val & ~(PCI_INTERRUPT_LINE_MASK <<
PCI_INTERRUPT_LINE_SHIFT)) | depth;
pci_conf_write(NULL, tag, PCI_INTERRUPT_REG, val);
}
val = pci_conf_read(NULL, tag, PCI_CLASS_REG);
if (IS_PCI_BRIDGE(val)) {
(*maxbus)++;
if (*maxbus > 255)
panic("mspcic_pci_fixup: maxbus > 255");
mspcic_pci_fixup(depth + 1, tag, maxbus, io, mem,
iotop, memtop);
}
pci_conf_write(NULL, tag, PCI_MAPREG_ROM, (pcireg_t) ~0x0);
val = pci_conf_read(NULL, tag, PCI_MAPREG_ROM);
if (val) {
size = PCI_MAPREG_MEM_SIZE(val);
start = RND_MEM_START(*mem, val);
if (start + size <= memtop) {
*mem = start + size;
pci_conf_write(NULL, tag, PCI_MAPREG_ROM,
start);
#ifdef SPARC_PCI_FIXUP_DEBUG
printf("> ROM: %08x to %08x mask %08x\n",
start, (*mem) - 1, val);
#endif
}
}
val = pci_conf_read(NULL, tag, PCI_BHLC_REG);
if (!PCI_HDRTYPE_MULTIFN(val))
i = DF_NEXTDEV(i);
if (PCI_HDRTYPE_TYPE(val))
continue;
for (j = 0; j < 6; j++) {
pci_conf_write(NULL, tag, PCI_BAR(j),
(pcireg_t) ~0x0);
val = pci_conf_read(NULL, tag, PCI_BAR(j));
if (!val)
continue;
if (PCI_MAPREG_TYPE(val) ==
PCI_MAPREG_TYPE_IO) {
size = PCI_MAPREG_IO_SIZE(val);
start = RND_IO_START(*io, val);
if (start + size <= iotop) {
*io = start + size;
pci_conf_write(NULL, tag, PCI_BAR(j),
start);
#ifdef SPARC_PCI_FIXUP_DEBUG
printf("> BAR %02x set: %08x to %08x "
"mask %08x (io)\n", PCI_BAR(j),
start, (*io) - 1, val);
#endif
} else {
pci_conf_write(NULL, tag,
PCI_COMMAND_STATUS_REG, 0);
printf("Fixup failed for (%d %d %d)\n",
startbus, i / 8, i % 8);
}
} else {
size = PCI_MAPREG_MEM_SIZE(val);
start = RND_MEM_START(*mem, val);
if (start + size <= memtop) {
*mem = start + size;
pci_conf_write(NULL, tag, PCI_BAR(j),
start);
#ifdef SPARC_PCI_FIXUP_DEBUG
printf("> BAR %02x set: %08x to %08x "
"mask %08x (mem)\n", PCI_BAR(j),
start, (*mem) - 1, val);
#endif
} else {
pci_conf_write(NULL, tag,
PCI_COMMAND_STATUS_REG, 0);
printf("Fixup failed for (%d %d %d)\n",
startbus, i / 8, i % 8);
}
}
}
}
pci_conf_write(NULL, starttag, PCI_BRIDGE_BUS_REG,
__SHIFTIN(startbus & 0xff, PCI_BRIDGE_BUS_SECONDARY) |
__SHIFTIN(*maxbus & 0xff, PCI_BRIDGE_BUS_SUBORDINATE));
val = ((startio & 0xf000) >> 8) | ((*(io) - 1) & 0xf000);
pci_conf_write(NULL, starttag, PCI_BRIDGE_STATIO_REG, val);
#ifdef SPARC_PCI_FIXUP_DEBUG
printf("16-bit I/O range = %04x\n",
pci_conf_read(NULL, starttag, PCI_BRIDGE_STATIO_REG) & 0xffff);
#endif
val = ((startmem & 0xfff00000) >> 16) |
((*(mem) - 1) & 0xfff00000);
pci_conf_write(NULL, starttag, PCI_BRIDGE_MEMORY_REG, val);
pci_conf_write(NULL, starttag, PCI_BRIDGE_PREFETCHMEM_REG, 0x0000ffff);
#ifdef SPARC_PCI_FIXUP_DEBUG
printf("Mem range = %08x\n",
pci_conf_read(NULL, starttag, PCI_BRIDGE_MEMORY_REG));
printf("Pref mem range = %08x\n",
pci_conf_read(NULL, starttag, PCI_BRIDGE_PREFETCHMEM_REG));
#endif
val = pci_conf_read(NULL, starttag, PCI_BRIDGE_STATIO_REG);
if ((val & 0x0101) == 0x0101) {
val = ((startio & 0xffff0000) >> 16) |
((*(io) - 1) & 0xffff0000);
pci_conf_write(NULL, starttag, PCI_BRIDGE_IOHIGH_REG, val);
}
#ifdef SPARC_PCI_FIXUP_DEBUG
printf("32-bit I/O range = %08x\n",
pci_conf_read(NULL, starttag, PCI_BRIDGE_IOHIGH_REG));
#endif
val = pci_conf_read(NULL, starttag, PCI_BRIDGE_PREFETCHMEM_REG);
if (val & 0x01) {
pci_conf_write(NULL, starttag,
PCI_BRIDGE_PREFETCHBASEUP32_REG, (pcireg_t) ~0);
pci_conf_write(NULL, starttag,
PCI_BRIDGE_PREFETCHLIMITUP32_REG, (pcireg_t) 0);
}
#ifdef SPARC_PCI_FIXUP_DEBUG
printf("mspcic_pci_fixup finish:\n");
printf(" bridge at (%d %d %d), depth %d\n", PCITAG_BUS(starttag),
PCITAG_DEV(starttag), PCITAG_FUN(starttag), depth);
printf(" bus range %d to %d\n", startbus, *maxbus);
printf(" io used %08x to %08x\n", startio, *(io) - 1);
printf(" mem used %08x to %08x\n", startmem, *(mem) - 1);
#endif
}