diff options
-rw-r--r-- | sys/dev/pci/pci.c | 245 | ||||
-rw-r--r-- | sys/dev/pci/pci_private.h | 4 |
2 files changed, 167 insertions, 82 deletions
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index c3a6441..d73c34c 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -136,7 +136,7 @@ static device_method_t pci_methods[] = { DEVMETHOD(bus_delete_resource, pci_delete_resource), DEVMETHOD(bus_alloc_resource, pci_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource), - DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), + DEVMETHOD(bus_activate_resource, pci_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_child_pnpinfo_str, pci_child_pnpinfo_str_method), DEVMETHOD(bus_child_location_str, pci_child_location_str_method), @@ -2294,8 +2294,8 @@ pci_add_map(device_t pcib, device_t bus, device_t dev, /* * Disable decoding via the command register before - * determining the BARs length since we will be placing them - * in a weird state. + * determining the BAR's length since we will be placing it in + * a weird state. */ cmd = PCIB_READ_CONFIG(pcib, b, s, f, PCIR_COMMAND, 2); PCIB_WRITE_CONFIG(pcib, b, s, f, PCIR_COMMAND, @@ -2336,7 +2336,10 @@ pci_add_map(device_t pcib, device_t bus, device_t dev, return (barlen); if (ln2range == 64) - /* Read the other half of a 64bit map register */ + /* + * Read the other half of a 64bit map register. We + * assume that the size of the BAR is less than 2^32. + */ base |= (uint64_t) PCIB_READ_CONFIG(pcib, b, s, f, reg + 4, 4) << 32; if (bootverbose) { printf("\tmap[%02x]: type %s, range %2d, base %#jx, size %2d", @@ -2422,8 +2425,10 @@ pci_add_map(device_t pcib, device_t bus, device_t dev, */ resource_list_delete(rl, type, reg); start = 0; - } else + } else { start = rman_get_start(res); + rman_set_device(res, bus); + } pci_write_config(dev, reg, start, 4); if (ln2range == 64) pci_write_config(dev, reg + 4, start >> 32, 4); @@ -2441,6 +2446,7 @@ static void pci_ata_maps(device_t pcib, device_t bus, device_t dev, int b, int s, int f, struct resource_list *rl, int force, uint32_t prefetchmask) { + struct resource *r; int rid, type, progif; #if 0 /* if this device supports PCI native addressing use it */ @@ -2463,12 +2469,14 @@ pci_ata_maps(device_t pcib, device_t bus, device_t dev, int b, } else { rid = PCIR_BAR(0); resource_list_add(rl, type, rid, 0x1f0, 0x1f7, 8); - resource_list_alloc(rl, bus, dev, type, &rid, 0x1f0, 0x1f7, 8, - 0); + r = resource_list_alloc(rl, bus, dev, type, &rid, 0x1f0, 0x1f7, + 8, 0); + rman_set_device(r, bus); rid = PCIR_BAR(1); resource_list_add(rl, type, rid, 0x3f6, 0x3f6, 1); - resource_list_alloc(rl, bus, dev, type, &rid, 0x3f6, 0x3f6, 1, - 0); + r = resource_list_alloc(rl, bus, dev, type, &rid, 0x3f6, 0x3f6, + 1, 0); + rman_set_device(r, bus); } if (progif & PCIP_STORAGE_IDE_MODESEC) { pci_add_map(pcib, bus, dev, b, s, f, PCIR_BAR(2), rl, force, @@ -2478,12 +2486,14 @@ pci_ata_maps(device_t pcib, device_t bus, device_t dev, int b, } else { rid = PCIR_BAR(2); resource_list_add(rl, type, rid, 0x170, 0x177, 8); - resource_list_alloc(rl, bus, dev, type, &rid, 0x170, 0x177, 8, - 0); + r = resource_list_alloc(rl, bus, dev, type, &rid, 0x170, 0x177, + 8, 0); + rman_set_device(r, bus); rid = PCIR_BAR(3); resource_list_add(rl, type, rid, 0x376, 0x376, 1); - resource_list_alloc(rl, bus, dev, type, &rid, 0x376, 0x376, 1, - 0); + r = resource_list_alloc(rl, bus, dev, type, &rid, 0x376, 0x376, + 1, 0); + rman_set_device(r, bus); } pci_add_map(pcib, bus, dev, b, s, f, PCIR_BAR(4), rl, force, prefetchmask & (1 << 4)); @@ -3391,6 +3401,7 @@ pci_alloc_map(device_t dev, device_t child, int type, int *rid, struct resource_list_entry *rle; struct resource *res; pci_addr_t map, testval; + uint16_t cmd; int mapsize; /* @@ -3402,12 +3413,21 @@ pci_alloc_map(device_t dev, device_t child, int type, int *rid, */ res = NULL; map = pci_read_config(child, *rid, 4); + + /* + * Disable decoding via the command register before + * determining the BAR's length since we will be placing it in + * a weird state. + */ + cmd = pci_read_config(child, PCIR_COMMAND, 2); + pci_write_config(child, PCIR_COMMAND, + cmd & ~(PCI_BAR_MEM(map) ? PCIM_CMD_MEMEN : PCIM_CMD_PORTEN), 2); + + /* Determine the BAR's length. */ pci_write_config(child, *rid, 0xffffffff, 4); testval = pci_read_config(child, *rid, 4); if (pci_maprange(testval) == 64) map |= (pci_addr_t)pci_read_config(child, *rid + 4, 4) << 32; - if (pci_mapbase(testval) == 0) - goto out; /* * Restore the original value of the BAR. We may have reprogrammed @@ -3415,6 +3435,11 @@ pci_alloc_map(device_t dev, device_t child, int type, int *rid, * we need the console device addressable. */ pci_write_config(child, *rid, map, 4); + pci_write_config(child, PCIR_COMMAND, cmd, 2); + + /* Ignore a BAR with a base of 0. */ + if (pci_mapbase(testval) == 0) + goto out; if (PCI_BAR_MEM(testval)) { if (type != SYS_RES_MEMORY) { @@ -3452,13 +3477,14 @@ pci_alloc_map(device_t dev, device_t child, int type, int *rid, * appropriate bar for that resource. */ res = BUS_ALLOC_RESOURCE(device_get_parent(dev), child, type, rid, - start, end, count, flags); + start, end, count, flags & ~RF_ACTIVE); if (res == NULL) { device_printf(child, "%#lx bytes of rid %#x res %d failed (%#lx, %#lx).\n", count, *rid, type, start, end); goto out; } + rman_set_device(res, dev); resource_list_add(rl, type, *rid, start, end, count); rle = resource_list_find(rl, type, *rid); if (rle == NULL) @@ -3472,10 +3498,10 @@ pci_alloc_map(device_t dev, device_t child, int type, int *rid, "Lazy allocation of %#lx bytes rid %#x type %d at %#lx\n", count, *rid, type, rman_get_start(res)); map = rman_get_start(res); -out:; pci_write_config(child, *rid, map, 4); if (pci_maprange(testval) == 64) pci_write_config(child, *rid + 4, map >> 32, 4); +out:; return (res); } @@ -3487,68 +3513,63 @@ pci_alloc_resource(device_t dev, device_t child, int type, int *rid, struct pci_devinfo *dinfo = device_get_ivars(child); struct resource_list *rl = &dinfo->resources; struct resource_list_entry *rle; + struct resource *res; pcicfgregs *cfg = &dinfo->cfg; + if (device_get_parent(child) != dev) + return (BUS_ALLOC_RESOURCE(device_get_parent(dev), child, + type, rid, start, end, count, flags)); + /* * Perform lazy resource allocation */ - if (device_get_parent(child) == dev) { - switch (type) { - case SYS_RES_IRQ: - /* - * Can't alloc legacy interrupt once MSI messages - * have been allocated. - */ - if (*rid == 0 && (cfg->msi.msi_alloc > 0 || - cfg->msix.msix_alloc > 0)) + switch (type) { + case SYS_RES_IRQ: + /* + * Can't alloc legacy interrupt once MSI messages have + * been allocated. + */ + if (*rid == 0 && (cfg->msi.msi_alloc > 0 || + cfg->msix.msix_alloc > 0)) + return (NULL); + + /* + * If the child device doesn't have an interrupt + * routed and is deserving of an interrupt, try to + * assign it one. + */ + if (*rid == 0 && !PCI_INTERRUPT_VALID(cfg->intline) && + (cfg->intpin != 0)) + pci_assign_interrupt(dev, child, 0); + break; + case SYS_RES_IOPORT: + case SYS_RES_MEMORY: + /* Allocate resources for this BAR if needed. */ + rle = resource_list_find(rl, type, *rid); + if (rle == NULL) { + res = pci_alloc_map(dev, child, type, rid, start, end, + count, flags); + if (res == NULL) return (NULL); - /* - * If the child device doesn't have an - * interrupt routed and is deserving of an - * interrupt, try to assign it one. - */ - if (*rid == 0 && !PCI_INTERRUPT_VALID(cfg->intline) && - (cfg->intpin != 0)) - pci_assign_interrupt(dev, child, 0); - break; - case SYS_RES_IOPORT: - case SYS_RES_MEMORY: - if (*rid < PCIR_BAR(cfg->nummaps)) { - /* - * Enable the I/O mode. We should - * also be assigning resources too - * when none are present. The - * resource_list_alloc kind of sorta does - * this... - */ - if (PCI_ENABLE_IO(dev, child, type)) - return (NULL); - } rle = resource_list_find(rl, type, *rid); - if (rle == NULL) - return (pci_alloc_map(dev, child, type, rid, - start, end, count, flags)); - break; } + /* - * If we've already allocated the resource, then - * return it now. But first we may need to activate - * it, since we don't allocate the resource as active - * above. Normally this would be done down in the - * nexus, but since we short-circuit that path we have - * to do its job here. Not sure if we should free the - * resource if it fails to activate. + * If the resource belongs to the bus, then give it to + * the child. We need to activate it if requested + * since the bus always allocates inactive resources. */ - rle = resource_list_find(rl, type, *rid); - if (rle != NULL && rle->res != NULL) { + if (rle != NULL && rle->res != NULL && + rman_get_device(rle->res) == dev) { if (bootverbose) device_printf(child, "Reserved %#lx bytes for rid %#x type %d at %#lx\n", rman_get_size(rle->res), *rid, type, rman_get_start(rle->res)); + rman_set_device(rle->res, child); if ((flags & RF_ACTIVE) && - bus_generic_activate_resource(dev, child, type, - *rid, rle->res) != 0) + bus_activate_resource(child, type, *rid, + rle->res) != 0) return (NULL); return (rle->res); } @@ -3557,6 +3578,59 @@ pci_alloc_resource(device_t dev, device_t child, int type, int *rid, start, end, count, flags)); } +int +pci_release_resource(device_t dev, device_t child, int type, int rid, + struct resource *r) +{ + int error; + + if (device_get_parent(child) != dev) + return (BUS_RELEASE_RESOURCE(device_get_parent(dev), child, + type, rid, r)); + + /* + * For BARs we don't actually want to release the resource. + * Instead, we deactivate the resource if needed and then give + * ownership of the BAR back to the bus. + */ + switch (type) { + case SYS_RES_IOPORT: + case SYS_RES_MEMORY: + if (rman_get_device(r) != child) + return (EINVAL); + if (rman_get_flags(r) & RF_ACTIVE) { + error = bus_deactivate_resource(child, type, rid, r); + if (error) + return (error); + } + rman_set_device(r, dev); + return (0); + } + return (bus_generic_rl_release_resource(dev, child, type, rid, r)); +} + +int +pci_activate_resource(device_t dev, device_t child, int type, int rid, + struct resource *r) +{ + int error; + + error = bus_generic_activate_resource(dev, child, type, rid, r); + if (error) + return (error); + + /* Enable decoding in the command register when activating BARs. */ + if (device_get_parent(child) == dev) { + switch (type) { + case SYS_RES_IOPORT: + case SYS_RES_MEMORY: + error = PCI_ENABLE_IO(dev, child, type); + break; + } + } + return (error); +} + void pci_delete_resource(device_t dev, device_t child, int type, int rid) { @@ -3570,27 +3644,34 @@ pci_delete_resource(device_t dev, device_t child, int type, int rid) dinfo = device_get_ivars(child); rl = &dinfo->resources; rle = resource_list_find(rl, type, rid); - if (rle) { - if (rle->res) { - if (rman_get_device(rle->res) != dev || - rman_get_flags(rle->res) & RF_ACTIVE) { - device_printf(dev, "delete_resource: " - "Resource still owned by child, oops. " - "(type=%d, rid=%d, addr=%lx)\n", - rle->type, rle->rid, - rman_get_start(rle->res)); - return; - } - bus_release_resource(dev, type, rid, rle->res); + if (rle == NULL) + return; + + if (rle->res) { + if (rman_get_device(rle->res) != dev || + rman_get_flags(rle->res) & RF_ACTIVE) { + device_printf(dev, "delete_resource: " + "Resource still owned by child, oops. " + "(type=%d, rid=%d, addr=%lx)\n", + rle->type, rle->rid, + rman_get_start(rle->res)); + return; } - resource_list_delete(rl, type, rid); + + /* + * If this is a BAR, clear the BAR so it stops + * decoding before releasing the resource. + */ + switch (type) { + case SYS_RES_IOPORT: + case SYS_RES_MEMORY: + /* XXX: 64-bit BARs? */ + pci_write_config(child, rid, 0, 4); + break; + } + bus_release_resource(dev, type, rid, rle->res); } - /* - * Why do we turn off the PCI configuration BAR when we delete a - * resource? -- imp - */ - pci_write_config(child, rid, 0, 4); - BUS_DELETE_RESOURCE(device_get_parent(dev), child, type, rid); + resource_list_delete(rl, type, rid); } struct resource_list * diff --git a/sys/dev/pci/pci_private.h b/sys/dev/pci/pci_private.h index f7ccc19..e45afb7 100644 --- a/sys/dev/pci/pci_private.h +++ b/sys/dev/pci/pci_private.h @@ -82,6 +82,10 @@ int pci_msix_count_method(device_t dev, device_t child); struct resource *pci_alloc_resource(device_t dev, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags); +int pci_release_resource(device_t dev, device_t child, int type, + int rid, struct resource *r); +int pci_activate_resource(device_t dev, device_t child, int type, + int rid, struct resource *r); void pci_delete_resource(device_t dev, device_t child, int type, int rid); struct resource_list *pci_get_resource_list (device_t dev, device_t child); |