Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 22 Jul 2015 09:46:23 +0000 (UTC)
From:      Zbigniew Bodek <zbb@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r285789 - head/sys/dev/ahci
Message-ID:  <201507220946.t6M9kN3c062812@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
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;
 }
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201507220946.t6M9kN3c062812>