Index: head/sys/dev/ahci/ahci.h =================================================================== --- head/sys/dev/ahci/ahci.h +++ head/sys/dev/ahci/ahci.h @@ -482,11 +482,15 @@ device_t dev; bus_dma_tag_t dma_tag; int r_rid; + int r_msix_tab_rid; + int r_msix_pba_rid; uint16_t vendorid; /* Vendor ID from the bus */ uint16_t deviceid; /* Device ID from the bus */ uint16_t subvendorid; /* Subvendor ID from the bus */ uint16_t subdeviceid; /* Subdevice ID from the bus */ struct resource *r_mem; + struct resource *r_msix_table; + struct resource *r_msix_pba; struct rman sc_iomem; struct ahci_controller_irq { struct ahci_controller *ctlr; @@ -621,3 +625,4 @@ bus_dma_tag_t ahci_get_dma_tag(device_t dev, device_t child); int ahci_ctlr_reset(device_t dev); int ahci_ctlr_setup(device_t dev); +void ahci_free_mem(device_t dev); Index: head/sys/dev/ahci/ahci.c =================================================================== --- head/sys/dev/ahci/ahci.c +++ head/sys/dev/ahci/ahci.c @@ -181,12 +181,12 @@ ctlr->sc_iomem.rm_type = RMAN_ARRAY; ctlr->sc_iomem.rm_descr = "I/O memory addresses"; if ((error = rman_init(&ctlr->sc_iomem)) != 0) { - bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); + ahci_free_mem(dev); return (error); } if ((error = rman_manage_region(&ctlr->sc_iomem, rman_get_start(ctlr->r_mem), rman_get_end(ctlr->r_mem))) != 0) { - bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); + ahci_free_mem(dev); rman_fini(&ctlr->sc_iomem); return (error); } @@ -250,8 +250,7 @@ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE, BUS_SPACE_UNRESTRICTED, BUS_SPACE_MAXSIZE, 0, NULL, NULL, &ctlr->dma_tag)) { - bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, - ctlr->r_mem); + ahci_free_mem(dev); rman_fini(&ctlr->sc_iomem); return (ENXIO); } @@ -261,8 +260,7 @@ /* Setup interrupts. */ if ((error = ahci_setup_interrupt(dev)) != 0) { bus_dma_tag_destroy(ctlr->dma_tag); - bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, - ctlr->r_mem); + ahci_free_mem(dev); rman_fini(&ctlr->sc_iomem); return (error); } @@ -367,9 +365,26 @@ bus_dma_tag_destroy(ctlr->dma_tag); /* Free memory. */ rman_fini(&ctlr->sc_iomem); + ahci_free_mem(dev); + return (0); +} + +void +ahci_free_mem(device_t dev) +{ + struct ahci_controller *ctlr = device_get_softc(dev); + + /* Release memory resources */ if (ctlr->r_mem) bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); - return (0); + if (ctlr->r_msix_table) + bus_release_resource(dev, SYS_RES_MEMORY, + ctlr->r_msix_tab_rid, ctlr->r_msix_table); + if (ctlr->r_msix_pba) + bus_release_resource(dev, SYS_RES_MEMORY, + ctlr->r_msix_pba_rid, ctlr->r_msix_pba); + + ctlr->r_msix_pba = ctlr->r_mem = ctlr->r_msix_table = NULL; } int Index: head/sys/dev/ahci/ahci_pci.c =================================================================== --- head/sys/dev/ahci/ahci_pci.c +++ head/sys/dev/ahci/ahci_pci.c @@ -374,12 +374,39 @@ } static int +ahci_pci_read_msix_bars(device_t dev, uint8_t *table_bar, uint8_t *pba_bar) +{ + int cap_offset = 0, ret; + uint32_t val; + + if ((table_bar == NULL) || (pba_bar == NULL)) + return (EINVAL); + + ret = pci_find_cap(dev, PCIY_MSIX, &cap_offset); + if (ret != 0) + return (EINVAL); + + val = pci_read_config(dev, cap_offset + PCIR_MSIX_TABLE, 4); + *table_bar = PCIR_BAR(val & PCIM_MSIX_BIR_MASK); + + val = pci_read_config(dev, cap_offset + PCIR_MSIX_PBA, 4); + *pba_bar = PCIR_BAR(val & PCIM_MSIX_BIR_MASK); + + return (0); +} + +static int ahci_pci_attach(device_t dev) { struct ahci_controller *ctlr = device_get_softc(dev); int error, i; uint32_t devid = pci_get_devid(dev); uint8_t revid = pci_get_revid(dev); + int msi_count, msix_count; + uint8_t table_bar = 0, pba_bar = 0; + + msi_count = pci_msi_count(dev); + msix_count = pci_msix_count(dev); i = 0; while (ahci_ids[i].id != 0 && @@ -406,10 +433,57 @@ if (!(ctlr->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &ctlr->r_rid, RF_ACTIVE))) return ENXIO; + + /* Read MSI-x BAR IDs if supported */ + if (msix_count > 0) { + error = ahci_pci_read_msix_bars(dev, &table_bar, &pba_bar); + if (error == 0) { + ctlr->r_msix_tab_rid = table_bar; + ctlr->r_msix_pba_rid = pba_bar; + } else { + /* Failed to read BARs, disable MSI-x */ + msix_count = 0; + } + } + + /* Allocate resources for MSI-x table and PBA */ + if (msix_count > 0) { + /* + * Allocate new MSI-x table only if not + * allocated before. + */ + ctlr->r_msix_table = NULL; + if (ctlr->r_msix_tab_rid != ctlr->r_rid) { + /* Separate BAR for MSI-x */ + ctlr->r_msix_table = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &ctlr->r_msix_tab_rid, RF_ACTIVE); + if (ctlr->r_msix_table == NULL) { + ahci_free_mem(dev); + return (ENXIO); + } + } + + /* + * Allocate new PBA table only if not + * allocated before. + */ + ctlr->r_msix_pba = NULL; + if ((ctlr->r_msix_pba_rid != ctlr->r_msix_tab_rid) && + (ctlr->r_msix_pba_rid != ctlr->r_rid)) { + /* Separate BAR for PBA */ + ctlr->r_msix_pba = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &ctlr->r_msix_pba_rid, RF_ACTIVE); + if (ctlr->r_msix_pba == NULL) { + ahci_free_mem(dev); + return (ENXIO); + } + } + } + pci_enable_busmaster(dev); /* Reset controller */ if ((error = ahci_pci_ctlr_reset(dev)) != 0) { - bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, ctlr->r_mem); + ahci_free_mem(dev); return (error); }; @@ -426,24 +500,51 @@ resource_int_value(device_get_name(dev), device_get_unit(dev), "msi", &ctlr->msi); ctlr->numirqs = 1; + if (msi_count == 0 && msix_count == 0) + ctlr->msi = 0; if (ctlr->msi < 0) ctlr->msi = 0; - else if (ctlr->msi == 1) - ctlr->msi = min(1, pci_msi_count(dev)); - else if (ctlr->msi > 1) { + else if (ctlr->msi == 1) { + msi_count = min(1, msi_count); + msix_count = min(1, msix_count); + } else if (ctlr->msi > 1) ctlr->msi = 2; - ctlr->numirqs = pci_msi_count(dev); - } - /* Allocate MSI if needed/present. */ - if (ctlr->msi && pci_alloc_msi(dev, &ctlr->numirqs) != 0) { - ctlr->msi = 0; - ctlr->numirqs = 1; + + /* Allocate MSI/MSI-x if needed/present. */ + if (ctlr->msi > 0) { + error = ENXIO; + + /* Try to allocate MSI-x first */ + if (msix_count > 0) { + error = pci_alloc_msix(dev, &msix_count); + if (error == 0) + ctlr->numirqs = msix_count; + } + + /* + * Try to allocate MSI if msi_count is greater than 0 + * and if MSI-x allocation failed. + */ + if ((error != 0) && (msi_count > 0)) { + error = pci_alloc_msi(dev, &msi_count); + if (error == 0) + ctlr->numirqs = msi_count; + } + + /* Both MSI and MSI-x allocations failed */ + if (error != 0) { + ctlr->msi = 0; + device_printf(dev, "Failed to allocate MSI/MSI-x, " + "falling back to INTx\n"); + } } error = ahci_attach(dev); - if (error != 0) - if (ctlr->msi) + if (error != 0) { + if (ctlr->msi > 0) pci_release_msi(dev); + ahci_free_mem(dev); + } return error; }