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
f260c1b939
commit
0af6011a92
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=285789
@ -181,12 +181,12 @@ ahci_attach(device_t dev)
|
|||||||
ctlr->sc_iomem.rm_type = RMAN_ARRAY;
|
ctlr->sc_iomem.rm_type = RMAN_ARRAY;
|
||||||
ctlr->sc_iomem.rm_descr = "I/O memory addresses";
|
ctlr->sc_iomem.rm_descr = "I/O memory addresses";
|
||||||
if ((error = rman_init(&ctlr->sc_iomem)) != 0) {
|
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);
|
return (error);
|
||||||
}
|
}
|
||||||
if ((error = rman_manage_region(&ctlr->sc_iomem,
|
if ((error = rman_manage_region(&ctlr->sc_iomem,
|
||||||
rman_get_start(ctlr->r_mem), rman_get_end(ctlr->r_mem))) != 0) {
|
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);
|
rman_fini(&ctlr->sc_iomem);
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
@ -250,8 +250,7 @@ ahci_attach(device_t dev)
|
|||||||
BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
|
BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
|
||||||
BUS_SPACE_MAXSIZE, BUS_SPACE_UNRESTRICTED, BUS_SPACE_MAXSIZE,
|
BUS_SPACE_MAXSIZE, BUS_SPACE_UNRESTRICTED, BUS_SPACE_MAXSIZE,
|
||||||
0, NULL, NULL, &ctlr->dma_tag)) {
|
0, NULL, NULL, &ctlr->dma_tag)) {
|
||||||
bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid,
|
ahci_free_mem(dev);
|
||||||
ctlr->r_mem);
|
|
||||||
rman_fini(&ctlr->sc_iomem);
|
rman_fini(&ctlr->sc_iomem);
|
||||||
return (ENXIO);
|
return (ENXIO);
|
||||||
}
|
}
|
||||||
@ -261,8 +260,7 @@ ahci_attach(device_t dev)
|
|||||||
/* Setup interrupts. */
|
/* Setup interrupts. */
|
||||||
if ((error = ahci_setup_interrupt(dev)) != 0) {
|
if ((error = ahci_setup_interrupt(dev)) != 0) {
|
||||||
bus_dma_tag_destroy(ctlr->dma_tag);
|
bus_dma_tag_destroy(ctlr->dma_tag);
|
||||||
bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid,
|
ahci_free_mem(dev);
|
||||||
ctlr->r_mem);
|
|
||||||
rman_fini(&ctlr->sc_iomem);
|
rman_fini(&ctlr->sc_iomem);
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
@ -367,9 +365,26 @@ ahci_detach(device_t dev)
|
|||||||
bus_dma_tag_destroy(ctlr->dma_tag);
|
bus_dma_tag_destroy(ctlr->dma_tag);
|
||||||
/* Free memory. */
|
/* Free memory. */
|
||||||
rman_fini(&ctlr->sc_iomem);
|
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)
|
if (ctlr->r_mem)
|
||||||
bus_release_resource(dev, SYS_RES_MEMORY, ctlr->r_rid, 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
|
int
|
||||||
|
@ -482,11 +482,15 @@ struct ahci_controller {
|
|||||||
device_t dev;
|
device_t dev;
|
||||||
bus_dma_tag_t dma_tag;
|
bus_dma_tag_t dma_tag;
|
||||||
int r_rid;
|
int r_rid;
|
||||||
|
int r_msix_tab_rid;
|
||||||
|
int r_msix_pba_rid;
|
||||||
uint16_t vendorid; /* Vendor ID from the bus */
|
uint16_t vendorid; /* Vendor ID from the bus */
|
||||||
uint16_t deviceid; /* Device ID from the bus */
|
uint16_t deviceid; /* Device ID from the bus */
|
||||||
uint16_t subvendorid; /* Subvendor ID from the bus */
|
uint16_t subvendorid; /* Subvendor ID from the bus */
|
||||||
uint16_t subdeviceid; /* Subdevice ID from the bus */
|
uint16_t subdeviceid; /* Subdevice ID from the bus */
|
||||||
struct resource *r_mem;
|
struct resource *r_mem;
|
||||||
|
struct resource *r_msix_table;
|
||||||
|
struct resource *r_msix_pba;
|
||||||
struct rman sc_iomem;
|
struct rman sc_iomem;
|
||||||
struct ahci_controller_irq {
|
struct ahci_controller_irq {
|
||||||
struct ahci_controller *ctlr;
|
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);
|
bus_dma_tag_t ahci_get_dma_tag(device_t dev, device_t child);
|
||||||
int ahci_ctlr_reset(device_t dev);
|
int ahci_ctlr_reset(device_t dev);
|
||||||
int ahci_ctlr_setup(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);
|
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
|
static int
|
||||||
ahci_pci_attach(device_t dev)
|
ahci_pci_attach(device_t dev)
|
||||||
{
|
{
|
||||||
@ -380,6 +402,11 @@ ahci_pci_attach(device_t dev)
|
|||||||
int error, i;
|
int error, i;
|
||||||
uint32_t devid = pci_get_devid(dev);
|
uint32_t devid = pci_get_devid(dev);
|
||||||
uint8_t revid = pci_get_revid(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;
|
i = 0;
|
||||||
while (ahci_ids[i].id != 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,
|
if (!(ctlr->r_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
|
||||||
&ctlr->r_rid, RF_ACTIVE)))
|
&ctlr->r_rid, RF_ACTIVE)))
|
||||||
return ENXIO;
|
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);
|
pci_enable_busmaster(dev);
|
||||||
/* Reset controller */
|
/* Reset controller */
|
||||||
if ((error = ahci_pci_ctlr_reset(dev)) != 0) {
|
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);
|
return (error);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -426,24 +500,51 @@ ahci_pci_attach(device_t dev)
|
|||||||
resource_int_value(device_get_name(dev),
|
resource_int_value(device_get_name(dev),
|
||||||
device_get_unit(dev), "msi", &ctlr->msi);
|
device_get_unit(dev), "msi", &ctlr->msi);
|
||||||
ctlr->numirqs = 1;
|
ctlr->numirqs = 1;
|
||||||
|
if (msi_count == 0 && msix_count == 0)
|
||||||
|
ctlr->msi = 0;
|
||||||
if (ctlr->msi < 0)
|
if (ctlr->msi < 0)
|
||||||
ctlr->msi = 0;
|
ctlr->msi = 0;
|
||||||
else if (ctlr->msi == 1)
|
else if (ctlr->msi == 1) {
|
||||||
ctlr->msi = min(1, pci_msi_count(dev));
|
msi_count = min(1, msi_count);
|
||||||
else if (ctlr->msi > 1) {
|
msix_count = min(1, msix_count);
|
||||||
|
} else if (ctlr->msi > 1)
|
||||||
ctlr->msi = 2;
|
ctlr->msi = 2;
|
||||||
ctlr->numirqs = pci_msi_count(dev);
|
|
||||||
}
|
/* Allocate MSI/MSI-x if needed/present. */
|
||||||
/* Allocate MSI if needed/present. */
|
if (ctlr->msi > 0) {
|
||||||
if (ctlr->msi && pci_alloc_msi(dev, &ctlr->numirqs) != 0) {
|
error = ENXIO;
|
||||||
ctlr->msi = 0;
|
|
||||||
ctlr->numirqs = 1;
|
/* 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);
|
error = ahci_attach(dev);
|
||||||
if (error != 0)
|
if (error != 0) {
|
||||||
if (ctlr->msi)
|
if (ctlr->msi > 0)
|
||||||
pci_release_msi(dev);
|
pci_release_msi(dev);
|
||||||
|
ahci_free_mem(dev);
|
||||||
|
}
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user