diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2009-04-06 20:51:49 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2009-04-06 20:51:49 +0000 |
commit | 46990b14d260e53f3cc5476531b07d43c6381cf9 (patch) | |
tree | 082cc204b4b1486a2c2f5af388d98eef69ae8167 | |
parent | 28400fae1b342b9c5d57327c525ecf50b259761e (diff) |
Set a first step on the road towards proper accounting of PCI resources by
keeping a per-bus extent containing the address space available to the bus.
Address space assigned to devices will be removed from these extents when
we attach a bus. And when we try to map a PCI BAR that hasn't had address
space assigned to it, we will allocate free space from this extent.
This won't do anything until the parent devices actually allocate and
initialize the extents.
ok oga@
-rw-r--r-- | sys/dev/pci/pci.c | 115 | ||||
-rw-r--r-- | sys/dev/pci/pci_map.c | 20 | ||||
-rw-r--r-- | sys/dev/pci/pcivar.h | 12 |
3 files changed, 142 insertions, 5 deletions
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index 45421372d19..543cc0f35df 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pci.c,v 1.59 2008/12/28 18:26:53 kettenis Exp $ */ +/* $OpenBSD: pci.c,v 1.60 2009/04/06 20:51:48 kettenis Exp $ */ /* $NetBSD: pci.c,v 1.31 1997/06/06 23:48:04 thorpej Exp $ */ /* @@ -43,6 +43,7 @@ #include <dev/pci/pcireg.h> #include <dev/pci/pcivar.h> #include <dev/pci/pcidevs.h> +#include <dev/pci/ppbreg.h> int pcimatch(struct device *, void *, void *); void pciattach(struct device *, struct device *, void *); @@ -84,6 +85,7 @@ int pcisubmatch(struct device *, void *, void *); int pci_enumerate_bus(struct pci_softc *, int (*)(struct pci_attach_args *), struct pci_attach_args *); #endif +int pci_reserve_resources(struct pci_attach_args *); /* * Important note about PCI-ISA bridges: @@ -153,6 +155,8 @@ pciattach(struct device *parent, struct device *self, void *aux) sc->sc_memt = pba->pba_memt; sc->sc_dmat = pba->pba_dmat; sc->sc_pc = pba->pba_pc; + sc->sc_ioex = pba->pba_ioex; + sc->sc_memex = pba->pba_memex; sc->sc_domain = pba->pba_domain; sc->sc_bus = pba->pba_bus; sc->sc_bridgetag = pba->pba_bridgetag; @@ -160,6 +164,7 @@ pciattach(struct device *parent, struct device *self, void *aux) sc->sc_maxndevs = pci_bus_maxdevs(pba->pba_pc, pba->pba_bus); sc->sc_intrswiz = pba->pba_intrswiz; sc->sc_intrtag = pba->pba_intrtag; + pci_enumerate_bus(sc, pci_reserve_resources, NULL); pci_enumerate_bus(sc, NULL, NULL); } @@ -276,6 +281,8 @@ pci_probe_device(struct pci_softc *sc, pcitag_t tag, pa.pa_memt = sc->sc_memt; pa.pa_dmat = sc->sc_dmat; pa.pa_pc = pc; + pa.pa_ioex = sc->sc_ioex; + pa.pa_memex = sc->sc_memex; pa.pa_domain = sc->sc_domain; pa.pa_bus = bus; pa.pa_device = device; @@ -498,6 +505,112 @@ pci_enumerate_bus(struct pci_softc *sc, } #endif /* PCI_MACHDEP_ENUMERATE_BUS */ +int +pci_reserve_resources(struct pci_attach_args *pa) +{ + pci_chipset_tag_t pc = pa->pa_pc; + pcitag_t tag = pa->pa_tag; + pcireg_t bhlc, blr, type; + bus_addr_t base, limit; + bus_size_t size; + int reg, reg_start, reg_end; + + bhlc = pci_conf_read(pc, tag, PCI_BHLC_REG); + switch (PCI_HDRTYPE_TYPE(bhlc)) { + case 0: + reg_start = PCI_MAPREG_START; + reg_end = PCI_MAPREG_END; + break; + case 1: /* PCI-PCI bridge */ + reg_start = PCI_MAPREG_START; + reg_end = PCI_MAPREG_PPB_END; + break; + case 2: /* PCI-CardBus bridge */ + reg_start = PCI_MAPREG_START; + reg_end = PCI_MAPREG_PCB_END; + break; + default: + return (0); + } + + for (reg = reg_start; reg < reg_end; reg += 4) { + if (!pci_mapreg_probe(pc, tag, reg, &type)) + continue; + + if (pci_mapreg_info(pc, tag, reg, type, &base, &size, NULL)) + continue; + + if (base == 0) + continue; + + switch (type) { + case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT: + case PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_64BIT: + if (pa->pa_memex && extent_alloc_region(pa->pa_memex, + base, size, EX_NOWAIT)) + printf("mem address conflict 0x%x/0x%x\n", + base, size); + break; + case PCI_MAPREG_TYPE_IO: + if (pa->pa_ioex && extent_alloc_region(pa->pa_ioex, + base, size, EX_NOWAIT)) + printf("io address conflict 0x%x/0x%x\n", + base, size); + break; + } + + if (type & PCI_MAPREG_MEM_TYPE_64BIT) + reg += 4; + } + + if (PCI_HDRTYPE_TYPE(bhlc) != 1) + return (0); + + /* Figure out the I/O address range of the bridge. */ + blr = pci_conf_read(pc, tag, PPB_REG_IOSTATUS); + base = (blr & 0x000000f0) << 8; + limit = (blr & 0x000f000) | 0x00000fff; + if (limit > base) + size = (limit - base + 1); + else + size = 0; + if (pa->pa_ioex && base > 0 && size > 0) { + if (extent_alloc_region(pa->pa_ioex, base, size, EX_NOWAIT)) + printf("bridge io address conflict 0x%x/0x%x\n", + base, size); + } + + /* Figure out the memory mapped I/O address range of the bridge. */ + blr = pci_conf_read(pc, tag, PPB_REG_MEM); + base = (blr & 0x0000fff0) << 16; + limit = (blr & 0xfff00000) | 0x000fffff; + if (limit > base) + size = (limit - base + 1); + else + size = 0; + if (pa->pa_memex && base > 0 && size > 0) { + if (extent_alloc_region(pa->pa_memex, base, size, EX_NOWAIT)) + printf("bridge mem address conflict 0x%x/0x%x\n", + base, size); + } + + /* Figure out the prefetchable memory address range of the bridge. */ + blr = pci_conf_read(pc, tag, PPB_REG_PREFMEM); + base = (blr & 0x0000fff0) << 16; + limit = (blr & 0xfff00000) | 0x000fffff; + if (limit > base) + size = (limit - base + 1); + else + size = 0; + if (pa->pa_memex && base > 0 && size > 0) { + if (extent_alloc_region(pa->pa_memex, base, size, EX_NOWAIT)) + printf("bridge mem address conflict 0x%x/0x%x\n", + base, size); + } + + return (0); +} + /* * Vital Product Data (PCI 2.2) */ diff --git a/sys/dev/pci/pci_map.c b/sys/dev/pci/pci_map.c index f3544e0c80e..ae9677bf686 100644 --- a/sys/dev/pci/pci_map.c +++ b/sys/dev/pci/pci_map.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pci_map.c,v 1.23 2008/06/26 05:42:17 ray Exp $ */ +/* $OpenBSD: pci_map.c,v 1.24 2009/04/06 20:51:48 kettenis Exp $ */ /* $NetBSD: pci_map.c,v 1.7 2000/05/10 16:58:42 thorpej Exp $ */ /*- @@ -320,8 +320,22 @@ pci_mapreg_map(struct pci_attach_args *pa, int reg, pcireg_t type, int busflags, &base, &size, &flags)) != 0) return (rv); #if !defined(__sparc64__) && !defined(__socppc__) - if (base == 0) - return (EINVAL); /* disabled because of invalid BAR */ + if (base == 0) { + struct extent *ex; + + if (PCI_MAPREG_TYPE(type) == PCI_MAPREG_TYPE_IO) + ex = pa->pa_ioex; + else + ex = pa->pa_memex; + + if (ex == NULL || extent_alloc(ex, size, size, 0, 0, 0, &base)) + return (EINVAL); /* disabled because of invalid BAR */ + + pci_conf_write(pa->pa_pc, pa->pa_tag, reg, base); + if (PCI_MAPREG_MEM_TYPE(type) == PCI_MAPREG_MEM_TYPE_64BIT) + pci_conf_write(pa->pa_pc, pa->pa_tag, reg + 4, + (u_int64_t)base >> 32); + } #endif csr = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); diff --git a/sys/dev/pci/pcivar.h b/sys/dev/pci/pcivar.h index ccec3443502..0347e29430b 100644 --- a/sys/dev/pci/pcivar.h +++ b/sys/dev/pci/pcivar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pcivar.h,v 1.57 2007/12/31 19:13:36 kettenis Exp $ */ +/* $OpenBSD: pcivar.h,v 1.58 2009/04/06 20:51:48 kettenis Exp $ */ /* $NetBSD: pcivar.h,v 1.23 1997/06/06 23:48:05 thorpej Exp $ */ /* @@ -43,6 +43,8 @@ */ #include <sys/device.h> +#include <sys/malloc.h> +#include <sys/extent.h> #include <machine/bus.h> #include <dev/pci/pcireg.h> @@ -89,6 +91,9 @@ struct pcibus_attach_args { bus_dma_tag_t pba_dmat; /* DMA tag */ pci_chipset_tag_t pba_pc; + struct extent *pba_ioex; + struct extent *pba_memex; + int pba_domain; /* PCI domain */ int pba_bus; /* PCI bus number */ @@ -117,6 +122,9 @@ struct pci_attach_args { pci_chipset_tag_t pa_pc; int pa_flags; /* flags; see below */ + struct extent *pa_ioex; + struct extent *pa_memex; + u_int pa_domain; u_int pa_bus; u_int pa_device; @@ -170,6 +178,8 @@ struct pci_softc { bus_space_tag_t sc_iot, sc_memt; bus_dma_tag_t sc_dmat; pci_chipset_tag_t sc_pc; + struct extent *sc_ioex; + struct extent *sc_memex; void *sc_powerhook; LIST_HEAD(, pci_dev) sc_devs; int sc_domain, sc_bus, sc_maxndevs; |