From patchwork Mon Oct 24 22:17:08 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ray Jui X-Patchwork-Id: 9393367 X-Patchwork-Delegate: bhelgaas@google.com Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 57F6E607F0 for ; Mon, 24 Oct 2016 22:18:12 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 46C0429061 for ; Mon, 24 Oct 2016 22:18:12 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 3B5AD29086; Mon, 24 Oct 2016 22:18:12 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.8 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 644C529187 for ; Mon, 24 Oct 2016 22:18:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S965592AbcJXWSI (ORCPT ); Mon, 24 Oct 2016 18:18:08 -0400 Received: from mail-pf0-f176.google.com ([209.85.192.176]:34254 "EHLO mail-pf0-f176.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S965586AbcJXWSF (ORCPT ); Mon, 24 Oct 2016 18:18:05 -0400 Received: by mail-pf0-f176.google.com with SMTP id r16so105899139pfg.1 for ; Mon, 24 Oct 2016 15:18:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=broadcom.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=7fQKp1ZgLEW5FooVfbDDvFd473eAigtjf9oHFcW5y2Y=; b=Np+J0OsnsAHefG0uCxo07C0UzEwljyvJUHgt1ud7Qsh4roGDZqIXMUrIQMx/wnNuKB R+F8olhEzs2NFsJebQNX2x6QfefJx1OHdRx1o5J+IaG1uS8cgGr/fn1pFBkeKIv28Bcq BS8XZXeJbBwyuObQEH92nXkWJqTB70Zd41vv8= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=7fQKp1ZgLEW5FooVfbDDvFd473eAigtjf9oHFcW5y2Y=; b=nK5HzCGbcffnfsYVc4IM1lEb/V9lUswv6qXeCPlYYEA1hKv9aD49G0JFcN/9shI76V AGB5XoNlVlcZc9Drk1j3jIVvpe8vUsnL4cgbNW5Qp3Ma5PNbJy9FD6cYUwd5bbCusHJ+ TjWYM2xsx3UoWyrjXg8R8XgpBOE/rMc0lbEnkh6BmafTSZ41ulhFdDeV2uihgJ2pnowF U0KtKqScnuVi/STNhGI5VeiNd0gp2/oi6jnsNcyTD1cnunA0lbJ/mZS0l2xC8LTMCfMg QXI2e5eWSMn3W8uZci6bcKvcW3n6jyUKnP2U6FZiBNHnpYU9NozNGw0fyn4PVrCeVr3C HSaA== X-Gm-Message-State: ABUngvc50dfXg+13VeorB1IPqxOb0uu0qY6ZVPPTdl3ufMEpLqXeABeMLk/G6mHOqNc5LyrT X-Received: by 10.98.9.147 with SMTP id 19mr32960935pfj.68.1477347483824; Mon, 24 Oct 2016 15:18:03 -0700 (PDT) Received: from lbrmn-lnxub44-1.ric.broadcom.com ([216.31.219.19]) by smtp.gmail.com with ESMTPSA id x1sm27745131pax.7.2016.10.24.15.18.01 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 24 Oct 2016 15:18:03 -0700 (PDT) From: Ray Jui To: Bjorn Helgaas , Bjorn Helgaas Cc: linux-kernel@vger.kernel.org, bcm-kernel-feedback-list@broadcom.com, linux-pci@vger.kernel.org, Alex Barba , Oza Oza , Ray Jui , Ray Jui Subject: [PATCH 08/12] PCI: iproc: Making outbound mapping code more generic Date: Mon, 24 Oct 2016 15:17:08 -0700 Message-Id: <1477347432-17656-9-git-send-email-ray.jui@broadcom.com> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1477347432-17656-1-git-send-email-ray.jui@broadcom.com> References: <1477347432-17656-1-git-send-email-ray.jui@broadcom.com> Sender: linux-pci-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Improve the iProc PCIe outbound mapping code by making it more generic and removing redundant device tree properties 'brcm,pcie-ob-window-size' and 'brcm,pcie-ob-oarr-size'. The driver is still backward compatible to device tree binaries with the two properties specified The driver now automatically confgures the correct mapping window size and number of mapping windows based on the value of device tree property 'ranges' and the capability of of the iProc PCIe controller Signed-off-by: Oza Oza Signed-off-by: Ray Jui Reviewed-by: Scott Branden --- drivers/pci/host/pcie-iproc-platform.c | 13 -- drivers/pci/host/pcie-iproc.c | 247 +++++++++++++++++++++++---------- drivers/pci/host/pcie-iproc.h | 15 +- 3 files changed, 186 insertions(+), 89 deletions(-) diff --git a/drivers/pci/host/pcie-iproc-platform.c b/drivers/pci/host/pcie-iproc-platform.c index f243150..47329d3 100644 --- a/drivers/pci/host/pcie-iproc-platform.c +++ b/drivers/pci/host/pcie-iproc-platform.c @@ -87,19 +87,6 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev) return ret; } pcie->ob.axi_offset = val; - - ret = of_property_read_u32(np, "brcm,pcie-ob-window-size", - &val); - if (ret) { - dev_err(dev, - "missing brcm,pcie-ob-window-size property\n"); - return ret; - } - pcie->ob.window_size = (resource_size_t)val * SZ_1M; - - if (of_property_read_bool(np, "brcm,pcie-ob-oarr-size")) - pcie->ob.set_oarr_size = true; - pcie->need_ob_cfg = true; } diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c index 02f3ed3..b466ba6 100644 --- a/drivers/pci/host/pcie-iproc.c +++ b/drivers/pci/host/pcie-iproc.c @@ -68,17 +68,47 @@ #define APB_ERR_EN_SHIFT 0 #define APB_ERR_EN BIT(APB_ERR_EN_SHIFT) +/* derive the enum index of the outbound/inbound mapping registers */ +#define MAP_REG(base_reg, index) ((base_reg) + (index) * 2) + +/* + * Maximum number of outbound mapping window sizes that can be supported by any + * OARR/OMAP mapping pair + */ +#define MAX_NUM_OB_WINDOW_SIZES 4 + #define OARR_VALID_SHIFT 0 #define OARR_VALID BIT(OARR_VALID_SHIFT) #define OARR_SIZE_CFG_SHIFT 1 -#define OARR_SIZE_CFG BIT(OARR_SIZE_CFG_SHIFT) #define PCI_EXP_CAP 0xac -#define MAX_NUM_OB_WINDOWS 2 - #define IPROC_PCIE_REG_INVALID 0xffff +/** + * iProc PCIe outbound mapping controller specific parameters + * + * @window_sizes: list of supported outbound mapping window sizes in MB + * @nr_sizes: number of supported outbound mapping window sizes + */ +struct iproc_pcie_ob_map { + resource_size_t window_sizes[MAX_NUM_OB_WINDOW_SIZES]; + unsigned int nr_sizes; +}; + +static const struct iproc_pcie_ob_map paxb_ob_map[] = { + { + /* OARR0/OMAP0 */ + .window_sizes = { 128, 256 }, + .nr_sizes = 2, + }, + { + /* OARR1/OMAP1 */ + .window_sizes = { 128, 256 }, + .nr_sizes = 2, + }, +}; + /* * iProc PCIe host registers */ @@ -123,10 +153,14 @@ enum iproc_pcie_reg { IPROC_PCIE_INTX_EN, /* outbound address mapping */ - IPROC_PCIE_OARR_LO, - IPROC_PCIE_OARR_HI, - IPROC_PCIE_OMAP_LO, - IPROC_PCIE_OMAP_HI, + IPROC_PCIE_OARR0, + IPROC_PCIE_OMAP0, + IPROC_PCIE_OARR1, + IPROC_PCIE_OMAP1, + IPROC_PCIE_OARR2, + IPROC_PCIE_OMAP2, + IPROC_PCIE_OARR3, + IPROC_PCIE_OMAP3, /* link status */ IPROC_PCIE_LINK_STATUS, @@ -151,27 +185,27 @@ static const u16 iproc_pcie_reg_paxb_bcma[] = { /* iProc PCIe PAXB registers */ static const u16 iproc_pcie_reg_paxb[] = { - [IPROC_PCIE_CLK_CTRL] = 0x000, - [IPROC_PCIE_CFG_IND_ADDR] = 0x120, - [IPROC_PCIE_CFG_IND_DATA] = 0x124, - [IPROC_PCIE_CFG_ADDR] = 0x1f8, - [IPROC_PCIE_CFG_DATA] = 0x1fc, - [IPROC_PCIE_INTX_EN] = 0x330, - [IPROC_PCIE_OARR_LO] = 0xd20, - [IPROC_PCIE_OARR_HI] = 0xd24, - [IPROC_PCIE_OMAP_LO] = 0xd40, - [IPROC_PCIE_OMAP_HI] = 0xd44, - [IPROC_PCIE_LINK_STATUS] = 0xf0c, - [IPROC_PCIE_APB_ERR_EN] = 0xf40, + [IPROC_PCIE_CLK_CTRL] = 0x000, + [IPROC_PCIE_CFG_IND_ADDR] = 0x120, + [IPROC_PCIE_CFG_IND_DATA] = 0x124, + [IPROC_PCIE_CFG_ADDR] = 0x1f8, + [IPROC_PCIE_CFG_DATA] = 0x1fc, + [IPROC_PCIE_INTX_EN] = 0x330, + [IPROC_PCIE_OARR0] = 0xd20, + [IPROC_PCIE_OMAP0] = 0xd40, + [IPROC_PCIE_OARR1] = 0xd28, + [IPROC_PCIE_OMAP1] = 0xd48, + [IPROC_PCIE_LINK_STATUS] = 0xf0c, + [IPROC_PCIE_APB_ERR_EN] = 0xf40, }; /* iProc PCIe PAXC v1 registers */ static const u16 iproc_pcie_reg_paxc[] = { - [IPROC_PCIE_CLK_CTRL] = 0x000, - [IPROC_PCIE_CFG_IND_ADDR] = 0x1f0, - [IPROC_PCIE_CFG_IND_DATA] = 0x1f4, - [IPROC_PCIE_CFG_ADDR] = 0x1f8, - [IPROC_PCIE_CFG_DATA] = 0x1fc, + [IPROC_PCIE_CLK_CTRL] = 0x000, + [IPROC_PCIE_CFG_IND_ADDR] = 0x1f0, + [IPROC_PCIE_CFG_IND_DATA] = 0x1f4, + [IPROC_PCIE_CFG_ADDR] = 0x1f8, + [IPROC_PCIE_CFG_DATA] = 0x1fc, }; /* iProc PCIe PAXC v2 registers */ @@ -256,18 +290,6 @@ static inline void iproc_pcie_apb_err_disable(struct pci_bus *bus, } } -static inline void iproc_pcie_ob_write(struct iproc_pcie *pcie, - enum iproc_pcie_reg reg, - unsigned window, u32 val) -{ - u16 offset = iproc_pcie_reg_offset(pcie, reg); - - if (iproc_pcie_reg_is_invalid(offset)) - return; - - writel(val, pcie->base + offset + (window * 8)); -} - /** * Note access to the configuration registers are protected at the higher layer * by 'pci_lock' in drivers/pci/access.c @@ -452,6 +474,58 @@ static void iproc_pcie_enable(struct iproc_pcie *pcie) iproc_pcie_write_reg(pcie, IPROC_PCIE_INTX_EN, SYS_RC_INTX_MASK); } +static inline bool iproc_pcie_ob_is_valid(struct iproc_pcie *pcie, + int window_idx) +{ + u32 val; + + val = iproc_pcie_read_reg(pcie, MAP_REG(IPROC_PCIE_OARR0, window_idx)); + + return !!(val & OARR_VALID); +} + +static inline int iproc_pcie_ob_write(struct iproc_pcie *pcie, int window_idx, + int size_idx, u64 axi_addr, u64 pci_addr) +{ + struct device *dev = pcie->dev; + u16 oarr_offset, omap_offset; + + /* + * Derive the OARR/OMAP offset from the first pair (OARR0/OMAP0) based + * on window index + */ + oarr_offset = iproc_pcie_reg_offset(pcie, MAP_REG(IPROC_PCIE_OARR0, + window_idx)); + omap_offset = iproc_pcie_reg_offset(pcie, MAP_REG(IPROC_PCIE_OMAP0, + window_idx)); + if (iproc_pcie_reg_is_invalid(oarr_offset) || + iproc_pcie_reg_is_invalid(omap_offset)) + return -EINVAL; + + /* + * Program the OARR registers. The upper 32-bit OARR register is + * always right after the lower 32-bit OARR register + */ + writel(lower_32_bits(axi_addr) | (size_idx << OARR_SIZE_CFG_SHIFT) | + OARR_VALID, pcie->base + oarr_offset); + writel(upper_32_bits(axi_addr), pcie->base + oarr_offset + 4); + + /* now program the OMAP registers */ + writel(lower_32_bits(pci_addr), pcie->base + omap_offset); + writel(upper_32_bits(pci_addr), pcie->base + omap_offset + 4); + + dev_info(dev, "ob window [%d]: offset 0x%x axi %pap pci %pap\n", + window_idx, oarr_offset, &axi_addr, &pci_addr); + dev_info(dev, "oarr lo 0x%x oarr hi 0x%x\n", + readl(pcie->base + oarr_offset), + readl(pcie->base + oarr_offset + 4)); + dev_info(dev, "omap lo 0x%x omap hi 0x%x\n", + readl(pcie->base + omap_offset), + readl(pcie->base + omap_offset + 4)); + + return 0; +} + /** * Some iProc SoCs require the SW to configure the outbound address mapping * @@ -468,24 +542,7 @@ static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr, { struct iproc_pcie_ob *ob = &pcie->ob; struct device *dev = pcie->dev; - unsigned i; - u64 max_size = (u64)ob->window_size * MAX_NUM_OB_WINDOWS; - u64 remainder; - - if (size > max_size) { - dev_err(dev, - "res size %pap exceeds max supported size 0x%llx\n", - &size, max_size); - return -EINVAL; - } - - div64_u64_rem(size, ob->window_size, &remainder); - if (remainder) { - dev_err(dev, - "res size %pap needs to be multiple of window size %pap\n", - &size, &ob->window_size); - return -EINVAL; - } + int ret = -EINVAL, window_idx, size_idx; if (axi_addr < ob->axi_offset) { dev_err(dev, "axi address %pap less than offset %pap\n", @@ -499,26 +556,70 @@ static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr, */ axi_addr -= ob->axi_offset; - for (i = 0; i < MAX_NUM_OB_WINDOWS; i++) { - iproc_pcie_ob_write(pcie, IPROC_PCIE_OARR_LO, i, - lower_32_bits(axi_addr) | OARR_VALID | - (ob->set_oarr_size ? 1 : 0)); - iproc_pcie_ob_write(pcie, IPROC_PCIE_OARR_HI, i, - upper_32_bits(axi_addr)); - iproc_pcie_ob_write(pcie, IPROC_PCIE_OMAP_LO, i, - lower_32_bits(pci_addr)); - iproc_pcie_ob_write(pcie, IPROC_PCIE_OMAP_HI, i, - upper_32_bits(pci_addr)); - - size -= ob->window_size; - if (size == 0) + /* iterate through all OARR/OMAP mapping windows */ + for (window_idx = ob->nr_windows - 1; window_idx >= 0; window_idx--) { + const struct iproc_pcie_ob_map *ob_map = + &pcie->ob_map[window_idx]; + + /* + * If current outbound window is already in use, move on to the + * next one + */ + if (iproc_pcie_ob_is_valid(pcie, window_idx)) + continue; + + /* + * Iterate through all supported window sizes within the + * OARR/OMAP pair to find a match. Go through the window sizes + * in a descending order + */ + for (size_idx = ob_map->nr_sizes - 1; size_idx >= 0; + size_idx--) { + resource_size_t window_size = + ob_map->window_sizes[size_idx] * SZ_1M; + + if (size < window_size) + continue; + + if (!IS_ALIGNED(axi_addr, window_size) || + !IS_ALIGNED(pci_addr, window_size)) { + dev_err(dev, + "axi %pap or pci %pap not aligned\n", + &axi_addr, &pci_addr); + return -EINVAL; + } + + /* + * Match found! Program both OARR and OMAP and mark + * them as a valid entry + */ + ret = iproc_pcie_ob_write(pcie, window_idx, size_idx, + axi_addr, pci_addr); + if (ret) + goto err_ob; + + size -= window_size; + if (size == 0) + return 0; + + /* + * If we are here, we are done with the current window, + * but not yet finished all mappings. Need to move on + * to the next window + */ + axi_addr += window_size; + pci_addr += window_size; break; - - axi_addr += ob->window_size; - pci_addr += ob->window_size; + } } - return 0; +err_ob: + dev_err(dev, "unable to configure outbound mapping\n"); + dev_err(dev, + "axi %pap, axi offset %pap, pci %pap, res size %pap\n", + &axi_addr, &ob->axi_offset, &pci_addr, &size); + + return ret; } static int iproc_pcie_map_ranges(struct iproc_pcie *pcie, @@ -703,6 +804,10 @@ static int iproc_pcie_rev_init(struct iproc_pcie *pcie) case IPROC_PCIE_PAXB: regs = iproc_pcie_reg_paxb; pcie->has_apb_err_disable = true; + if (pcie->need_ob_cfg) { + pcie->ob_map = paxb_ob_map; + pcie->ob.nr_windows = ARRAY_SIZE(paxb_ob_map); + } break; case IPROC_PCIE_PAXC: regs = iproc_pcie_reg_paxc; diff --git a/drivers/pci/host/pcie-iproc.h b/drivers/pci/host/pcie-iproc.h index c2da140..861b526 100644 --- a/drivers/pci/host/pcie-iproc.h +++ b/drivers/pci/host/pcie-iproc.h @@ -32,17 +32,16 @@ enum iproc_pcie_type { /** * iProc PCIe outbound mapping - * @set_oarr_size: indicates the OARR size bit needs to be set * @axi_offset: offset from the AXI address to the internal address used by * the iProc PCIe core - * @window_size: outbound window size + * @nr_windows: total number of supported outbound mapping windows */ struct iproc_pcie_ob { - bool set_oarr_size; resource_size_t axi_offset; - resource_size_t window_size; + unsigned int nr_windows; }; +struct iproc_pcie_ob_map; struct iproc_msi; /** @@ -60,8 +59,11 @@ struct iproc_msi; * @ep_is_internal: indicates an internal emulated endpoint device is connected * @has_apb_err_disable: indicates the controller can be configured to prevent * unsupported request from being forwarded as an APB bus error + * * @need_ob_cfg: indicates SW needs to configure the outbound mapping window - * @ob: outbound mapping parameters + * @ob: outbound mapping related parameters + * @ob_map: outbound mapping related parameters specific to the controller + * * @need_msi_steer: indicates additional configuration of the iProc PCIe * controller is required to steer MSI writes to external interrupt controller * @msi: MSI data @@ -80,8 +82,11 @@ struct iproc_pcie { int (*map_irq)(const struct pci_dev *, u8, u8); bool ep_is_internal; bool has_apb_err_disable; + bool need_ob_cfg; struct iproc_pcie_ob ob; + const struct iproc_pcie_ob_map *ob_map; + bool need_msi_steer; struct iproc_msi *msi; };