Introduce support for MSI-X interrupts in AHCI

- Allocate resources for MSI-X table and PBA if necessary
- Add function ahci_free_mem() to free all resources

Reviewed by:   jhb, mav
Obtained from: Semihalf
Sponsored by:  The FreeBSD Foundation
Differential Revision: https://reviews.freebsd.org/D3009
This commit is contained in:
Zbigniew Bodek 2015-07-22 09:46:22 +00:00
parent f260c1b939
commit 0af6011a92
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=285789
3 changed files with 140 additions and 19 deletions

View File

@ -181,12 +181,12 @@ ahci_attach(device_t dev)
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 @@ ahci_attach(device_t dev)
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 @@ ahci_attach(device_t dev)
/* 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 @@ ahci_detach(device_t dev)
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

View File

@ -482,11 +482,15 @@ struct ahci_controller {
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 @@ int ahci_child_location_str(device_t dev, device_t child, char *buf,
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);

View File

@ -373,6 +373,28 @@ ahci_ata_probe(device_t dev)
return (BUS_PROBE_DEFAULT);
}
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)
{
@ -380,6 +402,11 @@ ahci_pci_attach(device_t 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 @@ ahci_pci_attach(device_t dev)
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 @@ ahci_pci_attach(device_t dev)
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;
}