#include <sys/param.h>
#include <sys/systm.h>
#include <machine/bus.h>
#include <machine/autoconf.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_pci.h>
struct powerpc_bus_dma_tag pci_bus_dma_tag = {
NULL,
_dmamap_create,
_dmamap_destroy,
_dmamap_load,
_dmamap_load_mbuf,
_dmamap_load_uio,
_dmamap_load_raw,
_dmamap_unload,
_dmamap_sync,
_dmamem_alloc,
_dmamem_alloc_range,
_dmamem_free,
_dmamem_map,
_dmamem_unmap,
_dmamem_mmap
};
void
pci_attach_hook(struct device *parent, struct device *self,
struct pcibus_attach_args *pba)
{
}
int
pci_bus_maxdevs(pci_chipset_tag_t pc, int busno)
{
return (32);
}
pcitag_t
pci_make_tag(pci_chipset_tag_t pc, int b, int d, int f)
{
struct ofw_pci_register reg;
pcitag_t tag;
int node, busrange[2];
if (pc->busnode[b])
return PCITAG_CREATE(0, b, d, f);
node = pc->pc_node;
if (OF_getprop(node, "bus-range", &busrange, sizeof(busrange)) < 0)
node = OF_child(pc->pc_node);
for (; node; node = OF_peer(node)) {
while ((OF_getprop(node, "bus-range", &busrange,
sizeof(busrange)) == sizeof(busrange)) &&
(b >= busrange[0] && b <= busrange[1])) {
node = OF_child(node);
}
if (OF_getprop(node, "reg", ®, sizeof(reg)) < sizeof(reg))
continue;
if (b != OFW_PCI_PHYS_HI_BUS(reg.phys_hi))
continue;
if (d != OFW_PCI_PHYS_HI_DEVICE(reg.phys_hi))
continue;
if (f != OFW_PCI_PHYS_HI_FUNCTION(reg.phys_hi))
continue;
tag = PCITAG_CREATE(node, b, d, f);
return (tag);
}
return (PCITAG_CREATE(-1, b, d, f));
}
void
pci_decompose_tag(pci_chipset_tag_t pc, pcitag_t tag, int *b, int *d, int *f)
{
if (b != NULL)
*b = PCITAG_BUS(tag);
if (d != NULL)
*d = PCITAG_DEV(tag);
if (f != NULL)
*f = PCITAG_FUN(tag);
}
int
pci_conf_size(pci_chipset_tag_t pc, pcitag_t tag)
{
return (PCI_CONFIG_SPACE_SIZE);
}
pcireg_t
pci_conf_read(pci_chipset_tag_t pc, pcitag_t tag, int reg)
{
if (PCITAG_NODE(tag) != -1)
return (*(pc)->pc_conf_read)(pc->pc_conf_v, tag, reg);
return ((pcireg_t)~0);
}
void
pci_conf_write(pci_chipset_tag_t pc, pcitag_t tag, int reg, pcireg_t data)
{
if (PCITAG_NODE(tag) != -1)
(*(pc)->pc_conf_write)(pc->pc_conf_v, tag, reg, data);
}
int
pci_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
{
struct ofw_pci_register reg;
int node = PCITAG_NODE(pa->pa_tag);
int intr[4], len;
if (OF_getprop(node, "reg", ®, sizeof(reg)) < sizeof(reg))
return (ENODEV);
len = OF_getprop(node, "AAPL,interrupts", &intr, sizeof(intr));
if (len == sizeof(intr[0]))
goto found;
len = OF_getprop(node, "interrupts", intr, sizeof(intr));
if (len < sizeof(intr[0]))
return (ENODEV);
reg.size_hi = intr[0];
if (ofw_intr_map(OF_parent(node), (uint32_t *)®, intr)) {
}
found:
*ihp = intr[0];
return (0);
}
int
pci_intr_map_msi(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
{
return (-1);
}
int
pci_intr_line(pci_chipset_tag_t pc, pci_intr_handle_t ih)
{
return (ih);
}
const char *
pci_intr_string(pci_chipset_tag_t pc, pci_intr_handle_t ih)
{
static char str[16];
snprintf(str, sizeof(str), "irq %ld", ih);
return (str);
}
void *
pci_intr_establish(pci_chipset_tag_t pc, pci_intr_handle_t ih, int lvl,
int (*func)(void *), void *arg, const char *what)
{
return (*intr_establish_func)(pc, ih, IST_LEVEL, lvl, func, arg, what);
}
void
pci_intr_disestablish(pci_chipset_tag_t pc, void *cookie)
{
(*intr_disestablish_func)(pc, cookie);
}
int
pci_ether_hw_addr(pci_chipset_tag_t pc, uint8_t *oaddr)
{
uint8_t laddr[6];
int node, len;
node = OF_finddevice("enet");
len = OF_getprop(node, "local-mac-address", laddr, sizeof(laddr));
if (sizeof(laddr) == len) {
memcpy(oaddr, laddr, sizeof(laddr));
return (1);
}
oaddr[0] = oaddr[1] = oaddr[2] = 0xff;
oaddr[3] = oaddr[4] = oaddr[5] = 0xff;
return (0);
}
int
ofw_enumerate_pcibus(struct pci_softc *sc,
int (*match)(struct pci_attach_args *), struct pci_attach_args *pap)
{
pci_chipset_tag_t pc = sc->sc_pc;
struct ofw_pci_register reg;
int len, node, b, d, f, ret;
uint32_t val = 0;
char compat[32];
pcireg_t bhlcr;
pcitag_t tag;
if (sc->sc_bridgetag)
node = PCITAG_NODE(*sc->sc_bridgetag);
else
node = pc->pc_node;
len = OF_getprop(node, "compatible", compat, sizeof(compat));
if (len > 0 && strcmp(compat, "u3-agp") == 0) {
tag = PCITAG_CREATE(0, sc->sc_bus, 11, 0);
ret = pci_probe_device(sc, tag, match, pap);
if (match != NULL && ret != 0)
return (ret);
}
if (len > 0 && strcmp(compat, "u3-ht") == 0) {
int snode;
for (snode = OF_child(node); snode; snode = OF_peer(snode)) {
val = 0;
if ((OF_getprop(snode, "shasta-interrupt-sequencer",
&val, sizeof(val)) < sizeof(val)) || val != 1)
continue;
if (OF_getprop(snode, "reg", ®, sizeof(reg))
< sizeof(reg))
continue;
b = OFW_PCI_PHYS_HI_BUS(reg.phys_hi);
d = OFW_PCI_PHYS_HI_DEVICE(reg.phys_hi);
f = OFW_PCI_PHYS_HI_FUNCTION(reg.phys_hi);
tag = PCITAG_CREATE(snode, b, d, f);
bhlcr = pci_conf_read(pc, tag, PCI_BHLC_REG);
if (PCI_HDRTYPE_TYPE(bhlcr) > 2)
continue;
ret = pci_probe_device(sc, tag, match, pap);
if (match != NULL && ret != 0)
return (ret);
}
}
for (node = OF_child(node); node; node = OF_peer(node)) {
if (OF_getprop(node, "reg", ®, sizeof(reg)) < sizeof(reg))
continue;
val = 0;
if ((OF_getprop(node, "shasta-interrupt-sequencer", &val,
sizeof(val)) >= sizeof(val)) && val == 1)
continue;
b = OFW_PCI_PHYS_HI_BUS(reg.phys_hi);
d = OFW_PCI_PHYS_HI_DEVICE(reg.phys_hi);
f = OFW_PCI_PHYS_HI_FUNCTION(reg.phys_hi);
tag = PCITAG_CREATE(node, b, d, f);
bhlcr = pci_conf_read(pc, tag, PCI_BHLC_REG);
if (PCI_HDRTYPE_TYPE(bhlcr) > 2)
continue;
ret = pci_probe_device(sc, tag, match, pap);
if (match != NULL && ret != 0)
return (ret);
}
return (0);
}
int
ofw_intr_map(int node, uint32_t *addr, uint32_t *intr)
{
uint32_t imap[144], mmask[8], *mp, *mp1;
uint32_t acells, icells, mcells;
int ilen, mlen, i, step = 0;
int parent;
ilen = OF_getprop(node, "interrupt-map", imap, sizeof(imap));
mlen = OF_getprop(node, "interrupt-map-mask", mmask, sizeof(mmask));
if (ilen < 0 || mlen < 0)
return (-1);
if ((OF_getprop(node, "#address-cells", &acells, 4) < 0) ||
(OF_getprop(node, "#interrupt-cells", &icells, 4) < 0))
return (-1);
mcells = acells + icells;
if (mcells != (mlen / sizeof(mmask[0])))
return (-1);
for (i = 0; i < mcells; i++)
addr[i] &= mmask[i];
for (mp = imap; ilen > mlen; mp += step) {
mp1 = mp + mcells;
parent = *mp1;
if (bcmp(mp, addr, mlen) == 0) {
char ic[20];
if (OF_getprop(parent, "interrupt-controller", ic, 20))
return ofw_intr_map(parent, &mp1[1], intr);
*intr = mp1[1];
return (0);
}
if (OF_getprop(parent, "#address-cells", &acells, 4) < 0)
acells = 0;
if (OF_getprop(parent, "#interrupt-cells", &icells, 4) < 0)
break;
step = mcells + 1 + acells + icells;
ilen -= step * sizeof(imap[0]);
}
return (-1);
}