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:
parent
e8d0638773
commit
d8c34e18a3
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user