From owner-svn-src-head@freebsd.org Wed Jul 22 09:46:24 2015 Return-Path: Delivered-To: svn-src-head@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 3B6BE9A6331; Wed, 22 Jul 2015 09:46:24 +0000 (UTC) (envelope-from zbb@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 175181173; Wed, 22 Jul 2015 09:46:24 +0000 (UTC) (envelope-from zbb@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.70]) by repo.freebsd.org (8.14.9/8.14.9) with ESMTP id t6M9kNKX062817; Wed, 22 Jul 2015 09:46:23 GMT (envelope-from zbb@FreeBSD.org) Received: (from zbb@localhost) by repo.freebsd.org (8.14.9/8.14.9/Submit) id t6M9kN3c062812; Wed, 22 Jul 2015 09:46:23 GMT (envelope-from zbb@FreeBSD.org) Message-Id: <201507220946.t6M9kN3c062812@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: zbb set sender to zbb@FreeBSD.org using -f From: Zbigniew Bodek Date: Wed, 22 Jul 2015 09:46:23 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r285789 - head/sys/dev/ahci X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 22 Jul 2015 09:46:24 -0000 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; }