diff options
| author | John Baldwin <jhb@FreeBSD.org> | 2007-01-22 21:48:44 +0000 |
|---|---|---|
| committer | John Baldwin <jhb@FreeBSD.org> | 2007-01-22 21:48:44 +0000 |
| commit | 5fe82bca57a1dbad1538d277602bb047c78a73fc (patch) | |
| tree | e54f3042911f50266b958edf09a9fe2525a0987a /sys/dev/pci | |
| parent | 7c32173ba888e963a1d352e14915f5527915a388 (diff) | |
Notes
Diffstat (limited to 'sys/dev/pci')
| -rw-r--r-- | sys/dev/pci/pci.c | 170 | ||||
| -rw-r--r-- | sys/dev/pci/pci_if.m | 17 | ||||
| -rw-r--r-- | sys/dev/pci/pci_pci.c | 11 | ||||
| -rw-r--r-- | sys/dev/pci/pci_private.h | 4 | ||||
| -rw-r--r-- | sys/dev/pci/pcib_if.m | 10 | ||||
| -rw-r--r-- | sys/dev/pci/pcib_private.h | 1 | ||||
| -rw-r--r-- | sys/dev/pci/pcivar.h | 18 |
7 files changed, 204 insertions, 27 deletions
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index dc930c1aa5c4..7ef07e2ced8f 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -101,6 +101,7 @@ static void pci_write_vpd_reg(device_t pcib, pcicfgregs *cfg, int reg, uint32_t data); #endif static void pci_read_vpd(device_t pcib, pcicfgregs *cfg); +static int pci_msi_blacklisted(void); static device_method_t pci_methods[] = { /* Device interface */ @@ -145,8 +146,11 @@ static device_method_t pci_methods[] = { DEVMETHOD(pci_assign_interrupt, pci_assign_interrupt_method), DEVMETHOD(pci_find_extcap, pci_find_extcap_method), DEVMETHOD(pci_alloc_msi, pci_alloc_msi_method), + DEVMETHOD(pci_alloc_msix, pci_alloc_msix_method), + DEVMETHOD(pci_remap_msix, pci_remap_msix_method), DEVMETHOD(pci_release_msi, pci_release_msi_method), DEVMETHOD(pci_msi_count, pci_msi_count_method), + DEVMETHOD(pci_msix_count, pci_msix_count_method), { 0, 0 } }; @@ -1024,14 +1028,36 @@ pci_pending_msix(device_t dev, u_int index) return (bus_read_4(cfg->msix.msix_pba_res, offset) & bit); } -static int -pci_alloc_msix(device_t dev, device_t child, int *count) +/* + * Attempt to allocate *count MSI-X messages. The actual number allocated is + * returned in *count. After this function returns, each message will be + * available to the driver as SYS_RES_IRQ resources starting at rid 1. + */ +int +pci_alloc_msix_method(device_t dev, device_t child, int *count) { struct pci_devinfo *dinfo = device_get_ivars(child); pcicfgregs *cfg = &dinfo->cfg; struct resource_list_entry *rle; int actual, error, i, irq, max; + /* Don't let count == 0 get us into trouble. */ + if (*count == 0) + return (EINVAL); + + /* If rid 0 is allocated, then fail. */ + rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, 0); + if (rle != NULL && rle->res != NULL) + return (ENXIO); + + /* Already have allocated messages? */ + if (cfg->msi.msi_alloc != 0 || cfg->msix.msix_alloc != 0) + return (ENXIO); + + /* If MSI is blacklisted for this system, fail. */ + if (pci_msi_blacklisted()) + return (ENXIO); + /* MSI-X capability present? */ if (cfg->msix.msix_location == 0 || !pci_do_msix) return (ENODEV); @@ -1052,10 +1078,6 @@ pci_alloc_msix(device_t dev, device_t child, int *count) } cfg->msix.msix_pba_res = rle->res; - /* Already have allocated messages? */ - if (cfg->msix.msix_alloc != 0) - return (ENXIO); - if (bootverbose) device_printf(child, "attempting to allocate %d MSI-X vectors (%d supported)\n", @@ -1132,24 +1154,105 @@ pci_alloc_msix(device_t dev, device_t child, int *count) return (0); } +/* + * By default, pci_alloc_msix() will assign the allocated IRQ resources to + * the first N messages in the MSI-X table. However, device drivers may + * want to use different layouts in the case that they do not allocate a + * full table. This method allows the driver to specify what layout it + * wants. It must be called after a successful pci_alloc_msix() but + * before any of the associated SYS_RES_IRQ resources are allocated via + * bus_alloc_resource(). The 'indices' array contains N (where N equals + * the 'count' returned from pci_alloc_msix()) message indices. The + * indices are 1-based (meaning the first message is at index 1). On + * successful return, each of the messages in the 'indices' array will + * have an associated SYS_RES_IRQ whose rid is equal to the index. Thus, + * if indices contains { 2, 4 }, then upon successful return, the 'child' + * device will have two SYS_RES_IRQ resources available at rids 2 and 4. + */ +int +pci_remap_msix_method(device_t dev, device_t child, u_int *indices) +{ + struct pci_devinfo *dinfo = device_get_ivars(child); + pcicfgregs *cfg = &dinfo->cfg; + struct resource_list_entry *rle; + int count, error, i, j, *irqs; + + /* Sanity check the indices. */ + for (i = 0; i < cfg->msix.msix_alloc; i++) + if (indices[i] == 0 || indices[i] > cfg->msix.msix_msgnum) + return (EINVAL); + + /* Check for duplicates. */ + for (i = 0; i < cfg->msix.msix_alloc; i++) + for (j = i + 1; j < cfg->msix.msix_alloc; j++) + if (indices[i] == indices[j]) + return (EINVAL); + + /* Make sure none of the resources are allocated. */ + for (i = 1, count = 0; count < cfg->msix.msix_alloc; i++) { + rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, i); + if (rle == NULL) + continue; + if (rle->res != NULL) + return (EBUSY); + count++; + } + + /* Save the IRQ values and free the existing resources. */ + irqs = malloc(sizeof(int) * cfg->msix.msix_alloc, M_TEMP, M_WAITOK); + for (i = 1, count = 0; count < cfg->msix.msix_alloc; i++) { + rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, i); + if (rle == NULL) + continue; + irqs[count] = rle->start; + resource_list_delete(&dinfo->resources, SYS_RES_IRQ, i); + count++; + } + + /* Map the IRQ values to the new message indices and rids. */ + for (i = 0; i < cfg->msix.msix_alloc; i++) { + resource_list_add(&dinfo->resources, SYS_RES_IRQ, indices[i], + irqs[i], irqs[i], 1); + error = PCIB_REMAP_MSIX(device_get_parent(dev), child, + indices[i], irqs[i]); + KASSERT(error == 0, ("Failed to remap MSI-X message")); + } + if (bootverbose) { + if (cfg->msix.msix_alloc == 1) + device_printf(child, + "Remapped MSI-X IRQ to index %d\n", indices[0]); + else { + device_printf(child, "Remapped MSI-X IRQs to indices"); + for (i = 0; i < cfg->msix.msix_alloc - 1; i++) + printf(" %d,", indices[i]); + printf(" %d\n", indices[cfg->msix.msix_alloc - 1]); + } + } + free(irqs, M_TEMP); + + return (0); +} + static int pci_release_msix(device_t dev, device_t child) { struct pci_devinfo *dinfo = device_get_ivars(child); pcicfgregs *cfg = &dinfo->cfg; struct resource_list_entry *rle; - int i; + int count, i; /* Do we have any messages to release? */ if (cfg->msix.msix_alloc == 0) return (ENODEV); /* Make sure none of the resources are allocated. */ - for (i = 0; i < cfg->msix.msix_alloc; i++) { - rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, i + 1); - KASSERT(rle != NULL, ("missing MSI resource")); + for (i = 1, count = 0; count < cfg->msix.msix_alloc; i++) { + rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, i); + if (rle == NULL) + continue; if (rle->res != NULL) return (EBUSY); + count++; } /* Update control register with to disable MSI-X. */ @@ -1158,11 +1261,14 @@ pci_release_msix(device_t dev, device_t child) cfg->msix.msix_ctrl, 2); /* Release the messages. */ - for (i = 0; i < cfg->msix.msix_alloc; i++) { - rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, i + 1); + for (i = 1, count = 0; count < cfg->msix.msix_alloc; i++) { + rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, i); + if (rle == NULL) + continue; PCIB_RELEASE_MSIX(device_get_parent(dev), child, rle->start); - resource_list_delete(&dinfo->resources, SYS_RES_IRQ, i + 1); + resource_list_delete(&dinfo->resources, SYS_RES_IRQ, i); + count++; } /* Update alloc count. */ @@ -1171,6 +1277,23 @@ pci_release_msix(device_t dev, device_t child) } /* + * Return the max supported MSI-X messages this device supports. + * Basically, assuming the MD code can alloc messages, this function + * should return the maximum value that pci_alloc_msix() can return. + * Thus, it is subject to the tunables, etc. + */ +int +pci_msix_count_method(device_t dev, device_t child) +{ + struct pci_devinfo *dinfo = device_get_ivars(child); + pcicfgregs *cfg = &dinfo->cfg; + + if (pci_do_msix && cfg->msix.msix_location != 0) + return (cfg->msix.msix_msgnum); + return (0); +} + +/* * Support for MSI message signalled interrupts. */ void @@ -1294,23 +1417,18 @@ pci_alloc_msi_method(device_t dev, device_t child, int *count) if (rle != NULL && rle->res != NULL) return (ENXIO); + /* Already have allocated messages? */ + if (cfg->msi.msi_alloc != 0 || cfg->msix.msix_alloc != 0) + return (ENXIO); + /* If MSI is blacklisted for this system, fail. */ if (pci_msi_blacklisted()) return (ENXIO); - /* Try MSI-X first. */ - error = pci_alloc_msix(dev, child, count); - if (error != ENODEV) - return (error); - /* MSI capability present? */ if (cfg->msi.msi_location == 0 || !pci_do_msi) return (ENODEV); - /* Already have allocated messages? */ - if (cfg->msi.msi_alloc != 0) - return (ENXIO); - if (bootverbose) device_printf(child, "attempting to allocate %d MSI vectors (%d supported)\n", @@ -1444,10 +1562,10 @@ pci_release_msi_method(device_t dev, device_t child) } /* - * Return the max supported MSI or MSI-X messages this device supports. + * Return the max supported MSI messages this device supports. * Basically, assuming the MD code can alloc messages, this function - * should return the maximum value that pci_alloc_msi() can return. Thus, - * it is subject to the tunables, etc. + * should return the maximum value that pci_alloc_msi() can return. + * Thus, it is subject to the tunables, etc. */ int pci_msi_count_method(device_t dev, device_t child) @@ -1455,8 +1573,6 @@ pci_msi_count_method(device_t dev, device_t child) struct pci_devinfo *dinfo = device_get_ivars(child); pcicfgregs *cfg = &dinfo->cfg; - if (pci_do_msix && cfg->msix.msix_location != 0) - return (cfg->msix.msix_msgnum); if (pci_do_msi && cfg->msi.msi_location != 0) return (cfg->msi.msi_msgnum); return (0); diff --git a/sys/dev/pci/pci_if.m b/sys/dev/pci/pci_if.m index ba6041520b01..266ec34d10f9 100644 --- a/sys/dev/pci/pci_if.m +++ b/sys/dev/pci/pci_if.m @@ -118,6 +118,18 @@ METHOD int alloc_msi { int *count; }; +METHOD int alloc_msix { + device_t dev; + device_t child; + int *count; +}; + +METHOD int remap_msix { + device_t dev; + device_t child; + u_int *indices; +}; + METHOD int release_msi { device_t dev; device_t child; @@ -127,3 +139,8 @@ METHOD int msi_count { device_t dev; device_t child; } DEFAULT null_msi_count; + +METHOD int msix_count { + device_t dev; + device_t child; +} DEFAULT null_msi_count; diff --git a/sys/dev/pci/pci_pci.c b/sys/dev/pci/pci_pci.c index 8accc42033cb..6e947981d8b1 100644 --- a/sys/dev/pci/pci_pci.c +++ b/sys/dev/pci/pci_pci.c @@ -82,6 +82,7 @@ static device_method_t pcib_methods[] = { DEVMETHOD(pcib_alloc_msi, pcib_alloc_msi), DEVMETHOD(pcib_release_msi, pcib_release_msi), DEVMETHOD(pcib_alloc_msix, pcib_alloc_msix), + DEVMETHOD(pcib_remap_msix, pcib_remap_msix), DEVMETHOD(pcib_release_msix, pcib_release_msix), { 0, 0 } @@ -583,6 +584,16 @@ pcib_alloc_msix(device_t pcib, device_t dev, int index, int *irq) return (PCIB_ALLOC_MSIX(device_get_parent(bus), dev, index, irq)); } +/* Pass request to remap an MSI-X message up to the parent bridge. */ +int +pcib_remap_msix(device_t pcib, device_t dev, int index, int irq) +{ + device_t bus; + + bus = device_get_parent(pcib); + return (PCIB_REMAP_MSIX(device_get_parent(bus), dev, index, irq)); +} + /* Pass request to release an MSI-X message up to the parent bridge. */ int pcib_release_msix(device_t pcib, device_t dev, int irq) diff --git a/sys/dev/pci/pci_private.h b/sys/dev/pci/pci_private.h index 9aea42d4bae4..32be4aa59fa8 100644 --- a/sys/dev/pci/pci_private.h +++ b/sys/dev/pci/pci_private.h @@ -67,8 +67,12 @@ int pci_disable_io_method(device_t dev, device_t child, int space); int pci_find_extcap_method(device_t dev, device_t child, int capability, int *capreg); int pci_alloc_msi_method(device_t dev, device_t child, int *count); +int pci_alloc_msix_method(device_t dev, device_t child, int *count); +int pci_remap_msix_method(device_t dev, device_t child, + u_int *indices); int pci_release_msi_method(device_t dev, device_t child); int pci_msi_count_method(device_t dev, device_t child); +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); diff --git a/sys/dev/pci/pcib_if.m b/sys/dev/pci/pcib_if.m index 0e12c0038504..18755faf6539 100644 --- a/sys/dev/pci/pcib_if.m +++ b/sys/dev/pci/pcib_if.m @@ -126,6 +126,16 @@ METHOD int alloc_msix { }; # +# Remap a single MSI-X message to a different index. +# +METHOD int remap_msix { + device_t pcib; + device_t dev; + int index; + int irq; +}; + +# # Release a single MSI-X message mapped onto 'irq'. # METHOD int release_msix { diff --git a/sys/dev/pci/pcib_private.h b/sys/dev/pci/pcib_private.h index 9575df2b22db..a571578e1155 100644 --- a/sys/dev/pci/pcib_private.h +++ b/sys/dev/pci/pcib_private.h @@ -78,6 +78,7 @@ int pcib_route_interrupt(device_t pcib, device_t dev, int pin); int pcib_alloc_msi(device_t pcib, device_t dev, int count, int maxcount, int *irqs); int pcib_release_msi(device_t pcib, device_t dev, int count, int *irqs); int pcib_alloc_msix(device_t pcib, device_t dev, int index, int *irq); +int pcib_remap_msix(device_t pcib, device_t dev, int index, int irq); int pcib_release_msix(device_t pcib, device_t dev, int irq); #endif diff --git a/sys/dev/pci/pcivar.h b/sys/dev/pci/pcivar.h index eca13efaeb1f..a0757454c14c 100644 --- a/sys/dev/pci/pcivar.h +++ b/sys/dev/pci/pcivar.h @@ -396,6 +396,18 @@ pci_alloc_msi(device_t dev, int *count) } static __inline int +pci_alloc_msix(device_t dev, int *count) +{ + return (PCI_ALLOC_MSIX(device_get_parent(dev), dev, count)); +} + +static __inline int +pci_remap_msix(device_t dev, u_int *indices) +{ + return (PCI_REMAP_MSIX(device_get_parent(dev), dev, indices)); +} + +static __inline int pci_release_msi(device_t dev) { return (PCI_RELEASE_MSI(device_get_parent(dev), dev)); @@ -407,6 +419,12 @@ pci_msi_count(device_t dev) return (PCI_MSI_COUNT(device_get_parent(dev), dev)); } +static __inline int +pci_msix_count(device_t dev) +{ + return (PCI_MSIX_COUNT(device_get_parent(dev), dev)); +} + device_t pci_find_bsf(uint8_t, uint8_t, uint8_t); device_t pci_find_device(uint16_t, uint16_t); |
