#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/extent.h>
#include <sys/malloc.h>
#include <machine/autoconf.h>
#include <machine/bus.h>
#include <machine/intr.h>
#include <machine/octeonvar.h>
#include <dev/pci/pcidevs.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/ppbreg.h>
#include <octeon/dev/iobusvar.h>
#include <octeon/dev/octeon_pcibus.h>
#include <uvm/uvm_extern.h>
#ifdef DEBUG
#define OCTEON_PCIDEBUG(p) printf p
#else
#define OCTEON_PCIDEBUG(p)
#endif
#define REG_READ32(addr) (*(volatile uint32_t *)(addr))
#define REG_WRITE32(addr, data) (*(volatile uint32_t *)(addr) = (uint32_t)(data))
struct octeon_pcibus_softc {
struct device sc_dev;
struct mips_pci_chipset sc_pc;
struct iobus_attach_args *sc_aa;
};
int octeon_pcibus_match(struct device *, void *, void *);
void octeon_pcibus_attach(struct device *, struct device *, void *);
int octeon_pcibus_print(void *, const char *);
const struct cfattach pcibus_ca = {
sizeof(struct octeon_pcibus_softc),
octeon_pcibus_match, octeon_pcibus_attach
};
struct cfdriver pcibus_cd = {
NULL, "pcibus", DV_DULL
};
bus_addr_t octeon_pcibus_pa_to_device(paddr_t);
paddr_t octeon_pcibus_device_to_pa(bus_addr_t);
void octeon_pcibus_attach_hook(struct device *, struct device *,
struct pcibus_attach_args *);
int octeon_pcibus_bus_maxdevs(void *, int);
pcitag_t octeon_pcibus_make_tag(void *, int, int, int);
void octeon_pcibus_decompose_tag(void *, pcitag_t, int *, int *, int *);
int octeon_pcibus_pci_conf_size(void *, pcitag_t);
pcireg_t octeon_pcibus_pci_conf_read(void *, pcitag_t, int);
void octeon_pcibus_pci_conf_write(void *, pcitag_t, int, pcireg_t);
int octeon_pcibus_pci_intr_map(struct pci_attach_args *,
pci_intr_handle_t *);
const char *octeon_pcibus_pci_intr_string(void *, pci_intr_handle_t);
void *octeon_pcibus_pci_intr_establish(void *, pci_intr_handle_t, int,
int (*)(void *), void *, char *);
void octeon_pcibus_pci_intr_disestablish(void *, void *);
int octeon_pcibus_intr_map(int dev, int fn, int pin);
int octeon_pcibus_io_map(bus_space_tag_t, bus_addr_t, bus_size_t, int,
bus_space_handle_t *);
int octeon_pcibus_mem_map(bus_space_tag_t, bus_addr_t, bus_size_t, int,
bus_space_handle_t *);
struct extent *octeon_pcibus_get_resource_extent(pci_chipset_tag_t, int);
struct machine_bus_dma_tag octeon_pcibus_bus_dma_tag = {
._cookie = NULL,
._dmamap_create = _dmamap_create,
._dmamap_destroy = _dmamap_destroy,
._dmamap_load = _dmamap_load,
._dmamap_load_mbuf = _dmamap_load_mbuf,
._dmamap_load_uio = _dmamap_load_uio,
._dmamap_load_raw = _dmamap_load_raw,
._dmamap_load_buffer = _dmamap_load_buffer,
._dmamap_unload = _dmamap_unload,
._dmamap_sync = _dmamap_sync,
._dmamem_alloc = _dmamem_alloc,
._dmamem_free = _dmamem_free,
._dmamem_map = _dmamem_map,
._dmamem_unmap = _dmamem_unmap,
._dmamem_mmap = _dmamem_mmap,
._pa_to_device = octeon_pcibus_pa_to_device,
._device_to_pa = octeon_pcibus_device_to_pa
};
#define _OCTEON_PCIBUS_PCIIO_BASE 0x00001000
#define _OCTEON_PCIBUS_PCIIO_SIZE 0x08000000
#define _OCTEON_PCIBUS_PCIMEM_BASE 0x80000000
#define _OCTEON_PCIBUS_PCIMEM_SIZE 0x40000000
struct mips_bus_space octeon_pcibus_pci_io_space_tag = {
.bus_base = PHYS_TO_XKPHYS(_OCTEON_PCIBUS_PCIIO_BASE, CCA_NC),
.bus_private = NULL,
._space_read_1 = generic_space_read_1,
._space_write_1 = generic_space_write_1,
._space_read_2 = generic_space_read_2,
._space_write_2 = generic_space_write_2,
._space_read_4 = generic_space_read_4,
._space_write_4 = generic_space_write_4,
._space_read_8 = generic_space_read_8,
._space_write_8 = generic_space_write_8,
._space_read_raw_2 = generic_space_read_raw_2,
._space_write_raw_2 = generic_space_write_raw_2,
._space_read_raw_4 = generic_space_read_raw_4,
._space_write_raw_4 = generic_space_write_raw_4,
._space_read_raw_8 = generic_space_read_raw_8,
._space_write_raw_8 = generic_space_write_raw_8,
._space_map = octeon_pcibus_io_map,
._space_unmap = generic_space_unmap,
._space_subregion = generic_space_region,
._space_vaddr = generic_space_vaddr
};
struct mips_bus_space octeon_pcibus_pci_mem_space_tag = {
.bus_base = PHYS_TO_XKPHYS(_OCTEON_PCIBUS_PCIMEM_BASE, CCA_NC),
.bus_private = NULL,
._space_read_1 = generic_space_read_1,
._space_write_1 = generic_space_write_1,
._space_read_2 = generic_space_read_2,
._space_write_2 = generic_space_write_2,
._space_read_4 = generic_space_read_4,
._space_write_4 = generic_space_write_4,
._space_read_8 = generic_space_read_8,
._space_write_8 = generic_space_write_8,
._space_read_raw_2 = generic_space_read_raw_2,
._space_write_raw_2 = generic_space_write_raw_2,
._space_read_raw_4 = generic_space_read_raw_4,
._space_write_raw_4 = generic_space_write_raw_4,
._space_read_raw_8 = generic_space_read_raw_8,
._space_write_raw_8 = generic_space_write_raw_8,
._space_map = octeon_pcibus_mem_map,
._space_unmap = generic_space_unmap,
._space_subregion = generic_space_region,
._space_vaddr = generic_space_vaddr
};
int
octeon_pcibus_match(struct device *parent, void *vcf, void *aux)
{
struct iobus_attach_args *aa = aux;
if ((octeon_boot_info->config_flags & BOOTINFO_CFG_FLAG_PCI_HOST) == 0) {
OCTEON_PCIDEBUG(("%s, no PCI host function detected.\n", __func__));
return 0;
}
if (strcmp(aa->aa_name, pcibus_cd.cd_name) == 0)
return 1;
return 0;
}
void
octeon_pcibus_attach(struct device *parent, struct device *self, void *aux)
{
struct octeon_pcibus_softc *sc;
struct pcibus_attach_args pba;
sc = (struct octeon_pcibus_softc *)self;
sc->sc_aa = aux;
printf("\n");
sc->sc_pc.pc_attach_hook = octeon_pcibus_attach_hook;
sc->sc_pc.pc_bus_maxdevs = octeon_pcibus_bus_maxdevs;
sc->sc_pc.pc_make_tag = octeon_pcibus_make_tag;
sc->sc_pc.pc_decompose_tag = octeon_pcibus_decompose_tag;
sc->sc_pc.pc_conf_v = sc;
sc->sc_pc.pc_conf_size = octeon_pcibus_pci_conf_size;
sc->sc_pc.pc_conf_read = octeon_pcibus_pci_conf_read;
sc->sc_pc.pc_conf_write = octeon_pcibus_pci_conf_write;
sc->sc_pc.pc_intr_v = sc;
sc->sc_pc.pc_intr_map = octeon_pcibus_pci_intr_map;
sc->sc_pc.pc_intr_string = octeon_pcibus_pci_intr_string;
sc->sc_pc.pc_intr_establish = octeon_pcibus_pci_intr_establish;
sc->sc_pc.pc_intr_disestablish = octeon_pcibus_pci_intr_disestablish;
bzero(&pba, sizeof pba);
pba.pba_busname = "pci";
pba.pba_iot = &octeon_pcibus_pci_io_space_tag;
pba.pba_memt = &octeon_pcibus_pci_mem_space_tag;
pba.pba_dmat = &octeon_pcibus_bus_dma_tag;
pba.pba_pc = &sc->sc_pc;
pba.pba_domain = pci_ndomains++;
pba.pba_bus = 0;
pba.pba_ioex = octeon_pcibus_get_resource_extent(&sc->sc_pc, 1);
pba.pba_memex = octeon_pcibus_get_resource_extent(&sc->sc_pc, 0);
config_found(&sc->sc_dev, &pba, octeon_pcibus_print);
}
bus_addr_t
octeon_pcibus_pa_to_device(paddr_t pa)
{
OCTEON_PCIDEBUG(("%s:%d: pa=%p\n", __func__, __LINE__, (void *)pa));
return pa & 0x1ffffffffffffUL;
}
paddr_t
octeon_pcibus_device_to_pa(bus_addr_t addr)
{
OCTEON_PCIDEBUG(("%s:%d: addr=%lx\n", __func__, __LINE__, addr));
return PHYS_TO_XKPHYS(addr, CCA_NC);
}
int
octeon_pcibus_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;
}
void
octeon_pcibus_attach_hook(struct device *parent, struct device *self,
struct pcibus_attach_args *pba)
{
}
int
octeon_pcibus_bus_maxdevs(void *v, int busno)
{
return (32);
}
pcitag_t
octeon_pcibus_make_tag(void *unused, int b, int d, int f)
{
return (b << 16) | (d << 11) | (f << 8);
}
void
octeon_pcibus_decompose_tag(void *unused, pcitag_t tag, int *bp, int *dp, int *fp)
{
if (bp != NULL)
*bp = (tag >> 16) & 0xff;
if (dp != NULL)
*dp = (tag >> 11) & 0x1f;
if (fp != NULL)
*fp = (tag >> 8) & 0x7;
}
int
octeon_pcibus_pci_conf_size(void *v, pcitag_t tag)
{
return PCI_CONFIG_SPACE_SIZE;
}
pcireg_t
octeon_pcibus_pci_conf_read(void *v, pcitag_t tag, int offset)
{
pcireg_t data;
uint64_t cfgoff;
if (tag == 0){
if (offset & 0x4){
cfgoff = OCTEON_PCI_CFG1 + (offset & 0xfff8);
} else {
cfgoff = OCTEON_PCI_CFG0 + (offset & 0xfff8);
}
} else {
cfgoff = tag + offset;
if (offset & 0x4) {
cfgoff = OCTEON_PCI_CONFIG_BASE1 + (cfgoff & 0xfffffff8);
} else {
cfgoff = OCTEON_PCI_CONFIG_BASE0 + (cfgoff & 0xfffffff8);
}
}
data = REG_READ32(cfgoff);
return data;
}
void
octeon_pcibus_pci_conf_write(void *v, pcitag_t tag, int offset, pcireg_t data)
{
uint64_t cfgoff;
if (tag == 0){
if (offset & 0x4){
cfgoff = OCTEON_PCI_CFG1 + (offset & 0xfff8);
} else {
cfgoff = OCTEON_PCI_CFG0 + (offset & 0xfff8);
}
} else {
cfgoff = tag + offset;
if (offset & 0x4){
cfgoff = OCTEON_PCI_CONFIG_BASE1 + (cfgoff & 0xfffffff8);
} else {
cfgoff = OCTEON_PCI_CONFIG_BASE0 + (cfgoff & 0xfffffff8);
}
}
REG_WRITE32(cfgoff, data);
}
int
octeon_pcibus_pci_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
{
#if 0
struct octeon_pcibus_softc *sc = pa->pa_pc->pc_intr_v;
#endif
int bus, dev, fn, pin;
*ihp = (pci_intr_handle_t)-1;
if (pa->pa_intrpin == 0)
return 1;
#ifdef DIAGNOSTIC
if (pa->pa_intrpin > 4) {
printf("%s: bad interrupt pin %d\n", __func__, pa->pa_intrpin);
return 1;
}
#endif
pci_decompose_tag(pa->pa_pc, pa->pa_tag, &bus, &dev, &fn);
if (pa->pa_bridgetag) {
pin = PPB_INTERRUPT_SWIZZLE(pa->pa_rawintrpin, dev);
*ihp = pa->pa_bridgeih[pin - 1];
} else {
if (bus == 0)
*ihp = octeon_pcibus_intr_map(dev, fn, pa->pa_intrpin);
if (*ihp == (pci_intr_handle_t)-1)
return 1;
}
return 0;
}
const char *
octeon_pcibus_pci_intr_string(void *cookie, pci_intr_handle_t ih)
{
static char irqstr[sizeof("irq 0123456789")];
snprintf(irqstr, sizeof irqstr, "irq %ld", ih);
return irqstr;
}
void *
octeon_pcibus_pci_intr_establish(void *cookie, pci_intr_handle_t ih, int level,
int (*cb)(void *), void *cbarg, char *name)
{
return octeon_intr_establish(ih, level, cb, cbarg, name);
}
void
octeon_pcibus_pci_intr_disestablish(void *cookie, void *ihp)
{
octeon_intr_disestablish(ihp);
}
int
octeon_pcibus_io_map(bus_space_tag_t t, bus_addr_t offs, bus_size_t size, int flags,
bus_space_handle_t *bshp)
{
if (ISSET(flags, BUS_SPACE_MAP_CACHEABLE)) {
offs +=
PHYS_TO_XKPHYS(0, CCA_CACHED) - PHYS_TO_XKPHYS(0, CCA_NC);
}
*bshp = t->bus_base + offs;
return 0;
}
int
octeon_pcibus_mem_map(bus_space_tag_t t, bus_addr_t offs, bus_size_t size, int flags,
bus_space_handle_t *bshp)
{
if (ISSET(flags, BUS_SPACE_MAP_CACHEABLE)) {
offs +=
PHYS_TO_XKPHYS(0, CCA_CACHED) - PHYS_TO_XKPHYS(0, CCA_NC);
}
*bshp = t->bus_base + offs;
return 0;
}
struct extent *
octeon_pcibus_get_resource_extent(pci_chipset_tag_t pc, int io)
{
struct octeon_pcibus_softc *sc = pc->pc_conf_v;
struct extent *ex;
char *exname;
int exnamesz;
exnamesz = 1 + 16 + 4;
exname = malloc(exnamesz, M_DEVBUF, M_NOWAIT);
if (exname == NULL)
return NULL;
snprintf(exname, exnamesz, "%s%s", sc->sc_dev.dv_xname,
io ? "_io" : "_mem");
ex = extent_create(exname, 0, 0xffffffffffffffff, M_DEVBUF, NULL, 0,
EX_NOWAIT | EX_FILLED);
if (ex == NULL)
goto error;
if (io) {
if (extent_free(ex, _OCTEON_PCIBUS_PCIIO_BASE,
_OCTEON_PCIBUS_PCIIO_SIZE, EX_NOWAIT) != 0)
goto error;
} else {
if (extent_free(ex, _OCTEON_PCIBUS_PCIMEM_BASE,
_OCTEON_PCIBUS_PCIMEM_SIZE, EX_NOWAIT) != 0)
goto error;
}
#if defined(DEBUG) && defined(DIAGNOSTIC)
extent_print(ex);
#endif
return ex;
error:
if (ex != NULL)
extent_destroy(ex);
free(exname, M_DEVBUF, exnamesz);
return NULL;
}
int
octeon_pcibus_intr_map(int dev, int fn, int pin)
{
return CIU_INT_PCI_INTA + ((pin - 1) & 3);
}