From patchwork Tue Nov 1 00:38:39 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ray Jui X-Patchwork-Id: 9406727 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 8F98A60234 for ; Tue, 1 Nov 2016 00:40:18 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 80EB62930A for ; Tue, 1 Nov 2016 00:40:18 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 75F8329336; Tue, 1 Nov 2016 00:40:18 +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=unavailable 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 C76D72930A for ; Tue, 1 Nov 2016 00:40:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S948190AbcKAAkQ (ORCPT ); Mon, 31 Oct 2016 20:40:16 -0400 Received: from mail-pf0-f172.google.com ([209.85.192.172]:35722 "EHLO mail-pf0-f172.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1161086AbcKAAjq (ORCPT ); Mon, 31 Oct 2016 20:39:46 -0400 Received: by mail-pf0-f172.google.com with SMTP id s8so84771211pfj.2 for ; Mon, 31 Oct 2016 17:39:40 -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=YBVGzmdjyWlWLDiv3kENyJQP94XQ0suWAsjxo+CJ5ec=; b=N9gmCzBqDVyBSPaLw85rfNf6xW3SRehVzmbtTcA8K2xDBtoQ0PLszyPIs6cyU4VFth mo6EU4Xy7Xfx+I5U9tC3oTiM1Gf7sTvYywCfeBySqw1xTvOGVrz32rQ6LJOzkdZgR7DO OhwHZbYrdhxq9AHQfl9z93Szpd2CxBeUk/b4c= 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=YBVGzmdjyWlWLDiv3kENyJQP94XQ0suWAsjxo+CJ5ec=; b=DmcxzHjYFWSjlufn3Z6IgjcmCONhSxwsqtxQNq5YI3LRlGzDaerMJAttQ/iIn0maJH rBPa2R9JYYSCmwioFJh3TR9qiLWpjsrWYnAn1Sf+X12EhFUgKP5kAfekiRt/LrHhv5h6 2ydkFjOqaOFSFnVDyApMnlg4C2iVSlJEUtO2FYIcaj9J/JoMhYNxGPLYQ2uyGrkGtM7G WOuJy8Ef1+PruMMJjs7ot+7+D4osBOZkbkcVS0+1KpGP1DbuYDuxuxv7Nc5qgpshPhPn sk9DfDWUj5V6Md1Ad5aXNZTUTpsBy0vlV7Y7y4QoGDTTuI0CeqAVbR3s2z4onQ/plox8 1CIA== X-Gm-Message-State: ABUngvexUypVFiAIYJ0nXbE45pGBZonOxyiQeAYs9MQ9Ln/V71crRMR86+5FjVm9Y7DuruG1 X-Received: by 10.98.41.3 with SMTP id p3mr53936168pfp.92.1477960780126; Mon, 31 Oct 2016 17:39:40 -0700 (PDT) Received: from lbrmn-lnxub44-1.ric.broadcom.com ([216.31.219.19]) by smtp.gmail.com with ESMTPSA id x62sm2574619pfb.20.2016.10.31.17.39.38 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 31 Oct 2016 17:39:39 -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 Subject: [PATCH v2 10/12] PCI: iproc: Add inbound DMA mapping support Date: Mon, 31 Oct 2016 17:38:39 -0700 Message-Id: <1477960721-17649-11-git-send-email-ray.jui@broadcom.com> X-Mailer: git-send-email 2.1.4 In-Reply-To: <1477960721-17649-1-git-send-email-ray.jui@broadcom.com> References: <1477960721-17649-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 This patch adds the support of inbound mapping to the iProc PCIe driver. The range of the inbound mapping is configured by the optional device tree property 'dma-ranges'. While inbound mapping is done automatically in the ASIC on most iProc based SoCs, newer ASIC (e.g., Stingray) requires inbound mapping to be configured explicitly in software Signed-off-by: Oza Oza Signed-off-by: Ray Jui Reviewed-by: Scott Branden --- drivers/pci/host/pcie-iproc.c | 235 ++++++++++++++++++++++++++++++++++++++++++ drivers/pci/host/pcie-iproc.h | 15 +++ 2 files changed, 250 insertions(+) diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c index b466ba6..c4e2211 100644 --- a/drivers/pci/host/pcie-iproc.c +++ b/drivers/pci/host/pcie-iproc.c @@ -81,6 +81,15 @@ #define OARR_VALID BIT(OARR_VALID_SHIFT) #define OARR_SIZE_CFG_SHIFT 1 +/* + * Maximum number of inbound mapping region sizes that can be supported by an + * IARR + */ +#define MAX_NUM_IB_REGION_SIZES 9 + +#define IMAP_VALID_SHIFT 0 +#define IMAP_VALID BIT(IMAP_VALID_SHIFT) + #define PCI_EXP_CAP 0xac #define IPROC_PCIE_REG_INVALID 0xffff @@ -109,6 +118,44 @@ static const struct iproc_pcie_ob_map paxb_ob_map[] = { }, }; +/** + * iProc PCIe inbound mapping type + */ +enum iproc_pcie_ib_map_type { + /* for DDR memory */ + IPROC_PCIE_IB_MAP_MEM = 0, + + /* for device I/O memory */ + IPROC_PCIE_IB_MAP_IO, + + /* invalid or unused */ + IPROC_PCIE_IB_MAP_INVALID +}; + +/** + * iProc PCIe inbound mapping controller specific parameters + * + * @type: inbound mapping region type + * @size_unit: inbound mapping region size unit, could be SZ_1K, SZ_1M, or + * SZ_1G + * @region_sizes: list of supported inbound mapping region sizes in KB, MB, or + * GB, depedning on the size unit + * @nr_sizes: number of supported inbound mapping region sizes + * @nr_windows: number of supported inbound mapping windows for the region + * @imap_addr_offset: register offset between the upper and lower 32-bit + * IMAP address registers + * @imap_window_offset: register offset between each IMAP window + */ +struct iproc_pcie_ib_map { + enum iproc_pcie_ib_map_type type; + unsigned int size_unit; + resource_size_t region_sizes[MAX_NUM_IB_REGION_SIZES]; + unsigned int nr_sizes; + unsigned int nr_windows; + u16 imap_addr_offset; + u16 imap_window_offset; +}; + /* * iProc PCIe host registers */ @@ -162,6 +209,18 @@ enum iproc_pcie_reg { IPROC_PCIE_OARR3, IPROC_PCIE_OMAP3, + /* inbound address mapping */ + IPROC_PCIE_IARR0, + IPROC_PCIE_IMAP0, + IPROC_PCIE_IARR1, + IPROC_PCIE_IMAP1, + IPROC_PCIE_IARR2, + IPROC_PCIE_IMAP2, + IPROC_PCIE_IARR3, + IPROC_PCIE_IMAP3, + IPROC_PCIE_IARR4, + IPROC_PCIE_IMAP4, + /* link status */ IPROC_PCIE_LINK_STATUS, @@ -653,6 +712,178 @@ static int iproc_pcie_map_ranges(struct iproc_pcie *pcie, return 0; } +static inline bool iproc_pcie_ib_is_in_use(struct iproc_pcie *pcie, + int region_idx) +{ + const struct iproc_pcie_ib_map *ib_map = &pcie->ib_map[region_idx]; + u32 val; + + val = iproc_pcie_read_reg(pcie, MAP_REG(IPROC_PCIE_IARR0, region_idx)); + + return !!(val & (BIT(ib_map->nr_sizes) - 1)); +} + +static inline bool iproc_pcie_ib_check_type(const struct iproc_pcie_ib_map *ib_map, + enum iproc_pcie_ib_map_type type) +{ + return !!(ib_map->type == type); +} + +static int iproc_pcie_ib_write(struct iproc_pcie *pcie, int region_idx, + int size_idx, int nr_windows, u64 axi_addr, + u64 pci_addr, resource_size_t size) +{ + struct device *dev = pcie->dev; + const struct iproc_pcie_ib_map *ib_map = &pcie->ib_map[region_idx]; + u16 iarr_offset, imap_offset; + u32 val; + int window_idx; + + iarr_offset = iproc_pcie_reg_offset(pcie, + MAP_REG(IPROC_PCIE_IARR0, region_idx)); + imap_offset = iproc_pcie_reg_offset(pcie, + MAP_REG(IPROC_PCIE_IMAP0, region_idx)); + if (iproc_pcie_reg_is_invalid(iarr_offset) || + iproc_pcie_reg_is_invalid(imap_offset)) + return -EINVAL; + + dev_info(dev, "ib region [%d]: offset 0x%x axi %pap pci %pap\n", + region_idx, iarr_offset, &axi_addr, &pci_addr); + + /* + * Program the IARR registers. The upper 32-bit IARR register is + * always right after the lower 32-bit IARR register + */ + writel(lower_32_bits(pci_addr) | BIT(size_idx), + pcie->base + iarr_offset); + writel(upper_32_bits(pci_addr), pcie->base + iarr_offset + 4); + + dev_info(dev, "iarr lo 0x%x iarr hi 0x%x\n", + readl(pcie->base + iarr_offset), + readl(pcie->base + iarr_offset + 4)); + + /* + * Now program the IMAP registers. Each IARR region may have one or + * more IMAP windows + */ + size /= nr_windows; + for (window_idx = 0; window_idx < nr_windows; window_idx++) { + val = readl(pcie->base + imap_offset); + val |= lower_32_bits(axi_addr) | IMAP_VALID; + writel(val, pcie->base + imap_offset); + writel(upper_32_bits(axi_addr), + pcie->base + imap_offset + ib_map->imap_addr_offset); + + dev_info(dev, "imap window [%d] lo 0x%x hi 0x%x\n", + window_idx, readl(pcie->base + imap_offset), + readl(pcie->base + imap_offset + + ib_map->imap_addr_offset)); + + imap_offset += ib_map->imap_window_offset; + axi_addr += size; + } + + return 0; +} + +static int iproc_pcie_setup_ib(struct iproc_pcie *pcie, + struct of_pci_range *range, + enum iproc_pcie_ib_map_type type) +{ + struct device *dev = pcie->dev; + struct iproc_pcie_ib *ib = &pcie->ib; + int ret; + unsigned int region_idx, size_idx; + u64 axi_addr = range->cpu_addr, pci_addr = range->pci_addr; + resource_size_t size = range->size; + + /* iterate through all IARR mapping regions */ + for (region_idx = 0; region_idx < ib->nr_regions; region_idx++) { + const struct iproc_pcie_ib_map *ib_map = + &pcie->ib_map[region_idx]; + + /* + * If current inbound region is already in use or not a + * compatible type, move on to the next + */ + if (iproc_pcie_ib_is_in_use(pcie, region_idx) || + !iproc_pcie_ib_check_type(ib_map, type)) + continue; + + /* iterate through all supported region sizes to find a match */ + for (size_idx = 0; size_idx < ib_map->nr_sizes; size_idx++) { + resource_size_t region_size = + ib_map->region_sizes[size_idx] * ib_map->size_unit; + + if (size != region_size) + continue; + + if (!IS_ALIGNED(axi_addr, region_size) || + !IS_ALIGNED(pci_addr, region_size)) { + dev_err(dev, + "axi %pap or pci %pap not aligned\n", + &axi_addr, &pci_addr); + return -EINVAL; + } + + /* match found! program IARR and all IMAP windows */ + ret = iproc_pcie_ib_write(pcie, region_idx, size_idx, + ib_map->nr_windows, axi_addr, + pci_addr, size); + if (ret) + goto err_ib; + else + return 0; + + } + } +err_ib: + dev_err(dev, "unable to configure inbound mapping\n"); + dev_err(dev, "axi %pap, pci %pap, res size %pap\n", + &axi_addr, &pci_addr, &size); + + return ret; +} + +static int pci_dma_range_parser_init(struct of_pci_range_parser *parser, + struct device_node *node) +{ + const int na = 3, ns = 2; + int rlen; + + parser->node = node; + parser->pna = of_n_addr_cells(node); + parser->np = parser->pna + na + ns; + + parser->range = of_get_property(node, "dma-ranges", &rlen); + if (!parser->range) + return -ENOENT; + + parser->end = parser->range + rlen / sizeof(__be32); + return 0; +} + +static int iproc_pcie_map_dma_ranges(struct iproc_pcie *pcie) +{ + struct of_pci_range range; + struct of_pci_range_parser parser; + int ret; + + /* get the dma-ranges from DT */ + ret = pci_dma_range_parser_init(&parser, pcie->dev->of_node); + if (ret) + return ret; + + for_each_of_pci_range(&parser, &range) { + /* each range entry corresponds to an inbound mapping region */ + ret = iproc_pcie_setup_ib(pcie, &range, IPROC_PCIE_IB_MAP_MEM); + if (ret) + return ret; + } + + return 0; +} + static int iproce_pcie_get_msi(struct iproc_pcie *pcie, struct device_node *msi_node, u64 *msi_addr) @@ -880,6 +1111,10 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) } } + ret = iproc_pcie_map_dma_ranges(pcie); + if (ret && ret != -ENOENT) + goto err_power_off_phy; + #ifdef CONFIG_ARM pcie->sysdata.private_data = pcie; sysdata = &pcie->sysdata; diff --git a/drivers/pci/host/pcie-iproc.h b/drivers/pci/host/pcie-iproc.h index 861b526..3f2709a 100644 --- a/drivers/pci/host/pcie-iproc.h +++ b/drivers/pci/host/pcie-iproc.h @@ -41,7 +41,16 @@ struct iproc_pcie_ob { unsigned int nr_windows; }; +/** + * iProc PCIe inbound mapping + * @nr_regions: total number of supported inbound mapping regions + */ +struct iproc_pcie_ib { + unsigned int nr_regions; +}; + struct iproc_pcie_ob_map; +struct iproc_pcie_ib_map; struct iproc_msi; /** @@ -64,6 +73,9 @@ struct iproc_msi; * @ob: outbound mapping related parameters * @ob_map: outbound mapping related parameters specific to the controller * + * @ib: inbound mapping related parameters + * @ib_map: outbound mapping region related 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 @@ -87,6 +99,9 @@ struct iproc_pcie { struct iproc_pcie_ob ob; const struct iproc_pcie_ob_map *ob_map; + struct iproc_pcie_ib ib; + const struct iproc_pcie_ib_map *ib_map; + bool need_msi_steer; struct iproc_msi *msi; };