Author: zbb
Date: Wed Jul 22 09:46:22 2015
New Revision: 285789
URL: https://svnweb.freebsd.org/changeset/base/285789

Log:
  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

Modified:
  head/sys/dev/ahci/ahci.c
  head/sys/dev/ahci/ahci.h
  head/sys/dev/ahci/ahci_pci.c

Modified: head/sys/dev/ahci/ahci.c
==============================================================================
--- head/sys/dev/ahci/ahci.c    Wed Jul 22 09:29:50 2015        (r285788)
+++ head/sys/dev/ahci/ahci.c    Wed Jul 22 09:46:22 2015        (r285789)
@@ -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

Modified: head/sys/dev/ahci/ahci.h
==============================================================================
--- head/sys/dev/ahci/ahci.h    Wed Jul 22 09:29:50 2015        (r285788)
+++ head/sys/dev/ahci/ahci.h    Wed Jul 22 09:46:22 2015        (r285789)
@@ -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
 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);

Modified: head/sys/dev/ahci/ahci_pci.c
==============================================================================
--- head/sys/dev/ahci/ahci_pci.c        Wed Jul 22 09:29:50 2015        
(r285788)
+++ head/sys/dev/ahci/ahci_pci.c        Wed Jul 22 09:46:22 2015        
(r285789)
@@ -374,12 +374,39 @@ ahci_ata_probe(device_t dev)
 }
 
 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 @@ 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;
 }
 
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to