Implement support for IO space, and better-handle both variants of MMIO space.

This commit is contained in:
jakllsch 2019-02-28 00:17:13 +00:00
parent 2cc9171b5c
commit 3689f53f38

View File

@ -1,4 +1,4 @@
/* $NetBSD: pcihost_fdt.c,v 1.6 2018/11/19 11:08:16 jmcneill Exp $ */
/* $NetBSD: pcihost_fdt.c,v 1.7 2019/02/28 00:17:13 jakllsch Exp $ */
/*-
* Copyright (c) 2018 Jared D. McNeill <jmcneill@invisible.ca>
@ -27,7 +27,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: pcihost_fdt.c,v 1.6 2018/11/19 11:08:16 jmcneill Exp $");
__KERNEL_RCSID(0, "$NetBSD: pcihost_fdt.c,v 1.7 2019/02/28 00:17:13 jakllsch Exp $");
#include <sys/param.h>
#include <sys/bus.h>
@ -81,6 +81,19 @@ enum pcihost_type {
PCIHOST_ECAM,
};
struct pcih_bus_space {
struct bus_space bst;
int (*map)(void *, bus_addr_t, bus_size_t,
int, bus_space_handle_t *);
struct space_range {
bus_addr_t bpci;
bus_addr_t bbus;
bus_size_t size;
} ranges[4];
size_t nranges;
};
struct pcihost_softc {
device_t sc_dev;
bus_dma_tag_t sc_dmat;
@ -95,6 +108,9 @@ struct pcihost_softc {
u_int sc_bus_max;
struct arm32_pci_chipset sc_pc;
struct pcih_bus_space sc_io;
struct pcih_bus_space sc_mem;
};
static int pcihost_match(device_t, cfdata_t, void *);
@ -126,6 +142,9 @@ static void * pcihost_intr_establish(void *, pci_intr_handle_t,
const char *);
static void pcihost_intr_disestablish(void *, void *);
static int pcihost_bus_space_map(void *, bus_addr_t, bus_size_t,
int, bus_space_handle_t *);
CFATTACH_DECL_NEW(pcihost_fdt, sizeof(struct pcihost_softc),
pcihost_match, pcihost_attach, NULL, NULL);
@ -203,6 +222,7 @@ pcihost_attach(device_t parent, device_t self, void *aux)
pba.pba_flags = PCI_FLAGS_MRL_OKAY |
PCI_FLAGS_MRM_OKAY |
PCI_FLAGS_MWI_OKAY |
PCI_FLAGS_IO_OKAY |
PCI_FLAGS_MEM_OKAY;
#ifdef __HAVE_PCI_MSI_MSIX
if (sc->sc_type == PCIHOST_ECAM) {
@ -210,8 +230,8 @@ pcihost_attach(device_t parent, device_t self, void *aux)
PCI_FLAGS_MSIX_OKAY;
}
#endif
pba.pba_iot = 0;
pba.pba_memt = sc->sc_bst;
pba.pba_iot = &sc->sc_io.bst;
pba.pba_memt = &sc->sc_mem.bst;
pba.pba_dmat = sc->sc_dmat;
#ifdef _PCI_HAVE_DMA64
pba.pba_dmat64 = sc->sc_dmat;
@ -253,6 +273,18 @@ pcihost_config(struct pcihost_softc *sc)
u_int probe_only;
int error, len;
struct pcih_bus_space * const pibs = &sc->sc_io;
pibs->bst = *sc->sc_bst;
pibs->bst.bs_cookie = pibs;
pibs->map = pibs->bst.bs_map;
pibs->bst.bs_map = pcihost_bus_space_map;
struct pcih_bus_space * const pmbs = &sc->sc_mem;
pmbs->bst = *sc->sc_bst;
pmbs->bst.bs_cookie = pmbs;
pmbs->map = pmbs->bst.bs_map;
pmbs->bst.bs_map = pcihost_bus_space_map;
/*
* If this flag is set, skip configuration of the PCI bus and use existing config.
*/
@ -276,47 +308,73 @@ pcihost_config(struct pcihost_softc *sc)
*/
while (len >= 28) {
const uint32_t phys_hi = be32dec(&ranges[0]);
const uint64_t bus_phys = be64dec(&ranges[1]);
const uint64_t cpu_phys = be64dec(&ranges[3]);
const uint64_t size = be64dec(&ranges[5]);
len -= 28;
ranges += 7;
const bool is64 = (__SHIFTOUT(phys_hi, PHYS_HI_SPACE) ==
PHYS_HI_SPACE_MEM64) ? true : false;
switch (__SHIFTOUT(phys_hi, PHYS_HI_SPACE)) {
case PHYS_HI_SPACE_IO:
if (pibs->nranges + 1 >= __arraycount(pibs->ranges)) {
aprint_error_dev(sc->sc_dev, "too many IO ranges\n");
continue;
}
pibs->ranges[pibs->nranges].bpci = bus_phys;
pibs->ranges[pibs->nranges].bbus = cpu_phys;
pibs->ranges[pibs->nranges].size = size;
++pibs->nranges;
if (ioext != NULL) {
aprint_error_dev(sc->sc_dev, "ignoring duplicate IO space range\n");
continue;
}
ioext = extent_create("pciio", cpu_phys, cpu_phys + size - 1, NULL, 0, EX_NOWAIT);
ioext = extent_create("pciio", bus_phys, bus_phys + size - 1, NULL, 0, EX_NOWAIT);
aprint_verbose_dev(sc->sc_dev,
"I/O memory @ 0x%" PRIx64 " size 0x%" PRIx64 "\n",
cpu_phys, size);
"IO: 0x%" PRIx64 "+0x%" PRIx64 "@0x%" PRIx64 "\n",
bus_phys, size, cpu_phys);
/* reserve a PC-like legacy IO ports range, perhaps for access to VGA registers */
if (bus_phys == 0 && size >= 0x10000)
extent_alloc_region(ioext, 0, 0x1000, EX_WAITOK);
break;
case PHYS_HI_SPACE_MEM64:
/* FALLTHROUGH */
case PHYS_HI_SPACE_MEM32:
if ((phys_hi & PHYS_HI_PREFETCH) != 0) {
if (pmbs->nranges + 1 >= __arraycount(pmbs->ranges)) {
aprint_error_dev(sc->sc_dev, "too many mem ranges\n");
continue;
}
/* both pmem and mem spaces are in the same tag */
pmbs->ranges[pmbs->nranges].bpci = bus_phys;
pmbs->ranges[pmbs->nranges].bbus = cpu_phys;
pmbs->ranges[pmbs->nranges].size = size;
++pmbs->nranges;
if ((phys_hi & PHYS_HI_PREFETCH) != 0 ||
__SHIFTOUT(phys_hi, PHYS_HI_SPACE) == PHYS_HI_SPACE_MEM64) {
if (pmemext != NULL) {
aprint_error_dev(sc->sc_dev, "ignoring duplicate mem (prefetchable) range\n");
continue;
}
pmemext = extent_create("pcipmem", cpu_phys, cpu_phys + size - 1, NULL, 0, EX_NOWAIT);
pmemext = extent_create("pcipmem", bus_phys, bus_phys + size - 1, NULL, 0, EX_NOWAIT);
aprint_verbose_dev(sc->sc_dev,
"32-bit MMIO (prefetchable) @ 0x%" PRIx64 " size 0x%" PRIx64 "\n",
cpu_phys, size);
"MMIO (%d-bit prefetchable): 0x%" PRIx64 "+0x%" PRIx64 "@0x%" PRIx64 "\n",
is64 ? 64 : 32, bus_phys, size, cpu_phys);
} else {
if (memext != NULL) {
aprint_error_dev(sc->sc_dev, "ignoring duplicate mem (non-prefetchable) range\n");
continue;
}
memext = extent_create("pcimem", cpu_phys, cpu_phys + size - 1, NULL, 0, EX_NOWAIT);
memext = extent_create("pcimem", bus_phys, bus_phys + size - 1, NULL, 0, EX_NOWAIT);
aprint_verbose_dev(sc->sc_dev,
"32-bit MMIO (non-prefetchable) @ 0x%" PRIx64 " size 0x%" PRIx64 "\n",
cpu_phys, size);
"MMIO (%d-bit non-prefetchable): 0x%" PRIx64 "+0x%" PRIx64 "@0x%" PRIx64 "\n",
is64 ? 64 : 32, bus_phys, size, cpu_phys);
}
break;
default:
break;
}
len -= 28;
ranges += 7;
}
error = pci_configure_bus(&sc->sc_pc, ioext, memext, pmemext, sc->sc_bus_min, PCIHOST_CACHELINE_SIZE);
@ -597,3 +655,20 @@ pcihost_intr_disestablish(void *v, void *vih)
fdtbus_intr_disestablish(sc->sc_phandle, vih);
}
static int
pcihost_bus_space_map(void *t, bus_addr_t bpa, bus_size_t size, int flag,
bus_space_handle_t *bshp)
{
struct pcih_bus_space * const pbs = t;
for (size_t i = 0; i < pbs->nranges; i++) {
const bus_addr_t rmin = pbs->ranges[i].bpci;
const bus_addr_t rmax = pbs->ranges[i].bpci - 1 + pbs->ranges[i].size;
if ((bpa >= rmin) && ((bpa - 1 + size) <= rmax)) {
return pbs->map(t, bpa - pbs->ranges[i].bpci + pbs->ranges[i].bbus, size, flag, bshp);
}
}
return ERANGE;
}