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),
return (0);
}
-/*
- * By default, pci_alloc_msix() will assign the allocated IRQ
- * resources consecutively to the first N messages in the MSI-X table.
- * However, device drivers may want to use different layouts if they
- * either receive fewer messages than they asked for, or they wish to
- * populate the MSI-X table sparsely. 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 'vectors' array contains 'count' message vectors. The array
- * maps directly to the MSI-X table in that index 0 in the array
- * specifies the vector for the first message in the MSI-X table, etc.
- * The vector value in each array index can either be 0 to indicate
- * that no vector should be assigned to a message slot, or it can be a
- * number from 1 to N (where N is the count returned from a
- * succcessful call to pci_alloc_msix()) to indicate which message
- * vector (IRQ) to be used for the corresponding message.
- *
- * On successful return, each message with a non-zero vector will have
- * an associated SYS_RES_IRQ whose rid is equal to the array index +
- * 1. Additionally, if any of the IRQs allocated via the previous
- * call to pci_alloc_msix() are not used in the mapping, those IRQs
- * will be kfreed back to the system automatically.
- *
- * For example, suppose a driver has a MSI-X table with 6 messages and
- * asks for 6 messages, but pci_alloc_msix() only returns a count of
- * 3. Call the three vectors allocated by pci_alloc_msix() A, B, and
- * C. After the call to pci_alloc_msix(), the device will be setup to
- * have an MSI-X table of ABC--- (where - means no vector assigned).
- * If the driver ten passes a vector array of { 1, 0, 1, 2, 0, 2 },
- * then the MSI-X table will look like A-AB-B, and the 'C' vector will
- * be kfreed back to the system. This device will also have valid
- * SYS_RES_IRQ rids of 1, 3, 4, and 6.
- *
- * In any case, the SYS_RES_IRQ rid X will always map to the message
- * at MSI-X table index X - 1 and will only be valid if a vector is
- * assigned to that table entry.
- */
-int
-pci_remap_msix_method(device_t dev, device_t child, int count,
- const u_int *vectors)
-{
- struct pci_devinfo *dinfo = device_get_ivars(child);
- struct pcicfg_msix *msix = &dinfo->cfg.msix;
- struct resource_list_entry *rle;
- int i, irq, j, *used;
-
- /*
- * Have to have at least one message in the table but the
- * table can't be bigger than the actual MSI-X table in the
- * device.
- */
- if (count == 0 || count > msix->msix_msgnum)
- return (EINVAL);
-
- /* Sanity check the vectors. */
- for (i = 0; i < count; i++)
- if (vectors[i] > msix->msix_alloc)
- return (EINVAL);
-
- /*
- * Make sure there aren't any holes in the vectors to be used.
- * It's a big pain to support it, and it doesn't really make
- * sense anyway. Also, at least one vector must be used.
- */
- used = kmalloc(sizeof(int) * msix->msix_alloc, M_DEVBUF, M_WAITOK |
- M_ZERO);
- for (i = 0; i < count; i++)
- if (vectors[i] != 0)
- used[vectors[i] - 1] = 1;
- for (i = 0; i < msix->msix_alloc - 1; i++)
- if (used[i] == 0 && used[i + 1] == 1) {
- kfree(used, M_DEVBUF);
- return (EINVAL);
- }
- if (used[0] != 1) {
- kfree(used, M_DEVBUF);
- return (EINVAL);
- }
-
- /* Make sure none of the resources are allocated. */
- for (i = 0; i < msix->msix_table_len; i++) {
- if (msix->msix_table[i].mte_vector == 0)
- continue;
- if (msix->msix_table[i].mte_handlers > 0)
- return (EBUSY);
- rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ, i + 1);
- KASSERT(rle != NULL, ("missing resource"));
- if (rle->res != NULL)
- return (EBUSY);
- }
-
- /* Free the existing resource list entries. */
- for (i = 0; i < msix->msix_table_len; i++) {
- if (msix->msix_table[i].mte_vector == 0)
- continue;
- resource_list_delete(&dinfo->resources, SYS_RES_IRQ, i + 1);
- }
-
- /*
- * Build the new virtual table keeping track of which vectors are
- * used.
- */
- kfree(msix->msix_table, M_DEVBUF);
- msix->msix_table = kmalloc(sizeof(struct msix_table_entry) * count,
- M_DEVBUF, M_WAITOK | M_ZERO);
- for (i = 0; i < count; i++)
- msix->msix_table[i].mte_vector = vectors[i];
- msix->msix_table_len = count;
-
- /* Free any unused IRQs and resize the vectors array if necessary. */
- j = msix->msix_alloc - 1;
- if (used[j] == 0) {
- struct msix_vector *vec;
-
- while (used[j] == 0) {
- PCIB_RELEASE_MSIX(device_get_parent(dev), child,
- msix->msix_vectors[j].mv_irq);
- j--;
- }
- vec = kmalloc(sizeof(struct msix_vector) * (j + 1), M_DEVBUF,
- M_WAITOK);
- bcopy(msix->msix_vectors, vec, sizeof(struct msix_vector) *
- (j + 1));
- kfree(msix->msix_vectors, M_DEVBUF);
- msix->msix_vectors = vec;
- msix->msix_alloc = j + 1;
- }
- kfree(used, M_DEVBUF);
-
- /* Map the IRQs onto the rids. */
- for (i = 0; i < count; i++) {
- if (vectors[i] == 0)
- continue;
- irq = msix->msix_vectors[vectors[i]].mv_irq;
- resource_list_add(&dinfo->resources, SYS_RES_IRQ, i + 1, irq,
- irq, 1, -1);
- }
-
- if (bootverbose) {
- device_printf(child, "Remapped MSI-X IRQs as: ");
- for (i = 0; i < count; i++) {
- if (i != 0)
- kprintf(", ");
- if (vectors[i] == 0)
- kprintf("---");
- else
- kprintf("%d",
- msix->msix_vectors[vectors[i]].mv_irq);
- }
- kprintf("\n");
- }
-
- return (0);
-}
-
static int
pci_release_msix(device_t dev, device_t child)
{
2);
}
-int
-pci_remap_msi_irq(device_t dev, u_int irq)
-{
- struct pci_devinfo *dinfo = device_get_ivars(dev);
- pcicfgregs *cfg = &dinfo->cfg;
- struct resource_list_entry *rle;
- struct msix_table_entry *mte;
- struct msix_vector *mv;
- device_t bus;
- uint64_t addr;
- uint32_t data;
- int error, i, j;
-
- bus = device_get_parent(dev);
-
- /*
- * Handle MSI first. We try to find this IRQ among our list
- * of MSI IRQs. If we find it, we request updated address and
- * data registers and apply the results.
- */
- if (cfg->msi.msi_alloc > 0) {
-
- /* If we don't have any active handlers, nothing to do. */
- if (cfg->msi.msi_handlers == 0)
- return (0);
- for (i = 0; i < cfg->msi.msi_alloc; i++) {
- rle = resource_list_find(&dinfo->resources, SYS_RES_IRQ,
- i + 1);
- if (rle->start == irq) {
- error = PCIB_MAP_MSI(device_get_parent(bus),
- dev, irq, &addr, &data, -1 /* XXX */);
- if (error)
- return (error);
- pci_disable_msi(dev);
- dinfo->cfg.msi.msi_addr = addr;
- dinfo->cfg.msi.msi_data = data;
- pci_enable_msi(dev, addr, data);
- return (0);
- }
- }
- return (ENOENT);
- }
-
- /*
- * For MSI-X, we check to see if we have this IRQ. If we do,
- * we request the updated mapping info. If that works, we go
- * through all the slots that use this IRQ and update them.
- */
- if (cfg->msix.msix_alloc > 0) {
- for (i = 0; i < cfg->msix.msix_alloc; i++) {
- mv = &cfg->msix.msix_vectors[i];
- if (mv->mv_irq == irq) {
- error = PCIB_MAP_MSI(device_get_parent(bus),
- dev, irq, &addr, &data, -1 /* XXX */);
- if (error)
- return (error);
- mv->mv_address = addr;
- mv->mv_data = data;
- for (j = 0; j < cfg->msix.msix_table_len; j++) {
- mte = &cfg->msix.msix_table[j];
- if (mte->mte_vector != i + 1)
- continue;
- if (mte->mte_handlers == 0)
- continue;
- pci_mask_msix(dev, j);
- pci_enable_msix(dev, j, addr, data);
- pci_unmask_msix(dev, j);
- }
- }
- }
- return (ENOENT);
- }
-
- return (ENOENT);
-}
-
/*
* Returns true if the specified device is blacklisted because MSI
* doesn't work.