From patchwork Mon Oct 24 22:17:06 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ray Jui X-Patchwork-Id: 686213 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3t2rMS23gjz9tkk for ; Tue, 25 Oct 2016 09:20:36 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=broadcom.com header.i=@broadcom.com header.b=eC5FBPBN; dkim-atps=neutral Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S965376AbcJXWUd (ORCPT ); Mon, 24 Oct 2016 18:20:33 -0400 Received: from mail-pf0-f179.google.com ([209.85.192.179]:33450 "EHLO mail-pf0-f179.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S965487AbcJXWSC (ORCPT ); Mon, 24 Oct 2016 18:18:02 -0400 Received: by mail-pf0-f179.google.com with SMTP id 128so106280804pfz.0 for ; Mon, 24 Oct 2016 15:17:56 -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=8UyAtUh07baKUDzsDKrVPldW5lX1F5jWZuCBvDWmS4U=; b=eC5FBPBNSsuwm0yACgyMvw10vejypg2zu0Og/0uEDymBmyonIqwuyjIqE1n/iUxmTg PVryonCu5m49I7b0WOGpveR9yn43vq3u6aylnPxFK7B5zHI+C3qmm24Gm+FAWcoCj/wp FT55gpSfyUWI8RetN25z6BqjwQ0qDXnGzI7FI= 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=8UyAtUh07baKUDzsDKrVPldW5lX1F5jWZuCBvDWmS4U=; b=eFqM6uc3wiITmyI7StdD6f9O0d5YzKrsgNF3MQMyhIAuEvsIql3FHu+lf0o+4+j7Q+ EpNdrPTxpifvNVRorSi7Y2GuZ76IZ3rE3huhNkhEPN6wBFu2sE9HFTHmn4Hk8T4oZVhU IAmswmOvXYiF2356p5SvB87ZD4UWyZlqVM5kRlGAe/J8D+po9ikEcrWmy2mndVRLcwAu 2ljG1O9o2MtdeZ81X/dwgPwKGLt2CmEc7TI+zyg1ITDYDLBbJRy+H5nLW16joCsk6Rwl 5aN1UaRIrDAoLiODWYs9zBsHzkLu495P2EXWyyuGBdUfr0tLshUdgIQ1p0yHNUKJj+UK +eeA== X-Gm-Message-State: ABUngvd/xqzbSIIoeG7YQPeW91LrHfMh/WemH1AfcaVgsEwOV2058XaXqc1FSrk8JrL+ynND X-Received: by 10.98.131.71 with SMTP id h68mr33035501pfe.166.1477347476048; Mon, 24 Oct 2016 15:17:56 -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.17.53 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 24 Oct 2016 15:17:55 -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 06/12] PCI: iproc: Add PAXC v2 support Date: Mon, 24 Oct 2016 15:17:06 -0700 Message-Id: <1477347432-17656-7-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 Add support for the second generation of the iProc PCIe PAXC host controller to the iProc PCIe host driver Signed-off-by: Ray Jui Reviewed-by: Anup Patel Reviewed-by: Scott Branden --- drivers/pci/host/pcie-iproc-platform.c | 3 + drivers/pci/host/pcie-iproc.c | 176 ++++++++++++++++++++++++++++++++- drivers/pci/host/pcie-iproc.h | 4 + 3 files changed, 180 insertions(+), 3 deletions(-) diff --git a/drivers/pci/host/pcie-iproc-platform.c b/drivers/pci/host/pcie-iproc-platform.c index a3de087..f243150 100644 --- a/drivers/pci/host/pcie-iproc-platform.c +++ b/drivers/pci/host/pcie-iproc-platform.c @@ -33,6 +33,9 @@ static const struct of_device_id iproc_pcie_of_match_table[] = { }, { .compatible = "brcm,iproc-pcie-paxc", .data = (int *)IPROC_PCIE_PAXC, + }, { + .compatible = "brcm,iproc-pcie-paxc-v2", + .data = (int *)IPROC_PCIE_PAXC_V2, }, { /* sentinel */ } }; diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c index 07ec478..02f3ed3 100644 --- a/drivers/pci/host/pcie-iproc.c +++ b/drivers/pci/host/pcie-iproc.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,12 @@ #define RC_PCIE_RST_OUTPUT BIT(RC_PCIE_RST_OUTPUT_SHIFT) #define PAXC_RESET_MASK 0x7f +#define GIC_V3_CFG_SHIFT 0 +#define GIC_V3_CFG BIT(GIC_V3_CFG_SHIFT) + +#define MSI_ENABLE_CFG_SHIFT 0 +#define MSI_ENABLE_CFG BIT(MSI_ENABLE_CFG_SHIFT) + #define CFG_IND_ADDR_MASK 0x00001ffc #define CFG_ADDR_BUS_NUM_SHIFT 20 @@ -79,6 +86,31 @@ enum iproc_pcie_reg { /* clock/reset signal control */ IPROC_PCIE_CLK_CTRL = 0, + /* + * To allow MSI to be steered to an external MSI controller (e.g., ARM + * GICv3 ITS) + */ + IPROC_PCIE_MSI_GIC_MODE, + + /* + * IPROC_PCIE_MSI_BASE_ADDR and IPROC_PCIE_MSI_WINDOW_SIZE define the + * window where the MSI posted writes are written, for the writes to be + * interpreted as MSI writes + */ + IPROC_PCIE_MSI_BASE_ADDR, + IPROC_PCIE_MSI_WINDOW_SIZE, + + /* + * To hold the address of the register where the MSI writes are + * programed. When ARM GICv3 ITS is used, this should be programmed + * with the address of the GITS_TRANSLATER register + */ + IPROC_PCIE_MSI_ADDR_LO, + IPROC_PCIE_MSI_ADDR_HI, + + /* enable MSI */ + IPROC_PCIE_MSI_EN_CFG, + /* allow access to root complex configuration space */ IPROC_PCIE_CFG_IND_ADDR, IPROC_PCIE_CFG_IND_DATA, @@ -142,6 +174,20 @@ static const u16 iproc_pcie_reg_paxc[] = { [IPROC_PCIE_CFG_DATA] = 0x1fc, }; +/* iProc PCIe PAXC v2 registers */ +static const u16 iproc_pcie_reg_paxc_v2[] = { + [IPROC_PCIE_MSI_GIC_MODE] = 0x050, + [IPROC_PCIE_MSI_BASE_ADDR] = 0x074, + [IPROC_PCIE_MSI_WINDOW_SIZE] = 0x078, + [IPROC_PCIE_MSI_ADDR_LO] = 0x07c, + [IPROC_PCIE_MSI_ADDR_HI] = 0x080, + [IPROC_PCIE_MSI_EN_CFG] = 0x09c, + [IPROC_PCIE_CFG_IND_ADDR] = 0x1f0, + [IPROC_PCIE_CFG_IND_DATA] = 0x1f4, + [IPROC_PCIE_CFG_ADDR] = 0x1f8, + [IPROC_PCIE_CFG_DATA] = 0x1fc, +}; + static inline struct iproc_pcie *iproc_data(struct pci_bus *bus) { struct iproc_pcie *pcie; @@ -506,13 +552,131 @@ static int iproc_pcie_map_ranges(struct iproc_pcie *pcie, return 0; } +static int iproce_pcie_get_msi(struct iproc_pcie *pcie, + struct device_node *msi_node, + u64 *msi_addr) +{ + struct device *dev = pcie->dev; + int ret; + struct resource res; + + /* + * Check if 'msi-map' points to ARM GICv3 ITS, which is the only + * supported external MSI controller that requires steering + */ + if (!of_device_is_compatible(msi_node, "arm,gic-v3-its")) { + dev_err(dev, "unable to find compatible MSI controller\n"); + return -ENODEV; + } + + /* derive GITS_TRANSLATER address from GICv3 */ + ret = of_address_to_resource(msi_node, 0, &res); + if (ret < 0) { + dev_err(dev, "unable to obtain MSI controller resources\n"); + return ret; + } + + *msi_addr = res.start + GITS_TRANSLATER; + return 0; +} + +static void iproc_pcie_paxc_v2_msi_steer(struct iproc_pcie *pcie, u64 msi_addr) +{ + u32 val; + + /* + * Program bits [43:13] of address of GITS_TRANSLATER register into + * bits [30:0] of the MSI base address register. In fact, in all iProc + * based SoCs, all I/O register bases are well below the 32-bit + * boundary, so we can safely assume bits [43:32] are always zeros + */ + iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_BASE_ADDR, + (u32)(msi_addr >> 13)); + + /* use a default 8K window size */ + iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_WINDOW_SIZE, 0); + + /* steering MSI to GICv3 ITS */ + val = iproc_pcie_read_reg(pcie, IPROC_PCIE_MSI_GIC_MODE); + val |= GIC_V3_CFG; + iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_GIC_MODE, val); + + /* + * Program bits [43:2] of address of GITS_TRANSLATER register into the + * iProc MSI address registers + */ + msi_addr >>= 2; + iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_ADDR_HI, + upper_32_bits(msi_addr)); + iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_ADDR_LO, + lower_32_bits(msi_addr)); + + /* enable MSI */ + val = iproc_pcie_read_reg(pcie, IPROC_PCIE_MSI_EN_CFG); + val |= MSI_ENABLE_CFG; + iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_EN_CFG, val); +} + +static int iproc_pcie_msi_steer(struct iproc_pcie *pcie, + struct device_node *msi_node) +{ + struct device *dev = pcie->dev; + int ret; + u64 msi_addr; + + ret = iproce_pcie_get_msi(pcie, msi_node, &msi_addr); + if (ret < 0) { + dev_err(dev, "msi steering failed\n"); + return ret; + } + + switch (pcie->type) { + case IPROC_PCIE_PAXC_V2: + iproc_pcie_paxc_v2_msi_steer(pcie, msi_addr); + break; + default: + return -EINVAL; + } + + return 0; +} + static int iproc_pcie_msi_enable(struct iproc_pcie *pcie) { struct device_node *msi_node; + int ret; + + /* + * Either the "msi-parent" or the "msi-map" phandle needs to exist + * for us to obtain the MSI node + */ msi_node = of_parse_phandle(pcie->dev->of_node, "msi-parent", 0); - if (!msi_node) - return -ENODEV; + if (!msi_node) { + const __be32 *msi_map = NULL; + int len; + u32 phandle; + + msi_map = of_get_property(pcie->dev->of_node, "msi-map", &len); + if (!msi_map) + return -ENODEV; + + phandle = be32_to_cpup(msi_map + 1); + msi_node = of_find_node_by_phandle(phandle); + if (!msi_node) + return -ENODEV; + } + + /* + * Certain revisions of the iProc PCIe controller require additional + * configurations to steer the MSI writes towards an external MSI + * controller + */ + if (pcie->need_msi_steer) { + ret = iproc_pcie_msi_steer(pcie, msi_node); + if (ret) + return ret; + } /* * If another MSI controller is being used, the call below should fail @@ -544,6 +708,11 @@ static int iproc_pcie_rev_init(struct iproc_pcie *pcie) regs = iproc_pcie_reg_paxc; pcie->ep_is_internal = true; break; + case IPROC_PCIE_PAXC_V2: + regs = iproc_pcie_reg_paxc_v2; + pcie->ep_is_internal = true; + pcie->need_msi_steer = true; + break; default: dev_err(dev, "incompatible iProc PCIe interface\n"); return -EINVAL; @@ -556,7 +725,8 @@ static int iproc_pcie_rev_init(struct iproc_pcie *pcie) return -ENOMEM; /* go through the register table and populate all valid registers */ - pcie->reg_offsets[0] = regs[0]; + pcie->reg_offsets[0] = (pcie->type == IPROC_PCIE_PAXC_V2) ? + IPROC_PCIE_REG_INVALID : regs[0]; for (reg_idx = 1; reg_idx < IPROC_PCIE_MAX_NUM_REG; reg_idx++) pcie->reg_offsets[reg_idx] = regs[reg_idx] ? regs[reg_idx] : IPROC_PCIE_REG_INVALID; diff --git a/drivers/pci/host/pcie-iproc.h b/drivers/pci/host/pcie-iproc.h index 711dd3a..c2da140 100644 --- a/drivers/pci/host/pcie-iproc.h +++ b/drivers/pci/host/pcie-iproc.h @@ -27,6 +27,7 @@ enum iproc_pcie_type { IPROC_PCIE_PAXB_BCMA = 0, IPROC_PCIE_PAXB, IPROC_PCIE_PAXC, + IPROC_PCIE_PAXC_V2, }; /** @@ -61,6 +62,8 @@ struct iproc_msi; * 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 + * @need_msi_steer: indicates additional configuration of the iProc PCIe + * controller is required to steer MSI writes to external interrupt controller * @msi: MSI data */ struct iproc_pcie { @@ -79,6 +82,7 @@ struct iproc_pcie { bool has_apb_err_disable; bool need_ob_cfg; struct iproc_pcie_ob ob; + bool need_msi_steer; struct iproc_msi *msi; };