#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <machine/autoconf.h>
#include <machine/bus.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcidevs.h>
#include <dev/ofw/openfirm.h>
int ht_match(struct device *, void *, void *);
void ht_attach(struct device *, struct device *, void *);
pcireg_t ht_conf_read(void *, pcitag_t, int);
void ht_conf_write(void *, pcitag_t, int, pcireg_t);
int ht_print(void *, const char *);
struct ht_softc {
struct device sc_dev;
int sc_maxdevs;
struct ppc_bus_space sc_mem_bus_space;
struct ppc_bus_space sc_io_bus_space;
struct ppc_pci_chipset sc_pc;
bus_space_tag_t sc_memt;
bus_space_handle_t sc_config0_memh;
bus_space_handle_t sc_config1_memh;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_config0_ioh;
};
const struct cfattach ht_ca = {
sizeof(struct ht_softc), ht_match, ht_attach
};
struct cfdriver ht_cd = {
NULL, "ht", DV_DULL,
};
int
ht_match(struct device *parent, void *cf, void *aux)
{
struct confargs *ca = aux;
if (strcmp(ca->ca_name, "ht") == 0)
return (1);
return (0);
}
void
ht_attach(struct device *parent, struct device *self, void *aux)
{
struct ht_softc *sc = (struct ht_softc *)self;
struct confargs *ca = aux;
struct pcibus_attach_args pba;
u_int32_t regs[6];
char compat[32];
int node, len;
if (ca->ca_node == 0) {
printf(": invalid node on ht config\n");
return;
}
len = OF_getprop(ca->ca_node, "reg", regs, sizeof(regs));
if (len < 0 || len < sizeof(regs)) {
printf(": regs lookup failed, node %x\n", ca->ca_node);
return;
}
sc->sc_mem_bus_space.bus_base = 0x80000000;
sc->sc_mem_bus_space.bus_size = 0;
sc->sc_mem_bus_space.bus_io = 0;
sc->sc_memt = &sc->sc_mem_bus_space;
sc->sc_io_bus_space.bus_base = 0x80000000;
sc->sc_io_bus_space.bus_size = 0;
sc->sc_io_bus_space.bus_io = 1;
sc->sc_iot = &sc->sc_io_bus_space;
sc->sc_maxdevs = 1;
for (node = OF_child(ca->ca_node); node; node = OF_peer(node))
sc->sc_maxdevs++;
if (bus_space_map(sc->sc_memt, regs[1],
(1 << 11)*sc->sc_maxdevs, 0, &sc->sc_config0_memh)) {
printf(": can't map PCI config0 memory\n");
return;
}
if (bus_space_map(sc->sc_memt, regs[1] + 0x01000000,
regs[2] - 0x01000000, 0, &sc->sc_config1_memh)) {
printf(": can't map PCI config1 memory\n");
return;
}
if (bus_space_map(sc->sc_iot, regs[4], 0x1000, 0,
&sc->sc_config0_ioh)) {
printf(": can't map PCI config0 io\n");
return;
}
len = OF_getprop(ca->ca_node, "compatible", compat, sizeof(compat));
if (len <= 0)
printf(": unknown");
else
printf(": %s", compat);
sc->sc_pc.pc_conf_v = sc;
sc->sc_pc.pc_node = ca->ca_node;
sc->sc_pc.pc_conf_read = ht_conf_read;
sc->sc_pc.pc_conf_write = ht_conf_write;
bzero(&pba, sizeof(pba));
pba.pba_busname = "pci";
pba.pba_iot = sc->sc_iot;
pba.pba_memt = sc->sc_memt;
pba.pba_dmat = &pci_bus_dma_tag;
pba.pba_pc = &sc->sc_pc;
pba.pba_domain = pci_ndomains++;
pba.pba_bus = 0;
printf(", %d devices\n", sc->sc_maxdevs);
config_found(self, &pba, ht_print);
}
pcireg_t
ht_conf_read(void *cpv, pcitag_t tag, int offset)
{
struct ht_softc *sc = cpv;
int bus, dev, fcn;
pcireg_t reg;
uint32_t val;
val = PCITAG_OFFSET(tag);
#ifdef DEBUG
printf("ht_conf_read: tag=%x, offset=%x\n", val, offset);
#endif
pci_decompose_tag(NULL, tag, &bus, &dev, &fcn);
if (bus == 0 && dev == 0) {
val |= (offset << 2);
reg = bus_space_read_4(sc->sc_iot, sc->sc_config0_ioh, val);
reg = letoh32(reg);
} else if (bus == 0) {
if (fcn > 0)
return ~0;
val |= offset;
reg = bus_space_read_4(sc->sc_memt, sc->sc_config0_memh, val);
} else {
val |= offset;
reg = bus_space_read_4(sc->sc_memt, sc->sc_config1_memh, val);
}
#ifdef DEBUG
printf("ht_conf_read: reg=%x\n", reg);
#endif
return reg;
}
void
ht_conf_write(void *cpv, pcitag_t tag, int offset, pcireg_t data)
{
struct ht_softc *sc = cpv;
int bus, dev, fcn;
uint32_t val;
val = PCITAG_OFFSET(tag);
#ifdef DEBUG
printf("ht_conf_write: tag=%x, offset=%x, data = %x\n",
val, offset, data);
#endif
pci_decompose_tag(NULL, tag, &bus, &dev, &fcn);
if (bus == 0 && dev == 0) {
val |= (offset << 2);
data = htole32(data);
bus_space_write_4(sc->sc_iot, sc->sc_config0_ioh, val, data);
bus_space_read_4(sc->sc_iot, sc->sc_config0_ioh, val);
} else if (bus == 0) {
if (fcn > 0)
return;
val |= offset;
bus_space_write_4(sc->sc_memt, sc->sc_config0_memh, val, data);
bus_space_read_4(sc->sc_memt, sc->sc_config0_memh, val);
} else {
val |= offset;
bus_space_write_4(sc->sc_memt, sc->sc_config1_memh, val, data);
bus_space_read_4(sc->sc_memt, sc->sc_config1_memh, val);
}
}
int
ht_print(void *aux, const char *pnp)
{
struct pcibus_attach_args *pba = aux;
if (pnp)
printf("%s at %s", pba->pba_busname, pnp);
printf(" bus %d", pba->pba_bus);
return (UNCONF);
}