2019-02-18 09:55:53

by Bao Xiaowei

[permalink] [raw]
Subject: [PATCH 1/6] PCI: mobiveil: Add the EP mode support

Add the EP mode support for Mobiveil base on endpoint framework.

Signed-off-by: Xiaowei Bao <[email protected]>
---
depends on: http://patchwork.ozlabs.org/project/linux-pci/list/?series=88754

drivers/pci/controller/mobiveil/Kconfig | 5 +
drivers/pci/controller/mobiveil/Makefile | 1 +
drivers/pci/controller/mobiveil/pcie-mobiveil-ep.c | 527 ++++++++++++++++++++
drivers/pci/controller/mobiveil/pcie-mobiveil.c | 100 ++++-
drivers/pci/controller/mobiveil/pcie-mobiveil.h | 64 +++
5 files changed, 691 insertions(+), 6 deletions(-)
create mode 100644 drivers/pci/controller/mobiveil/pcie-mobiveil-ep.c

diff --git a/drivers/pci/controller/mobiveil/Kconfig b/drivers/pci/controller/mobiveil/Kconfig
index 3ddb7d6..c037db6 100644
--- a/drivers/pci/controller/mobiveil/Kconfig
+++ b/drivers/pci/controller/mobiveil/Kconfig
@@ -11,6 +11,11 @@ config PCIE_MOBIVEIL_HOST
depends on PCI_MSI_IRQ_DOMAIN
select PCIE_MOBIVEIL

+config PCIE_MOBIVEIL_EP
+ bool
+ depends on PCI_ENDPOINT
+ select PCIE_MOBIVEIL
+
config PCIE_MOBIVEIL_PLAT
bool "Mobiveil AXI PCIe controller"
depends on ARCH_ZYNQMP || COMPILE_TEST
diff --git a/drivers/pci/controller/mobiveil/Makefile b/drivers/pci/controller/mobiveil/Makefile
index ff66774..4f520b7 100644
--- a/drivers/pci/controller/mobiveil/Makefile
+++ b/drivers/pci/controller/mobiveil/Makefile
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_PCIE_MOBIVEIL) += pcie-mobiveil.o
obj-$(CONFIG_PCIE_MOBIVEIL_HOST) += pcie-mobiveil-host.o
+obj-$(CONFIG_PCIE_MOBIVEIL_EP) += pcie-mobiveil-ep.o
obj-$(CONFIG_PCIE_MOBIVEIL_PLAT) += pcie-mobiveil-plat.o
obj-$(CONFIG_PCI_LAYERSCAPE_GEN4) += pci-layerscape-gen4.o
diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil-ep.c b/drivers/pci/controller/mobiveil/pcie-mobiveil-ep.c
new file mode 100644
index 0000000..16ca7fb
--- /dev/null
+++ b/drivers/pci/controller/mobiveil/pcie-mobiveil-ep.c
@@ -0,0 +1,527 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * Mobiveil PCIe Endpoint controller driver
+ *
+ * Copyright (C) 2018 NXP Semiconductor.
+ * Author: Xiaowei Bao <[email protected]>
+ */
+
+#include <linux/of.h>
+#include <linux/pci-epc.h>
+#include <linux/pci-epf.h>
+#include <linux/platform_device.h>
+#include "pcie-mobiveil.h"
+
+void mobiveil_pcie_ep_linkup(struct mobiveil_pcie_ep *ep)
+{
+ struct pci_epc *epc = ep->epc;
+
+ pci_epc_linkup(epc);
+}
+
+static void __mobiveil_pcie_ep_reset_bar(struct mobiveil_pcie *pcie,
+ enum pci_barno bar)
+{
+ csr_writel(pcie, bar, GPEX_BAR_SELECT);
+ csr_writel(pcie, 0, GPEX_BAR_SIZE_LDW);
+ csr_writel(pcie, 0, GPEX_BAR_SIZE_UDW);
+}
+
+void mobiveil_pcie_ep_reset_bar(struct mobiveil_pcie *pcie,
+ enum pci_barno bar)
+{
+ __mobiveil_pcie_ep_reset_bar(pcie, bar);
+}
+
+static u8 __mobiveil_pcie_ep_find_next_cap(struct mobiveil_pcie *pcie,
+ u8 cap_ptr, u8 cap)
+{
+ u8 cap_id, next_cap_ptr;
+ u16 reg;
+
+ reg = csr_readw(pcie, cap_ptr);
+ next_cap_ptr = (reg & 0xff00) >> 8;
+ cap_id = (reg & 0x00ff);
+
+ if (cap_id == cap)
+ return cap_ptr;
+
+ if (!next_cap_ptr || cap_id > PCI_CAP_ID_MAX)
+ return 0;
+
+ return __mobiveil_pcie_ep_find_next_cap(pcie, next_cap_ptr, cap);
+}
+
+static u8 mobiveil_pcie_ep_find_capability(struct mobiveil_pcie *pcie,
+ u8 cap)
+{
+ u8 next_cap_ptr;
+ u16 reg;
+
+ reg = csr_readw(pcie, PCI_CAPABILITY_LIST);
+ next_cap_ptr = (reg & 0x00ff);
+
+ if (!next_cap_ptr)
+ return 0;
+
+ return __mobiveil_pcie_ep_find_next_cap(pcie, next_cap_ptr, cap);
+}
+
+static int mobiveil_pcie_ep_write_header(struct pci_epc *epc, u8 func_no,
+ struct pci_epf_header *hdr)
+{
+ struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
+ struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
+
+ csr_writew(pcie, hdr->vendorid, PCI_VENDOR_ID);
+ csr_writew(pcie, hdr->deviceid, PCI_DEVICE_ID);
+ csr_writeb(pcie, hdr->revid, PCI_REVISION_ID);
+ csr_writeb(pcie, hdr->progif_code, PCI_CLASS_PROG);
+ csr_writew(pcie, hdr->subclass_code | hdr->baseclass_code << 8,
+ PCI_CLASS_DEVICE);
+ csr_writeb(pcie, hdr->cache_line_size, PCI_CACHE_LINE_SIZE);
+ csr_writew(pcie, hdr->subsys_vendor_id, PCI_SUBSYSTEM_VENDOR_ID);
+ csr_writew(pcie, hdr->subsys_id, PCI_SUBSYSTEM_ID);
+ csr_writeb(pcie, hdr->interrupt_pin, PCI_INTERRUPT_PIN);
+
+ return 0;
+}
+
+static int mobiveil_pcie_ep_inbound_atu(struct mobiveil_pcie_ep *ep,
+ u8 func_no, enum pci_barno bar,
+ dma_addr_t cpu_addr)
+{
+ struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
+
+ program_ib_windows_ep(pcie, func_no, bar, cpu_addr);
+
+ return 0;
+}
+
+static int mobiveil_pcie_ep_outbound_atu(struct mobiveil_pcie_ep *ep,
+ phys_addr_t phys_addr,
+ u64 pci_addr, u8 func_no,
+ size_t size)
+{
+ int ret;
+ u32 free_win;
+ struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
+
+ free_win = find_first_zero_bit(ep->ob_window_map, ep->num_ob_windows);
+ if (free_win >= ep->num_ob_windows) {
+ dev_err(&pcie->pdev->dev, "No free outbound window\n");
+ return -EINVAL;
+ }
+
+ ret = program_ob_windows_ep(pcie, free_win, MEM_WINDOW_TYPE,
+ phys_addr, pci_addr, func_no, size);
+ if (ret < 0) {
+ dev_err(&pcie->pdev->dev, "Failed to program IB window\n");
+ return ret;
+ }
+
+ set_bit(free_win, ep->ob_window_map);
+ ep->outbound_addr[free_win] = phys_addr;
+
+ return 0;
+}
+
+static void mobiveil_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no,
+ struct pci_epf_bar *epf_bar)
+{
+ struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
+ struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
+ enum pci_barno bar = epf_bar->barno;
+
+ if (bar < ep->bar_num) {
+ __mobiveil_pcie_ep_reset_bar(pcie,
+ func_no * ep->bar_num + bar);
+
+ mobiveil_pcie_disable_ib_win_ep(pcie, func_no, bar);
+ }
+}
+
+static int mobiveil_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no,
+ struct pci_epf_bar *epf_bar)
+{
+ int ret;
+ struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
+ struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
+ enum pci_barno bar = epf_bar->barno;
+ size_t size = epf_bar->size;
+
+ if (bar < ep->bar_num) {
+ ret = mobiveil_pcie_ep_inbound_atu(ep, func_no, bar,
+ epf_bar->phys_addr);
+ if (ret)
+ return ret;
+
+ csr_writel(pcie, func_no * ep->bar_num + bar,
+ GPEX_BAR_SELECT);
+ csr_writel(pcie, lower_32_bits(~(size - 1)),
+ GPEX_BAR_SIZE_LDW);
+ csr_writel(pcie, upper_32_bits(~(size - 1)),
+ GPEX_BAR_SIZE_UDW);
+ }
+
+ return 0;
+}
+
+static int mobiveil_pcie_find_index(struct mobiveil_pcie_ep *ep,
+ phys_addr_t addr,
+ u32 *atu_index)
+{
+ u32 index;
+
+ for (index = 0; index < ep->num_ob_windows; index++) {
+ if (ep->outbound_addr[index] != addr)
+ continue;
+ *atu_index = index;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static void mobiveil_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no,
+ phys_addr_t addr)
+{
+ int ret;
+ u32 atu_index;
+ struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
+ struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
+
+ ret = mobiveil_pcie_find_index(ep, addr, &atu_index);
+ if (ret < 0)
+ return;
+
+ mobiveil_pcie_disable_ob_win(pcie, atu_index);
+ clear_bit(atu_index, ep->ob_window_map);
+}
+
+static int mobiveil_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no,
+ phys_addr_t addr,
+ u64 pci_addr, size_t size)
+{
+ int ret;
+ struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
+ struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
+
+ ret = mobiveil_pcie_ep_outbound_atu(ep, addr, pci_addr, func_no, size);
+ if (ret) {
+ dev_err(&pcie->pdev->dev, "Failed to enable address\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mobiveil_pcie_ep_get_msi(struct pci_epc *epc, u8 func_no)
+{
+ struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
+ struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
+ u32 val, reg;
+
+ if (!ep->msi_cap)
+ return -EINVAL;
+
+ reg = ep->msi_cap + PCI_MSI_FLAGS;
+ val = csr_readw(pcie, reg);
+ if (!(val & PCI_MSI_FLAGS_ENABLE))
+ return -EINVAL;
+
+ val = (val & PCI_MSI_FLAGS_QSIZE) >> 4;
+
+ return val;
+}
+
+static int mobiveil_pcie_ep_set_msi(struct pci_epc *epc,
+ u8 func_no, u8 interrupts)
+{
+ struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
+ struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
+ u32 val, reg;
+
+ if (!ep->msi_cap)
+ return -EINVAL;
+
+ reg = ep->msi_cap + PCI_MSI_FLAGS;
+ val = csr_readw(pcie, reg);
+ val &= ~PCI_MSI_FLAGS_QMASK;
+ val |= (interrupts << 1) & PCI_MSI_FLAGS_QMASK;
+ csr_writew(pcie, val, reg);
+
+ return 0;
+}
+
+static int mobiveil_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no)
+{
+ struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
+ struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
+ u32 val, reg;
+
+ if (!ep->msix_cap)
+ return -EINVAL;
+
+ reg = ep->msix_cap + PCI_MSIX_FLAGS;
+ val = csr_readw(pcie, reg);
+ if (!(val & PCI_MSIX_FLAGS_ENABLE))
+ return -EINVAL;
+
+ val &= PCI_MSIX_FLAGS_QSIZE;
+
+ return val;
+}
+
+static int mobiveil_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no,
+ u16 interrupts)
+{
+ struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
+ struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
+ u32 val, reg;
+
+ if (!ep->msix_cap)
+ return -EINVAL;
+
+ reg = ep->msix_cap + PCI_MSIX_FLAGS;
+ val = csr_readw(pcie, reg);
+ val &= ~PCI_MSIX_FLAGS_QSIZE;
+ val |= interrupts;
+ csr_writew(pcie, val, reg);
+
+ return 0;
+}
+
+static int mobiveil_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no,
+ enum pci_epc_irq_type type,
+ u16 interrupt_num)
+{
+ struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
+
+ if (!ep->ops->raise_irq)
+ return -EINVAL;
+
+ return ep->ops->raise_irq(ep, func_no, type, interrupt_num);
+}
+
+static const struct pci_epc_ops epc_ops = {
+ .write_header = mobiveil_pcie_ep_write_header,
+ .set_bar = mobiveil_pcie_ep_set_bar,
+ .clear_bar = mobiveil_pcie_ep_clear_bar,
+ .map_addr = mobiveil_pcie_ep_map_addr,
+ .unmap_addr = mobiveil_pcie_ep_unmap_addr,
+ .set_msi = mobiveil_pcie_ep_set_msi,
+ .get_msi = mobiveil_pcie_ep_get_msi,
+ .set_msix = mobiveil_pcie_ep_set_msix,
+ .get_msix = mobiveil_pcie_ep_get_msix,
+ .raise_irq = mobiveil_pcie_ep_raise_irq,
+};
+
+int mobiveil_pcie_ep_raise_legacy_irq(struct mobiveil_pcie_ep *ep, u8 func_no)
+{
+ struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
+
+ dev_err(&pcie->pdev->dev, "EP cannot trigger legacy IRQs\n");
+
+ return -EINVAL;
+}
+
+int mobiveil_pcie_ep_raise_msi_irq(struct mobiveil_pcie_ep *ep, u8 func_no,
+ u8 interrupt_num)
+{
+ struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
+ struct pci_epc *epc = ep->epc;
+ u16 msg_ctrl, msg_data;
+ u32 msg_addr_lower, msg_addr_upper, reg;
+ u64 msg_addr;
+ u32 func_num;
+ bool has_upper;
+ int ret;
+
+ if (!ep->msi_cap)
+ return -EINVAL;
+
+ /*
+ * In order ot get the PF's MSI capability register value from config
+ * space we need to set the PF number to the PAB_CTRL register.
+ */
+ func_num = csr_readl(pcie, PAB_CTRL);
+ func_num &= ~(FUNC_SEL_MASK << FUNC_SEL_SHIFT);
+ func_num |= (func_no & FUNC_SEL_MASK) << FUNC_SEL_SHIFT;
+ csr_writel(pcie, func_num, PAB_CTRL);
+
+ reg = ep->msi_cap + PCI_MSI_FLAGS;
+ msg_ctrl = csr_readw(pcie, reg);
+ has_upper = !!(msg_ctrl & PCI_MSI_FLAGS_64BIT);
+ reg = ep->msi_cap + PCI_MSI_ADDRESS_LO;
+ msg_addr_lower = csr_readl(pcie, reg);
+ if (has_upper) {
+ reg = ep->msi_cap + PCI_MSI_ADDRESS_HI;
+ msg_addr_upper = csr_readl(pcie, reg);
+ reg = ep->msi_cap + PCI_MSI_DATA_64;
+ msg_data = csr_readw(pcie, reg);
+ } else {
+ msg_addr_upper = 0;
+ reg = ep->msi_cap + PCI_MSI_DATA_32;
+ msg_data = csr_readw(pcie, reg);
+ }
+ msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower;
+
+ /*
+ * clear the FUNC_SEL_SHIFT bits when access other registers except
+ * config space register.
+ */
+ func_num = csr_readl(pcie, PAB_CTRL);
+ func_num &= ~(FUNC_SEL_MASK << FUNC_SEL_SHIFT);
+ csr_writel(pcie, func_num, PAB_CTRL);
+
+ ret = mobiveil_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys,
+ msg_addr, epc->mem->page_size);
+ if (ret)
+ return ret;
+
+ writel(msg_data | (interrupt_num - 1), ep->msi_mem);
+
+ mobiveil_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys);
+
+ return 0;
+}
+
+int mobiveil_pcie_ep_raise_msix_irq(struct mobiveil_pcie_ep *ep, u8 func_no,
+ u16 interrupt_num)
+{
+ struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
+ struct pci_epc *epc = ep->epc;
+ u32 msg_addr_upper, msg_addr_lower;
+ u32 msg_data;
+ u64 msg_addr;
+ u32 func_num;
+ int ret;
+
+ /*
+ * In order ot get the PF's MSI capability register value from config
+ * space we need to set the PF number to the PAB_CTRL register.
+ */
+ func_num = csr_readl(pcie, PAB_CTRL);
+ func_num &= ~(FUNC_SEL_MASK << FUNC_SEL_SHIFT);
+ func_num |= (func_no & FUNC_SEL_MASK) << FUNC_SEL_SHIFT;
+ csr_writel(pcie, func_num, PAB_CTRL);
+
+ msg_addr_lower = csr_readl(pcie, PAB_MSIX_TABLE_PBA_ACCESS +
+ PCI_MSIX_ENTRY_LOWER_ADDR +
+ (interrupt_num - 1) * PCI_MSIX_ENTRY_SIZE);
+ msg_addr_upper = csr_readl(pcie, PAB_MSIX_TABLE_PBA_ACCESS +
+ PCI_MSIX_ENTRY_UPPER_ADDR +
+ (interrupt_num - 1) * PCI_MSIX_ENTRY_SIZE);
+ msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower;
+ msg_data = csr_readl(pcie, PAB_MSIX_TABLE_PBA_ACCESS +
+ PCI_MSIX_ENTRY_DATA +
+ (interrupt_num - 1) * PCI_MSIX_ENTRY_SIZE);
+
+ /*
+ * clear the FUNC_SEL_SHIFT bits when access other registers except
+ * config space registers.
+ */
+ func_num = csr_readl(pcie, PAB_CTRL);
+ func_num &= ~(FUNC_SEL_MASK << FUNC_SEL_SHIFT);
+ csr_writel(pcie, func_num, PAB_CTRL);
+
+ ret = mobiveil_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys,
+ msg_addr, epc->mem->page_size);
+ if (ret)
+ return ret;
+
+ writel(msg_data, ep->msi_mem);
+
+ mobiveil_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys);
+
+ return 0;
+}
+
+void mobiveil_pcie_ep_exit(struct mobiveil_pcie_ep *ep)
+{
+ struct pci_epc *epc = ep->epc;
+
+ pci_epc_mem_free_addr(epc, ep->msi_mem_phys, ep->msi_mem,
+ epc->mem->page_size);
+
+ pci_epc_mem_exit(epc);
+}
+
+int mobiveil_pcie_ep_init(struct mobiveil_pcie_ep *ep)
+{
+ int ret;
+ void *addr;
+ struct pci_epc *epc;
+ struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
+ struct device *dev = &pcie->pdev->dev;
+ struct device_node *np = dev->of_node;
+
+ if (!pcie->csr_axi_slave_base) {
+ dev_err(dev, "csr_base is not populated\n");
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32(np, "num-ob-windows", &ep->num_ob_windows);
+ if (ret < 0) {
+ dev_err(dev, "Unable to read *num-ob-windows* property\n");
+ return ret;
+ }
+
+ if (ep->num_ob_windows > MAX_IATU_OUT) {
+ dev_err(dev, "Invalid *num-ob-windows*\n");
+ return -EINVAL;
+ }
+ ep->ob_window_map = devm_kcalloc(dev,
+ BITS_TO_LONGS(ep->num_ob_windows),
+ sizeof(long),
+ GFP_KERNEL);
+ if (!ep->ob_window_map)
+ return -ENOMEM;
+
+ addr = devm_kcalloc(dev, ep->num_ob_windows, sizeof(phys_addr_t),
+ GFP_KERNEL);
+ if (!addr)
+ return -ENOMEM;
+ ep->outbound_addr = addr;
+
+ mobiveil_pcie_enable_bridge_pio(pcie);
+ mobiveil_pcie_enable_engine_apio(pcie);
+ mobiveil_pcie_enable_engine_ppio(pcie);
+ mobiveil_pcie_enable_msi_ep(pcie);
+
+ epc = devm_pci_epc_create(dev, &epc_ops);
+ if (IS_ERR(epc)) {
+ dev_err(dev, "Failed to create epc device\n");
+ return PTR_ERR(epc);
+ }
+
+ ep->epc = epc;
+ epc_set_drvdata(epc, ep);
+
+ ep->msi_cap = mobiveil_pcie_ep_find_capability(pcie, PCI_CAP_ID_MSI);
+
+ ep->msix_cap = mobiveil_pcie_ep_find_capability(pcie,
+ PCI_CAP_ID_MSIX);
+
+ if (ep->ops->ep_init)
+ ep->ops->ep_init(ep);
+
+ epc->max_functions = ep->pf_num;
+
+ ret = __pci_epc_mem_init(epc, ep->phys_base, ep->addr_size,
+ ep->page_size);
+ if (ret < 0) {
+ dev_err(dev, "Failed to initialize address space\n");
+ return ret;
+ }
+
+ ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys,
+ epc->mem->page_size);
+ if (!ep->msi_mem) {
+ dev_err(dev, "Failed to reserve memory for MSI/MSI-X\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil.c b/drivers/pci/controller/mobiveil/pcie-mobiveil.c
index 49d471b..d1fdfed 100644
--- a/drivers/pci/controller/mobiveil/pcie-mobiveil.c
+++ b/drivers/pci/controller/mobiveil/pcie-mobiveil.c
@@ -210,6 +210,57 @@ void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, u64 cpu_addr,
pcie->ob_wins_configured++;
}

+int program_ob_windows_ep(struct mobiveil_pcie *pcie, int win_num, int type,
+ u64 phys, u64 bus_addr, u8 func, u64 size)
+{
+ u32 val;
+ u32 size_h, size_l;
+
+ if (size & (size - 1))
+ size = 1 << (1 + ilog2(size));
+
+ size_h = upper_32_bits(~(size - 1));
+ size_l = lower_32_bits(~(size - 1));
+
+ val = csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num));
+ val &= ~(WIN_TYPE_MASK << WIN_TYPE_SHIFT |
+ WIN_SIZE_MASK << WIN_SIZE_SHIFT);
+ val |= 1 << WIN_ENABLE_SHIFT | type << WIN_TYPE_SHIFT |
+ (size_l & (WIN_SIZE_MASK << WIN_SIZE_SHIFT));
+ csr_writel(pcie, val, PAB_AXI_AMAP_CTRL(win_num));
+
+ csr_writel(pcie, func, PAB_AXI_AMAP_PCI_HDR_PARAM(win_num));
+ csr_writel(pcie, lower_32_bits(phys), PAB_AXI_AMAP_AXI_WIN(win_num));
+ csr_writel(pcie, upper_32_bits(phys),
+ PAB_EXT_AXI_AMAP_AXI_WIN(win_num));
+ csr_writel(pcie, lower_32_bits(bus_addr),
+ PAB_AXI_AMAP_PEX_WIN_L(win_num));
+ csr_writel(pcie, upper_32_bits(bus_addr),
+ PAB_AXI_AMAP_PEX_WIN_H(win_num));
+ csr_writel(pcie, size_h, PAB_EXT_AXI_AMAP_SIZE(win_num));
+
+ return 0;
+}
+
+void program_ib_windows_ep(struct mobiveil_pcie *pcie, u8 func_no,
+ int bar, u64 phys)
+{
+ csr_writel(pcie, upper_32_bits(phys),
+ PAB_EXT_PEX_BAR_AMAP(func_no, bar));
+ csr_writel(pcie, lower_32_bits(phys) | PEX_BAR_AMAP_EN,
+ PAB_PEX_BAR_AMAP(func_no, bar));
+}
+
+void mobiveil_pcie_disable_ib_win_ep(struct mobiveil_pcie *pcie,
+ u8 func_no, u8 bar)
+{
+ u32 val;
+
+ val = csr_readl(pcie, PAB_PEX_BAR_AMAP(func_no, bar));
+ val &= ~(1 << 0);
+ csr_writel(pcie, val, PAB_PEX_BAR_AMAP(func_no, bar));
+}
+
int mobiveil_bringup_link(struct mobiveil_pcie *pcie)
{
int retries;
@@ -227,20 +278,57 @@ int mobiveil_bringup_link(struct mobiveil_pcie *pcie)
return -ETIMEDOUT;
}

-void mobiveil_pcie_disable_ib_win(struct mobiveil_pcie *pci, int win_num)
+void mobiveil_pcie_disable_ib_win(struct mobiveil_pcie *pcie, int win_num)
{
u32 val;

- val = csr_readl(pci, PAB_PEX_AMAP_CTRL(win_num));
+ val = csr_readl(pcie, PAB_PEX_AMAP_CTRL(win_num));
val &= ~(1 << AMAP_CTRL_EN_SHIFT);
- csr_writel(pci, val, PAB_PEX_AMAP_CTRL(win_num));
+ csr_writel(pcie, val, PAB_PEX_AMAP_CTRL(win_num));
}

-void mobiveil_pcie_disable_ob_win(struct mobiveil_pcie *pci, int win_num)
+void mobiveil_pcie_disable_ob_win(struct mobiveil_pcie *pcie, int win_num)
{
u32 val;

- val = csr_readl(pci, PAB_AXI_AMAP_CTRL(win_num));
+ val = csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num));
val &= ~(1 << WIN_ENABLE_SHIFT);
- csr_writel(pci, val, PAB_AXI_AMAP_CTRL(win_num));
+ csr_writel(pcie, val, PAB_AXI_AMAP_CTRL(win_num));
+}
+
+void mobiveil_pcie_enable_bridge_pio(struct mobiveil_pcie *pcie)
+{
+ u32 val;
+
+ val = csr_readl(pcie, PAB_CTRL);
+ val |= 1 << AMBA_PIO_ENABLE_SHIFT;
+ val |= 1 << PEX_PIO_ENABLE_SHIFT;
+ csr_writel(pcie, val, PAB_CTRL);
+}
+
+void mobiveil_pcie_enable_engine_apio(struct mobiveil_pcie *pcie)
+{
+ u32 val;
+
+ val = csr_readl(pcie, PAB_AXI_PIO_CTRL);
+ val |= APIO_EN_MASK;
+ csr_writel(pcie, val, PAB_AXI_PIO_CTRL);
+}
+
+void mobiveil_pcie_enable_engine_ppio(struct mobiveil_pcie *pcie)
+{
+ u32 val;
+
+ val = csr_readl(pcie, PAB_PEX_PIO_CTRL);
+ val |= 1 << PIO_ENABLE_SHIFT;
+ csr_writel(pcie, val, PAB_PEX_PIO_CTRL);
+}
+
+void mobiveil_pcie_enable_msi_ep(struct mobiveil_pcie *pcie)
+{
+ u32 val;
+
+ val = csr_readl(pcie, PAB_INTP_AMBA_MISC_ENB);
+ val |= 1 << 0;
+ csr_writel(pcie, val, PAB_INTP_AMBA_MISC_ENB);
}
diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil.h b/drivers/pci/controller/mobiveil/pcie-mobiveil.h
index f0e2e4a..275c68f 100644
--- a/drivers/pci/controller/mobiveil/pcie-mobiveil.h
+++ b/drivers/pci/controller/mobiveil/pcie-mobiveil.h
@@ -15,6 +15,10 @@
#include <linux/msi.h>
#include "../../pci.h"

+#include <linux/pci-epc.h>
+#include <linux/pci-epf.h>
+
+#define MAX_IATU_OUT 256
/* register offsets and bit positions */

/*
@@ -40,6 +44,9 @@
#define PAGE_SEL_MASK 0x3f
#define PAGE_LO_MASK 0x3ff
#define PAGE_SEL_OFFSET_SHIFT 10
+#define FUNC_SEL_SHIFT 19
+#define FUNC_SEL_MASK 0x1ff
+#define MSI_SW_CTRL_EN (1 << 29)

#define PAB_ACTIVITY_STAT 0x81c

@@ -100,6 +107,19 @@
#define PAB_PEX_AMAP_PEX_WIN_L(win) PAB_REG_ADDR(0x4ba8, win)
#define PAB_PEX_AMAP_PEX_WIN_H(win) PAB_REG_ADDR(0x4bac, win)

+/* PPIO WINs EP mode */
+#define PAB_PEX_BAR_AMAP(func, bar) (0x1ba0 + 0x20 * func + 4 * bar)
+#define PAB_EXT_PEX_BAR_AMAP(func, bar) (0x84a0 + 0x20 * func + 4 * bar)
+#define PEX_BAR_AMAP_EN (1 << 0)
+
+#define PAB_AXI_AMAP_PCI_HDR_PARAM(idx) (0x5ba0 + 0x04 * idx)
+#define PAB_MSIX_TABLE_PBA_ACCESS 0xD000
+
+#define GPEX_BAR_ENABLE 0x4D4
+#define GPEX_BAR_SIZE_LDW 0x4D8
+#define GPEX_BAR_SIZE_UDW 0x4DC
+#define GPEX_BAR_SELECT 0x4E0
+
/* starting offset of INTX bits in status register */
#define PAB_INTX_START 5

@@ -137,6 +157,7 @@
((off >> PAGE_SEL_OFFSET_SHIFT) & PAGE_SEL_MASK)

struct mobiveil_pcie;
+struct mobiveil_pcie_ep;

struct mobiveil_msi { /* MSI information */
struct mutex lock; /* protect bitmap variable */
@@ -169,6 +190,29 @@ struct mobiveil_pab_ops {
int (*host_init)(struct mobiveil_pcie *pcie);
};

+struct mobiveil_pcie_ep_ops {
+ void (*ep_init)(struct mobiveil_pcie_ep *ep);
+ int (*raise_irq)(struct mobiveil_pcie_ep *ep, u8 func_no,
+ enum pci_epc_irq_type type, u16 interrupt_num);
+};
+
+struct mobiveil_pcie_ep {
+ struct pci_epc *epc;
+ struct mobiveil_pcie_ep_ops *ops;
+ phys_addr_t phys_base;
+ size_t addr_size;
+ size_t page_size;
+ phys_addr_t *outbound_addr;
+ unsigned long *ob_window_map;
+ u32 num_ob_windows;
+ void __iomem *msi_mem;
+ phys_addr_t msi_mem_phys;
+ u8 msi_cap; /* MSI capability offset */
+ u8 msix_cap; /* MSI-X capability offset */
+ u8 bar_num;
+ u32 pf_num;
+};
+
struct mobiveil_pcie {
struct platform_device *pdev;
struct list_head *resources;
@@ -181,7 +225,10 @@ struct mobiveil_pcie {
u32 ib_wins_configured; /* configured inbound windows */
const struct mobiveil_pab_ops *ops;
struct root_port rp;
+ struct mobiveil_pcie_ep ep;
};
+#define to_mobiveil_pcie_from_ep(endpoint) \
+ container_of((endpoint), struct mobiveil_pcie, ep)

int mobiveil_pcie_host_probe(struct mobiveil_pcie *pcie);
int mobiveil_host_init(struct mobiveil_pcie *pcie, bool reinit);
@@ -226,4 +273,21 @@ static inline void csr_writeb(struct mobiveil_pcie *pcie, u32 val, u32 off)
csr_write(pcie, val, off, 0x1);
}

+void program_ib_windows_ep(struct mobiveil_pcie *pcie, u8 func_no,
+ int bar, u64 phys);
+int program_ob_windows_ep(struct mobiveil_pcie *pcie, int win_num, int type,
+ u64 phys, u64 bus_addr, u8 func, u64 size);
+void mobiveil_pcie_disable_ib_win_ep(struct mobiveil_pcie *pci,
+ u8 func_no, u8 bar);
+int mobiveil_pcie_ep_init(struct mobiveil_pcie_ep *ep);
+int mobiveil_pcie_ep_raise_legacy_irq(struct mobiveil_pcie_ep *ep, u8 func_no);
+int mobiveil_pcie_ep_raise_msi_irq(struct mobiveil_pcie_ep *ep, u8 func_no,
+ u8 interrupt_num);
+int mobiveil_pcie_ep_raise_msix_irq(struct mobiveil_pcie_ep *ep, u8 func_no,
+ u16 interrupt_num);
+void mobiveil_pcie_ep_reset_bar(struct mobiveil_pcie *pci, enum pci_barno bar);
+void mobiveil_pcie_enable_bridge_pio(struct mobiveil_pcie *pci);
+void mobiveil_pcie_enable_engine_apio(struct mobiveil_pcie *pci);
+void mobiveil_pcie_enable_engine_ppio(struct mobiveil_pcie *pci);
+void mobiveil_pcie_enable_msi_ep(struct mobiveil_pcie *pci);
#endif /* _PCIE_MOBIVEIL_H */
--
1.7.1



2019-02-18 09:55:21

by Bao Xiaowei

[permalink] [raw]
Subject: [PATCH 5/6] arm64: dts: freescale: lx2160a: add pcie EP mode DT nodes

The LX2160A PCIe EP mode node.

Signed-off-by: Xiaowei Bao <[email protected]>
---
depends on: http://patchwork.ozlabs.org/project/linux-pci/list/?series=88754

arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi | 56 ++++++++++++++++++++++++
1 files changed, 56 insertions(+), 0 deletions(-)

diff --git a/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi b/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi
index 3a64f6e..5fee592 100644
--- a/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi
@@ -923,6 +923,15 @@
status = "disabled";
};

+ pcie_ep@3400000 {
+ compatible = "fsl,lx2160a-pcie-ep";
+ reg = <0x00 0x03400000 0x0 0x00100000
+ 0x80 0x00000000 0x8 0x00000000>;
+ reg-names = "regs", "addr_space";
+ num-ob-windows = <256>;
+ status = "disabled";
+ };
+
pcie@3500000 {
compatible = "fsl,lx2160a-pcie";
reg = <0x00 0x03500000 0x0 0x00100000 /* controller registers */
@@ -950,6 +959,15 @@
status = "disabled";
};

+ pcie_ep@3500000 {
+ compatible = "fsl,lx2160a-pcie-ep";
+ reg = <0x00 0x03500000 0x0 0x00100000
+ 0x88 0x00000000 0x8 0x00000000>;
+ reg-names = "regs", "addr_space";
+ num-ob-windows = <256>;
+ status = "disabled";
+ };
+
pcie@3600000 {
compatible = "fsl,lx2160a-pcie";
reg = <0x00 0x03600000 0x0 0x00100000 /* controller registers */
@@ -977,6 +995,16 @@
status = "disabled";
};

+ pcie_ep@3600000 {
+ compatible = "fsl,lx2160a-pcie-ep";
+ reg = <0x00 0x03600000 0x0 0x00100000
+ 0x90 0x00000000 0x8 0x00000000>;
+ reg-names = "regs", "addr_space";
+ num-ob-windows = <256>;
+ max-functions = <2>;
+ status = "disabled";
+ };
+
pcie@3700000 {
compatible = "fsl,lx2160a-pcie";
reg = <0x00 0x03700000 0x0 0x00100000 /* controller registers */
@@ -1004,6 +1032,15 @@
status = "disabled";
};

+ pcie_ep@3700000 {
+ compatible = "fsl,lx2160a-pcie-ep";
+ reg = <0x00 0x03700000 0x0 0x00100000
+ 0x98 0x00000000 0x8 0x00000000>;
+ reg-names = "regs", "addr_space";
+ num-ob-windows = <256>;
+ status = "disabled";
+ };
+
pcie@3800000 {
compatible = "fsl,lx2160a-pcie";
reg = <0x00 0x03800000 0x0 0x00100000 /* controller registers */
@@ -1031,6 +1068,16 @@
status = "disabled";
};

+ pcie_ep@3800000 {
+ compatible = "fsl,lx2160a-pcie-ep";
+ reg = <0x00 0x03800000 0x0 0x00100000
+ 0xa0 0x00000000 0x8 0x00000000>;
+ reg-names = "regs", "addr_space";
+ num-ob-windows = <256>;
+ max-functions = <2>;
+ status = "disabled";
+ };
+
pcie@3900000 {
compatible = "fsl,lx2160a-pcie";
reg = <0x00 0x03900000 0x0 0x00100000 /* controller registers */
@@ -1058,5 +1105,14 @@
status = "disabled";
};

+ pcie_ep@3900000 {
+ compatible = "fsl,lx2160a-pcie-ep";
+ reg = <0x00 0x03900000 0x0 0x00100000
+ 0xa8 0x00000000 0x8 0x00000000>;
+ reg-names = "regs", "addr_space";
+ num-ob-windows = <256>;
+ status = "disabled";
+ };
+
};
};
--
1.7.1


2019-02-18 09:55:23

by Bao Xiaowei

[permalink] [raw]
Subject: [PATCH 3/6] PCI: mobiveil: Add PCIe Gen4 EP driver for NXP Layerscape SoCs

This PCIe controller is based on the Mobiveil GPEX IP, it work in EP
mode if select this config opteration.

Signed-off-by: Xiaowei Bao <[email protected]>
---
depends on: http://patchwork.ozlabs.org/project/linux-pci/list/?series=88754

drivers/pci/controller/mobiveil/Kconfig | 17 ++-
drivers/pci/controller/mobiveil/Makefile | 1 +
.../controller/mobiveil/pci-layerscape-gen4-ep.c | 166 ++++++++++++++++++++
3 files changed, 181 insertions(+), 3 deletions(-)
create mode 100644 drivers/pci/controller/mobiveil/pci-layerscape-gen4-ep.c

diff --git a/drivers/pci/controller/mobiveil/Kconfig b/drivers/pci/controller/mobiveil/Kconfig
index c037db6..16ee617 100644
--- a/drivers/pci/controller/mobiveil/Kconfig
+++ b/drivers/pci/controller/mobiveil/Kconfig
@@ -27,13 +27,24 @@ config PCIE_MOBIVEIL_PLAT
for address translation and it is a PCIe Gen4 IP.

config PCI_LAYERSCAPE_GEN4
- bool "Freescale Layerscpe PCIe Gen4 controller"
+ bool "Freescale Layerscpe PCIe Gen4 controller in RC mode"
depends on PCI
depends on OF && (ARM64 || ARCH_LAYERSCAPE)
depends on PCI_MSI_IRQ_DOMAIN
select PCIE_MOBIVEIL_HOST
help
Say Y here if you want PCIe Gen4 controller support on
- Layerscape SoCs. The PCIe controller can work in RC or
- EP mode according to RCW[HOST_AGT_PEX] setting.
+ Layerscape SoCs. And the PCIe controller work in RC mode
+ by setting the RCW[HOST_AGT_PEX] to 0.
+
+config PCI_LAYERSCAPE_GEN4_EP
+ bool "Freescale Layerscpe PCIe Gen4 controller in EP mode"
+ depends on PCI
+ depends on OF && (ARM64 || ARCH_LAYERSCAPE)
+ depends on PCI_ENDPOINT
+ select PCIE_MOBIVEIL_EP
+ help
+ Say Y here if you want PCIe Gen4 controller support on
+ Layerscape SoCs. And the PCIe controller work in EP mode
+ by setting the RCW[HOST_AGT_PEX] to 1.
endmenu
diff --git a/drivers/pci/controller/mobiveil/Makefile b/drivers/pci/controller/mobiveil/Makefile
index 4f520b7..cd907a7 100644
--- a/drivers/pci/controller/mobiveil/Makefile
+++ b/drivers/pci/controller/mobiveil/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_PCIE_MOBIVEIL_HOST) += pcie-mobiveil-host.o
obj-$(CONFIG_PCIE_MOBIVEIL_EP) += pcie-mobiveil-ep.o
obj-$(CONFIG_PCIE_MOBIVEIL_PLAT) += pcie-mobiveil-plat.o
obj-$(CONFIG_PCI_LAYERSCAPE_GEN4) += pci-layerscape-gen4.o
+obj-$(CONFIG_PCI_LAYERSCAPE_GEN4_EP) += pci-layerscape-gen4-ep.o
diff --git a/drivers/pci/controller/mobiveil/pci-layerscape-gen4-ep.c b/drivers/pci/controller/mobiveil/pci-layerscape-gen4-ep.c
new file mode 100644
index 0000000..dc3589d
--- /dev/null
+++ b/drivers/pci/controller/mobiveil/pci-layerscape-gen4-ep.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCIe controller EP driver for Freescale Layerscape SoCs
+ *
+ * Copyright (C) 2018 NXP Semiconductor.
+ *
+ * Author: Xiaowei Bao <[email protected]>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/of_pci.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/resource.h>
+
+#include "pcie-mobiveil.h"
+
+struct ls_pcie_g4_ep {
+ struct mobiveil_pcie *mv_pci;
+};
+
+#define to_ls_pcie_g4_ep(x) dev_get_drvdata((x)->dev)
+
+static const struct of_device_id ls_pcie_g4_ep_of_match[] = {
+ { .compatible = "fsl,lx2160a-pcie-ep",},
+ { },
+};
+
+static void ls_pcie_g4_get_bar_num(struct mobiveil_pcie_ep *ep)
+{
+ struct mobiveil_pcie *mv_pci = to_mobiveil_pcie_from_ep(ep);
+ u32 type, reg;
+ u8 bar;
+
+ ep->bar_num = BAR_5 + 1;
+
+ for (bar = BAR_0; bar <= BAR_5; bar++) {
+ reg = PCI_BASE_ADDRESS_0 + (4 * bar);
+ type = csr_readl(mv_pci, reg) &
+ PCI_BASE_ADDRESS_MEM_TYPE_MASK;
+ if (type & PCI_BASE_ADDRESS_MEM_TYPE_64)
+ ep->bar_num--;
+ }
+}
+
+static void ls_pcie_g4_ep_init(struct mobiveil_pcie_ep *ep)
+{
+ struct mobiveil_pcie *mv_pci = to_mobiveil_pcie_from_ep(ep);
+ struct pci_epc *epc = ep->epc;
+ enum pci_barno bar;
+ int win_idx;
+
+ ls_pcie_g4_get_bar_num(ep);
+
+ for (bar = BAR_0; bar < (ep->bar_num * ep->pf_num); bar++)
+ mobiveil_pcie_ep_reset_bar(mv_pci, bar);
+
+ for (win_idx = 0; win_idx < MAX_IATU_OUT; win_idx++)
+ mobiveil_pcie_disable_ob_win(mv_pci, win_idx);
+
+ epc->features |= EPC_FEATURE_NO_LINKUP_NOTIFIER;
+ epc->features |= EPC_FEATURE_MSIX_AVAILABLE;
+}
+
+static int ls_pcie_g4_ep_raise_irq(struct mobiveil_pcie_ep *ep, u8 func_no,
+ enum pci_epc_irq_type type,
+ u16 interrupt_num)
+{
+ struct mobiveil_pcie *mv_pci = to_mobiveil_pcie_from_ep(ep);
+
+ switch (type) {
+ case PCI_EPC_IRQ_LEGACY:
+ return mobiveil_pcie_ep_raise_legacy_irq(ep, func_no);
+ case PCI_EPC_IRQ_MSI:
+ return mobiveil_pcie_ep_raise_msi_irq(ep, func_no,
+ interrupt_num);
+ case PCI_EPC_IRQ_MSIX:
+ return mobiveil_pcie_ep_raise_msix_irq(ep, func_no,
+ interrupt_num);
+ default:
+ dev_err(&mv_pci->pdev->dev, "UNKNOWN IRQ type\n");
+ }
+
+ return 0;
+}
+
+static struct mobiveil_pcie_ep_ops pcie_ep_ops = {
+ .ep_init = ls_pcie_g4_ep_init,
+ .raise_irq = ls_pcie_g4_ep_raise_irq,
+};
+
+static int __init ls_pcie_gen4_add_pcie_ep(struct ls_pcie_g4_ep *ls_pcie_g4_ep,
+ struct platform_device *pdev)
+{
+ struct mobiveil_pcie *mv_pci = ls_pcie_g4_ep->mv_pci;
+ struct device *dev = &pdev->dev;
+ struct mobiveil_pcie_ep *ep;
+ struct resource *res;
+ int ret;
+ struct device_node *np = dev->of_node;
+
+ ep = &mv_pci->ep;
+ ep->ops = &pcie_ep_ops;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "addr_space");
+ if (!res)
+ return -EINVAL;
+
+ ep->phys_base = res->start;
+ ep->addr_size = resource_size(res);
+
+ ret = of_property_read_u32(np, "max-functions", &ep->pf_num);
+ if (ret < 0)
+ ep->pf_num = 1;
+
+ ret = mobiveil_pcie_ep_init(ep);
+ if (ret) {
+ dev_err(dev, "failed to initialize endpoint\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __init ls_pcie_g4_ep_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mobiveil_pcie *mv_pci;
+ struct ls_pcie_g4_ep *ls_pcie_g4_ep;
+ struct resource *res;
+ int ret;
+
+ ls_pcie_g4_ep = devm_kzalloc(dev, sizeof(*ls_pcie_g4_ep), GFP_KERNEL);
+ if (!ls_pcie_g4_ep)
+ return -ENOMEM;
+
+ mv_pci = devm_kzalloc(dev, sizeof(*mv_pci), GFP_KERNEL);
+ if (!mv_pci)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
+ mv_pci->csr_axi_slave_base = devm_pci_remap_cfg_resource(dev, res);
+ if (IS_ERR(mv_pci->csr_axi_slave_base))
+ return PTR_ERR(mv_pci->csr_axi_slave_base);
+
+ mv_pci->pdev = pdev;
+ ls_pcie_g4_ep->mv_pci = mv_pci;
+
+ platform_set_drvdata(pdev, ls_pcie_g4_ep);
+
+ ret = ls_pcie_gen4_add_pcie_ep(ls_pcie_g4_ep, pdev);
+
+ return ret;
+}
+
+static struct platform_driver ls_pcie_g4_ep_driver = {
+ .driver = {
+ .name = "layerscape-pcie-gen4-ep",
+ .of_match_table = ls_pcie_g4_ep_of_match,
+ .suppress_bind_attrs = true,
+ },
+};
+builtin_platform_driver_probe(ls_pcie_g4_ep_driver, ls_pcie_g4_ep_probe);
--
1.7.1


2019-02-18 09:55:36

by Bao Xiaowei

[permalink] [raw]
Subject: [PATCH 4/6] PCI: mobiveil: Add workaround for unsupported request error

Errata: unsupported request error on inbound posted write
transaction, PCIe controller reports advisory error instead
of uncorrectable error message to RC.

Signed-off-by: Xiaowei Bao <[email protected]>
---
depends on: http://patchwork.ozlabs.org/project/linux-pci/list/?series=88754

.../controller/mobiveil/pci-layerscape-gen4-ep.c | 14 +++++++++++++-
drivers/pci/controller/mobiveil/pcie-mobiveil.h | 4 ++++
2 files changed, 17 insertions(+), 1 deletions(-)

diff --git a/drivers/pci/controller/mobiveil/pci-layerscape-gen4-ep.c b/drivers/pci/controller/mobiveil/pci-layerscape-gen4-ep.c
index dc3589d..3d980f5 100644
--- a/drivers/pci/controller/mobiveil/pci-layerscape-gen4-ep.c
+++ b/drivers/pci/controller/mobiveil/pci-layerscape-gen4-ep.c
@@ -51,7 +51,19 @@ static void ls_pcie_g4_ep_init(struct mobiveil_pcie_ep *ep)
struct mobiveil_pcie *mv_pci = to_mobiveil_pcie_from_ep(ep);
struct pci_epc *epc = ep->epc;
enum pci_barno bar;
- int win_idx;
+ int win_idx, val;
+
+ /*
+ * Errata: unsupported request error on inbound posted write
+ * transaction, PCIe controller reports advisory error instead
+ * of uncorrectable error message to RC.
+ * workaround: set the bit20(unsupported_request_Error_severity) with
+ * value 1 in uncorrectable_Error_Severity_Register, make the
+ * unsupported request error generate the fatal error.
+ */
+ val = csr_readl(mv_pci, CFG_UNCORRECTABLE_ERROR_SEVERITY);
+ val |= 1 << UNSUPPORTED_REQUEST_ERROR_SHIFT;
+ csr_writel(mv_pci, val, CFG_UNCORRECTABLE_ERROR_SEVERITY);

ls_pcie_g4_get_bar_num(ep);

diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil.h b/drivers/pci/controller/mobiveil/pcie-mobiveil.h
index 275c68f..7dc2a12 100644
--- a/drivers/pci/controller/mobiveil/pcie-mobiveil.h
+++ b/drivers/pci/controller/mobiveil/pcie-mobiveil.h
@@ -120,6 +120,10 @@
#define GPEX_BAR_SIZE_UDW 0x4DC
#define GPEX_BAR_SELECT 0x4E0

+#define CFG_UNCORRECTABLE_ERROR_SEVERITY 0x10c
+#define UNSUPPORTED_REQUEST_ERROR_SHIFT 20
+#define CFG_UNCORRECTABLE_ERROR_MASK 0x108
+
/* starting offset of INTX bits in status register */
#define PAB_INTX_START 5

--
1.7.1


2019-02-18 09:55:49

by Bao Xiaowei

[permalink] [raw]
Subject: [PATCH 2/6] dt-bindings: add DT binding for the layerscape PCIe GEN4 controller with EP mode

Add the documentation for the Device Tree binding for the layerscape
PCIe GEN4 controller with EP mode.

Signed-off-by: Xiaowei Bao <[email protected]>
---
depends on: http://patchwork.ozlabs.org/project/linux-pci/list/?series=88754

.../bindings/pci/layerscape-pci-gen4.txt | 5 ++++-
1 files changed, 4 insertions(+), 1 deletions(-)

diff --git a/Documentation/devicetree/bindings/pci/layerscape-pci-gen4.txt b/Documentation/devicetree/bindings/pci/layerscape-pci-gen4.txt
index b40fb5d..1b9d0bf 100644
--- a/Documentation/devicetree/bindings/pci/layerscape-pci-gen4.txt
+++ b/Documentation/devicetree/bindings/pci/layerscape-pci-gen4.txt
@@ -5,7 +5,10 @@ the common properties defined in mobiveil-pcie.txt.

Required properties:
- compatible: should contain the platform identifier such as:
- "fsl,lx2160a-pcie"
+ RC mode:
+ "fsl,lx2160a-pcie"
+ EP mode:
+ "fsl,lx2160a-pcie-ep"
- reg: base addresses and lengths of the PCIe controller register blocks.
"csr_axi_slave": Bridge config registers
"config_axi_slave": PCIe controller registers
--
1.7.1


2019-02-18 09:56:53

by Bao Xiaowei

[permalink] [raw]
Subject: [PATCH 6/6] misc: pci_endpoint_test: Add the layerscape PCIe GEN4 EP device support

Add the layerscape PCIE GEN4 EP device support in pci_endpoint_test driver.

Signed-off-by: Xiaowei Bao <[email protected]>
---
drivers/misc/pci_endpoint_test.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c
index 896e2df..8c3435b 100644
--- a/drivers/misc/pci_endpoint_test.c
+++ b/drivers/misc/pci_endpoint_test.c
@@ -789,6 +789,7 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev)
{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA74x) },
{ PCI_DEVICE(PCI_VENDOR_ID_TI, PCI_DEVICE_ID_TI_DRA72x) },
{ PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, 0xedda) },
+ { PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, 0x8d80) },
{ }
};
MODULE_DEVICE_TABLE(pci, pci_endpoint_test_tbl);
--
1.7.1


2019-02-28 23:13:25

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH 2/6] dt-bindings: add DT binding for the layerscape PCIe GEN4 controller with EP mode

On Mon, 18 Feb 2019 17:46:39 +0800, Xiaowei Bao wrote:
> Add the documentation for the Device Tree binding for the layerscape
> PCIe GEN4 controller with EP mode.
>
> Signed-off-by: Xiaowei Bao <[email protected]>
> ---
> depends on: http://patchwork.ozlabs.org/project/linux-pci/list/?series=88754
>
> .../bindings/pci/layerscape-pci-gen4.txt | 5 ++++-
> 1 files changed, 4 insertions(+), 1 deletions(-)
>

Reviewed-by: Rob Herring <[email protected]>

2019-03-08 06:41:49

by Subrahmanya Lingappa

[permalink] [raw]
Subject: Re: [PATCH 1/6] PCI: mobiveil: Add the EP mode support

Xiaowei,


On Mon, Feb 18, 2019 at 3:22 PM Xiaowei Bao <[email protected]> wrote:
>
> Add the EP mode support for Mobiveil base on endpoint framework.
>
> Signed-off-by: Xiaowei Bao <[email protected]>
> ---
> depends on: http://patchwork.ozlabs.org/project/linux-pci/list/?series=88754
>
> drivers/pci/controller/mobiveil/Kconfig | 5 +
> drivers/pci/controller/mobiveil/Makefile | 1 +
> drivers/pci/controller/mobiveil/pcie-mobiveil-ep.c | 527 ++++++++++++++++++++
> drivers/pci/controller/mobiveil/pcie-mobiveil.c | 100 ++++-
> drivers/pci/controller/mobiveil/pcie-mobiveil.h | 64 +++
> 5 files changed, 691 insertions(+), 6 deletions(-)
> create mode 100644 drivers/pci/controller/mobiveil/pcie-mobiveil-ep.c
>
> diff --git a/drivers/pci/controller/mobiveil/Kconfig b/drivers/pci/controller/mobiveil/Kconfig
> index 3ddb7d6..c037db6 100644
> --- a/drivers/pci/controller/mobiveil/Kconfig
> +++ b/drivers/pci/controller/mobiveil/Kconfig
> @@ -11,6 +11,11 @@ config PCIE_MOBIVEIL_HOST
> depends on PCI_MSI_IRQ_DOMAIN
> select PCIE_MOBIVEIL
>
> +config PCIE_MOBIVEIL_EP
> + bool
> + depends on PCI_ENDPOINT
> + select PCIE_MOBIVEIL
> +
> config PCIE_MOBIVEIL_PLAT
> bool "Mobiveil AXI PCIe controller"
> depends on ARCH_ZYNQMP || COMPILE_TEST
> diff --git a/drivers/pci/controller/mobiveil/Makefile b/drivers/pci/controller/mobiveil/Makefile
> index ff66774..4f520b7 100644
> --- a/drivers/pci/controller/mobiveil/Makefile
> +++ b/drivers/pci/controller/mobiveil/Makefile
> @@ -1,5 +1,6 @@
> # SPDX-License-Identifier: GPL-2.0
> obj-$(CONFIG_PCIE_MOBIVEIL) += pcie-mobiveil.o
> obj-$(CONFIG_PCIE_MOBIVEIL_HOST) += pcie-mobiveil-host.o
> +obj-$(CONFIG_PCIE_MOBIVEIL_EP) += pcie-mobiveil-ep.o
> obj-$(CONFIG_PCIE_MOBIVEIL_PLAT) += pcie-mobiveil-plat.o
> obj-$(CONFIG_PCI_LAYERSCAPE_GEN4) += pci-layerscape-gen4.o
> diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil-ep.c b/drivers/pci/controller/mobiveil/pcie-mobiveil-ep.c
> new file mode 100644
> index 0000000..16ca7fb
> --- /dev/null
> +++ b/drivers/pci/controller/mobiveil/pcie-mobiveil-ep.c
> @@ -0,0 +1,527 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/**
> + * Mobiveil PCIe Endpoint controller driver
> + *
> + * Copyright (C) 2018 NXP Semiconductor.
> + * Author: Xiaowei Bao <[email protected]>
> + */
> +
> +#include <linux/of.h>
> +#include <linux/pci-epc.h>
> +#include <linux/pci-epf.h>
> +#include <linux/platform_device.h>
> +#include "pcie-mobiveil.h"
> +
> +void mobiveil_pcie_ep_linkup(struct mobiveil_pcie_ep *ep)
> +{
> + struct pci_epc *epc = ep->epc;
> +
> + pci_epc_linkup(epc);
> +}
> +
> +static void __mobiveil_pcie_ep_reset_bar(struct mobiveil_pcie *pcie,
> + enum pci_barno bar)
> +{
> + csr_writel(pcie, bar, GPEX_BAR_SELECT);
> + csr_writel(pcie, 0, GPEX_BAR_SIZE_LDW);
> + csr_writel(pcie, 0, GPEX_BAR_SIZE_UDW);
> +}
> +
> +void mobiveil_pcie_ep_reset_bar(struct mobiveil_pcie *pcie,
> + enum pci_barno bar)
> +{
> + __mobiveil_pcie_ep_reset_bar(pcie, bar);
> +}
> +
> +static u8 __mobiveil_pcie_ep_find_next_cap(struct mobiveil_pcie *pcie,
> + u8 cap_ptr, u8 cap)
> +{
> + u8 cap_id, next_cap_ptr;
> + u16 reg;
> +
> + reg = csr_readw(pcie, cap_ptr);
> + next_cap_ptr = (reg & 0xff00) >> 8;
> + cap_id = (reg & 0x00ff);
> +
> + if (cap_id == cap)
> + return cap_ptr;
> +
> + if (!next_cap_ptr || cap_id > PCI_CAP_ID_MAX)
> + return 0;
> +
> + return __mobiveil_pcie_ep_find_next_cap(pcie, next_cap_ptr, cap);
> +}
> +
> +static u8 mobiveil_pcie_ep_find_capability(struct mobiveil_pcie *pcie,
> + u8 cap)
> +{
> + u8 next_cap_ptr;
> + u16 reg;
> +
> + reg = csr_readw(pcie, PCI_CAPABILITY_LIST);
> + next_cap_ptr = (reg & 0x00ff);
> +
> + if (!next_cap_ptr)
> + return 0;
> +
> + return __mobiveil_pcie_ep_find_next_cap(pcie, next_cap_ptr, cap);
> +}
> +
> +static int mobiveil_pcie_ep_write_header(struct pci_epc *epc, u8 func_no,
> + struct pci_epf_header *hdr)
> +{
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> +
> + csr_writew(pcie, hdr->vendorid, PCI_VENDOR_ID);
> + csr_writew(pcie, hdr->deviceid, PCI_DEVICE_ID);
> + csr_writeb(pcie, hdr->revid, PCI_REVISION_ID);
> + csr_writeb(pcie, hdr->progif_code, PCI_CLASS_PROG);
> + csr_writew(pcie, hdr->subclass_code | hdr->baseclass_code << 8,
> + PCI_CLASS_DEVICE);
> + csr_writeb(pcie, hdr->cache_line_size, PCI_CACHE_LINE_SIZE);
> + csr_writew(pcie, hdr->subsys_vendor_id, PCI_SUBSYSTEM_VENDOR_ID);
> + csr_writew(pcie, hdr->subsys_id, PCI_SUBSYSTEM_ID);
> + csr_writeb(pcie, hdr->interrupt_pin, PCI_INTERRUPT_PIN);
> +
> + return 0;
> +}
> +
> +static int mobiveil_pcie_ep_inbound_atu(struct mobiveil_pcie_ep *ep,
> + u8 func_no, enum pci_barno bar,
> + dma_addr_t cpu_addr)
> +{
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> +
> + program_ib_windows_ep(pcie, func_no, bar, cpu_addr);
> +
> + return 0;
> +}
> +
> +static int mobiveil_pcie_ep_outbound_atu(struct mobiveil_pcie_ep *ep,
> + phys_addr_t phys_addr,
> + u64 pci_addr, u8 func_no,
> + size_t size)
> +{
> + int ret;
> + u32 free_win;
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> +
> + free_win = find_first_zero_bit(ep->ob_window_map, ep->num_ob_windows);
> + if (free_win >= ep->num_ob_windows) {
> + dev_err(&pcie->pdev->dev, "No free outbound window\n");
> + return -EINVAL;
> + }
> +
> + ret = program_ob_windows_ep(pcie, free_win, MEM_WINDOW_TYPE,
> + phys_addr, pci_addr, func_no, size);
> + if (ret < 0) {
> + dev_err(&pcie->pdev->dev, "Failed to program IB window\n");
> + return ret;
> + }
> +
> + set_bit(free_win, ep->ob_window_map);
> + ep->outbound_addr[free_win] = phys_addr;
> +
> + return 0;
> +}
> +
> +static void mobiveil_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no,
> + struct pci_epf_bar *epf_bar)
> +{
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + enum pci_barno bar = epf_bar->barno;
> +
> + if (bar < ep->bar_num) {
> + __mobiveil_pcie_ep_reset_bar(pcie,
> + func_no * ep->bar_num + bar);
> +
> + mobiveil_pcie_disable_ib_win_ep(pcie, func_no, bar);
> + }
> +}
> +
> +static int mobiveil_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no,
> + struct pci_epf_bar *epf_bar)
> +{
> + int ret;
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + enum pci_barno bar = epf_bar->barno;
> + size_t size = epf_bar->size;
> +
> + if (bar < ep->bar_num) {
> + ret = mobiveil_pcie_ep_inbound_atu(ep, func_no, bar,
> + epf_bar->phys_addr);
> + if (ret)
> + return ret;
> +
> + csr_writel(pcie, func_no * ep->bar_num + bar,
> + GPEX_BAR_SELECT);
> + csr_writel(pcie, lower_32_bits(~(size - 1)),
> + GPEX_BAR_SIZE_LDW);
> + csr_writel(pcie, upper_32_bits(~(size - 1)),
> + GPEX_BAR_SIZE_UDW);
> + }
> +
> + return 0;
> +}
> +
> +static int mobiveil_pcie_find_index(struct mobiveil_pcie_ep *ep,
> + phys_addr_t addr,
> + u32 *atu_index)
> +{
> + u32 index;
> +
> + for (index = 0; index < ep->num_ob_windows; index++) {
> + if (ep->outbound_addr[index] != addr)
> + continue;
> + *atu_index = index;
> + return 0;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static void mobiveil_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no,
> + phys_addr_t addr)
> +{
> + int ret;
> + u32 atu_index;
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> +
> + ret = mobiveil_pcie_find_index(ep, addr, &atu_index);
> + if (ret < 0)
> + return;
> +
> + mobiveil_pcie_disable_ob_win(pcie, atu_index);
> + clear_bit(atu_index, ep->ob_window_map);
> +}
> +
> +static int mobiveil_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no,
> + phys_addr_t addr,
> + u64 pci_addr, size_t size)
> +{
> + int ret;
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> +
> + ret = mobiveil_pcie_ep_outbound_atu(ep, addr, pci_addr, func_no, size);
> + if (ret) {
> + dev_err(&pcie->pdev->dev, "Failed to enable address\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int mobiveil_pcie_ep_get_msi(struct pci_epc *epc, u8 func_no)
> +{
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + u32 val, reg;
> +
> + if (!ep->msi_cap)
> + return -EINVAL;
> +
> + reg = ep->msi_cap + PCI_MSI_FLAGS;
> + val = csr_readw(pcie, reg);
> + if (!(val & PCI_MSI_FLAGS_ENABLE))
> + return -EINVAL;
> +
> + val = (val & PCI_MSI_FLAGS_QSIZE) >> 4;
> +
> + return val;
> +}
> +
> +static int mobiveil_pcie_ep_set_msi(struct pci_epc *epc,
> + u8 func_no, u8 interrupts)
> +{
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + u32 val, reg;
> +
> + if (!ep->msi_cap)
> + return -EINVAL;
> +
> + reg = ep->msi_cap + PCI_MSI_FLAGS;
> + val = csr_readw(pcie, reg);
> + val &= ~PCI_MSI_FLAGS_QMASK;
> + val |= (interrupts << 1) & PCI_MSI_FLAGS_QMASK;
> + csr_writew(pcie, val, reg);
> +
> + return 0;
> +}
> +
> +static int mobiveil_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no)
> +{
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + u32 val, reg;
> +
> + if (!ep->msix_cap)
> + return -EINVAL;
> +
> + reg = ep->msix_cap + PCI_MSIX_FLAGS;
> + val = csr_readw(pcie, reg);
> + if (!(val & PCI_MSIX_FLAGS_ENABLE))
> + return -EINVAL;
> +
> + val &= PCI_MSIX_FLAGS_QSIZE;
> +
> + return val;
> +}
> +
> +static int mobiveil_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no,
> + u16 interrupts)
> +{
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + u32 val, reg;
> +
> + if (!ep->msix_cap)
> + return -EINVAL;
> +
> + reg = ep->msix_cap + PCI_MSIX_FLAGS;
> + val = csr_readw(pcie, reg);
> + val &= ~PCI_MSIX_FLAGS_QSIZE;
> + val |= interrupts;
> + csr_writew(pcie, val, reg);
> +
> + return 0;
> +}
> +
> +static int mobiveil_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no,
> + enum pci_epc_irq_type type,
> + u16 interrupt_num)
> +{
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> +
> + if (!ep->ops->raise_irq)
> + return -EINVAL;
> +
> + return ep->ops->raise_irq(ep, func_no, type, interrupt_num);
> +}
> +
> +static const struct pci_epc_ops epc_ops = {
> + .write_header = mobiveil_pcie_ep_write_header,
> + .set_bar = mobiveil_pcie_ep_set_bar,
> + .clear_bar = mobiveil_pcie_ep_clear_bar,
> + .map_addr = mobiveil_pcie_ep_map_addr,
> + .unmap_addr = mobiveil_pcie_ep_unmap_addr,
> + .set_msi = mobiveil_pcie_ep_set_msi,
> + .get_msi = mobiveil_pcie_ep_get_msi,
> + .set_msix = mobiveil_pcie_ep_set_msix,
> + .get_msix = mobiveil_pcie_ep_get_msix,
> + .raise_irq = mobiveil_pcie_ep_raise_irq,
> +};
> +
> +int mobiveil_pcie_ep_raise_legacy_irq(struct mobiveil_pcie_ep *ep, u8 func_no)
> +{
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> +
> + dev_err(&pcie->pdev->dev, "EP cannot trigger legacy IRQs\n");
> +
> + return -EINVAL;
> +}
> +
> +int mobiveil_pcie_ep_raise_msi_irq(struct mobiveil_pcie_ep *ep, u8 func_no,
> + u8 interrupt_num)
> +{
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + struct pci_epc *epc = ep->epc;
> + u16 msg_ctrl, msg_data;
> + u32 msg_addr_lower, msg_addr_upper, reg;
> + u64 msg_addr;
> + u32 func_num;
> + bool has_upper;
> + int ret;
> +
> + if (!ep->msi_cap)
> + return -EINVAL;
> +
> + /*
> + * In order ot get the PF's MSI capability register value from config
> + * space we need to set the PF number to the PAB_CTRL register.
> + */
> + func_num = csr_readl(pcie, PAB_CTRL);
> + func_num &= ~(FUNC_SEL_MASK << FUNC_SEL_SHIFT);
> + func_num |= (func_no & FUNC_SEL_MASK) << FUNC_SEL_SHIFT;
> + csr_writel(pcie, func_num, PAB_CTRL);
> +
> + reg = ep->msi_cap + PCI_MSI_FLAGS;
> + msg_ctrl = csr_readw(pcie, reg);
> + has_upper = !!(msg_ctrl & PCI_MSI_FLAGS_64BIT);
> + reg = ep->msi_cap + PCI_MSI_ADDRESS_LO;
> + msg_addr_lower = csr_readl(pcie, reg);
> + if (has_upper) {
> + reg = ep->msi_cap + PCI_MSI_ADDRESS_HI;
> + msg_addr_upper = csr_readl(pcie, reg);
> + reg = ep->msi_cap + PCI_MSI_DATA_64;
> + msg_data = csr_readw(pcie, reg);
> + } else {
> + msg_addr_upper = 0;
> + reg = ep->msi_cap + PCI_MSI_DATA_32;
> + msg_data = csr_readw(pcie, reg);
> + }
> + msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower;
> +
> + /*
> + * clear the FUNC_SEL_SHIFT bits when access other registers except
> + * config space register.
> + */
> + func_num = csr_readl(pcie, PAB_CTRL);
> + func_num &= ~(FUNC_SEL_MASK << FUNC_SEL_SHIFT);
> + csr_writel(pcie, func_num, PAB_CTRL);
> +
> + ret = mobiveil_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys,
> + msg_addr, epc->mem->page_size);
> + if (ret)
> + return ret;
> +
> + writel(msg_data | (interrupt_num - 1), ep->msi_mem);
> +
> + mobiveil_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys);
> +
> + return 0;
> +}
> +
> +int mobiveil_pcie_ep_raise_msix_irq(struct mobiveil_pcie_ep *ep, u8 func_no,
> + u16 interrupt_num)
> +{
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + struct pci_epc *epc = ep->epc;
> + u32 msg_addr_upper, msg_addr_lower;
> + u32 msg_data;
> + u64 msg_addr;
> + u32 func_num;
> + int ret;
> +
> + /*
> + * In order ot get the PF's MSI capability register value from config
> + * space we need to set the PF number to the PAB_CTRL register.
> + */
> + func_num = csr_readl(pcie, PAB_CTRL);
> + func_num &= ~(FUNC_SEL_MASK << FUNC_SEL_SHIFT);
> + func_num |= (func_no & FUNC_SEL_MASK) << FUNC_SEL_SHIFT;
> + csr_writel(pcie, func_num, PAB_CTRL);
> +
> + msg_addr_lower = csr_readl(pcie, PAB_MSIX_TABLE_PBA_ACCESS +
> + PCI_MSIX_ENTRY_LOWER_ADDR +
> + (interrupt_num - 1) * PCI_MSIX_ENTRY_SIZE);
> + msg_addr_upper = csr_readl(pcie, PAB_MSIX_TABLE_PBA_ACCESS +
> + PCI_MSIX_ENTRY_UPPER_ADDR +
> + (interrupt_num - 1) * PCI_MSIX_ENTRY_SIZE);
> + msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower;
> + msg_data = csr_readl(pcie, PAB_MSIX_TABLE_PBA_ACCESS +
> + PCI_MSIX_ENTRY_DATA +
> + (interrupt_num - 1) * PCI_MSIX_ENTRY_SIZE);
> +
> + /*
> + * clear the FUNC_SEL_SHIFT bits when access other registers except
> + * config space registers.
> + */
> + func_num = csr_readl(pcie, PAB_CTRL);
> + func_num &= ~(FUNC_SEL_MASK << FUNC_SEL_SHIFT);
> + csr_writel(pcie, func_num, PAB_CTRL);
> +
> + ret = mobiveil_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys,
> + msg_addr, epc->mem->page_size);
> + if (ret)
> + return ret;
> +
> + writel(msg_data, ep->msi_mem);
> +
> + mobiveil_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys);
> +
> + return 0;
> +}
> +
> +void mobiveil_pcie_ep_exit(struct mobiveil_pcie_ep *ep)
> +{
> + struct pci_epc *epc = ep->epc;
> +
> + pci_epc_mem_free_addr(epc, ep->msi_mem_phys, ep->msi_mem,
> + epc->mem->page_size);
> +
> + pci_epc_mem_exit(epc);
> +}
> +
> +int mobiveil_pcie_ep_init(struct mobiveil_pcie_ep *ep)
> +{
> + int ret;
> + void *addr;
> + struct pci_epc *epc;
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + struct device *dev = &pcie->pdev->dev;
> + struct device_node *np = dev->of_node;
> +
> + if (!pcie->csr_axi_slave_base) {
> + dev_err(dev, "csr_base is not populated\n");
> + return -EINVAL;
> + }
> +
> + ret = of_property_read_u32(np, "num-ob-windows", &ep->num_ob_windows);
> + if (ret < 0) {
> + dev_err(dev, "Unable to read *num-ob-windows* property\n");
> + return ret;
> + }
> +
> + if (ep->num_ob_windows > MAX_IATU_OUT) {
> + dev_err(dev, "Invalid *num-ob-windows*\n");
> + return -EINVAL;
> + }
> + ep->ob_window_map = devm_kcalloc(dev,
> + BITS_TO_LONGS(ep->num_ob_windows),
> + sizeof(long),
> + GFP_KERNEL);
> + if (!ep->ob_window_map)
> + return -ENOMEM;
> +
> + addr = devm_kcalloc(dev, ep->num_ob_windows, sizeof(phys_addr_t),
> + GFP_KERNEL);
> + if (!addr)
> + return -ENOMEM;
> + ep->outbound_addr = addr;
> +
> + mobiveil_pcie_enable_bridge_pio(pcie);
> + mobiveil_pcie_enable_engine_apio(pcie);
> + mobiveil_pcie_enable_engine_ppio(pcie);
> + mobiveil_pcie_enable_msi_ep(pcie);
> +
> + epc = devm_pci_epc_create(dev, &epc_ops);
> + if (IS_ERR(epc)) {
> + dev_err(dev, "Failed to create epc device\n");
> + return PTR_ERR(epc);
> + }
> +
> + ep->epc = epc;
> + epc_set_drvdata(epc, ep);
> +
> + ep->msi_cap = mobiveil_pcie_ep_find_capability(pcie, PCI_CAP_ID_MSI);
> +
> + ep->msix_cap = mobiveil_pcie_ep_find_capability(pcie,
> + PCI_CAP_ID_MSIX);
> +
> + if (ep->ops->ep_init)
> + ep->ops->ep_init(ep);
> +
> + epc->max_functions = ep->pf_num;
> +
> + ret = __pci_epc_mem_init(epc, ep->phys_base, ep->addr_size,
> + ep->page_size);
> + if (ret < 0) {
> + dev_err(dev, "Failed to initialize address space\n");
> + return ret;
> + }
> +
> + ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys,
> + epc->mem->page_size);
> + if (!ep->msi_mem) {
> + dev_err(dev, "Failed to reserve memory for MSI/MSI-X\n");
> + return -ENOMEM;
> + }
> +
> + return 0;
> +}
> diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil.c b/drivers/pci/controller/mobiveil/pcie-mobiveil.c
> index 49d471b..d1fdfed 100644
> --- a/drivers/pci/controller/mobiveil/pcie-mobiveil.c
> +++ b/drivers/pci/controller/mobiveil/pcie-mobiveil.c
> @@ -210,6 +210,57 @@ void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, u64 cpu_addr,
> pcie->ob_wins_configured++;
> }
>
> +int program_ob_windows_ep(struct mobiveil_pcie *pcie, int win_num, int type,
> + u64 phys, u64 bus_addr, u8 func, u64 size)
> +{
> + u32 val;
> + u32 size_h, size_l;
> +
> + if (size & (size - 1))
> + size = 1 << (1 + ilog2(size));
> +
> + size_h = upper_32_bits(~(size - 1));
> + size_l = lower_32_bits(~(size - 1));
> +
> + val = csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num));
> + val &= ~(WIN_TYPE_MASK << WIN_TYPE_SHIFT |
> + WIN_SIZE_MASK << WIN_SIZE_SHIFT);
> + val |= 1 << WIN_ENABLE_SHIFT | type << WIN_TYPE_SHIFT |
> + (size_l & (WIN_SIZE_MASK << WIN_SIZE_SHIFT));
> + csr_writel(pcie, val, PAB_AXI_AMAP_CTRL(win_num));
> +
> + csr_writel(pcie, func, PAB_AXI_AMAP_PCI_HDR_PARAM(win_num));
> + csr_writel(pcie, lower_32_bits(phys), PAB_AXI_AMAP_AXI_WIN(win_num));
> + csr_writel(pcie, upper_32_bits(phys),
> + PAB_EXT_AXI_AMAP_AXI_WIN(win_num));
> + csr_writel(pcie, lower_32_bits(bus_addr),
> + PAB_AXI_AMAP_PEX_WIN_L(win_num));
> + csr_writel(pcie, upper_32_bits(bus_addr),
> + PAB_AXI_AMAP_PEX_WIN_H(win_num));
> + csr_writel(pcie, size_h, PAB_EXT_AXI_AMAP_SIZE(win_num));
> +
> + return 0;
> +}
> +
> +void program_ib_windows_ep(struct mobiveil_pcie *pcie, u8 func_no,
> + int bar, u64 phys)
> +{
> + csr_writel(pcie, upper_32_bits(phys),
> + PAB_EXT_PEX_BAR_AMAP(func_no, bar));
> + csr_writel(pcie, lower_32_bits(phys) | PEX_BAR_AMAP_EN,
> + PAB_PEX_BAR_AMAP(func_no, bar));
> +}
> +
> +void mobiveil_pcie_disable_ib_win_ep(struct mobiveil_pcie *pcie,
> + u8 func_no, u8 bar)
> +{
> + u32 val;
> +
> + val = csr_readl(pcie, PAB_PEX_BAR_AMAP(func_no, bar));
> + val &= ~(1 << 0);
> + csr_writel(pcie, val, PAB_PEX_BAR_AMAP(func_no, bar));
> +}
> +
> int mobiveil_bringup_link(struct mobiveil_pcie *pcie)
> {
> int retries;
> @@ -227,20 +278,57 @@ int mobiveil_bringup_link(struct mobiveil_pcie *pcie)
> return -ETIMEDOUT;
> }
>
> -void mobiveil_pcie_disable_ib_win(struct mobiveil_pcie *pci, int win_num)
> +void mobiveil_pcie_disable_ib_win(struct mobiveil_pcie *pcie, int win_num)
> {
> u32 val;
>
> - val = csr_readl(pci, PAB_PEX_AMAP_CTRL(win_num));
> + val = csr_readl(pcie, PAB_PEX_AMAP_CTRL(win_num));
> val &= ~(1 << AMAP_CTRL_EN_SHIFT);
> - csr_writel(pci, val, PAB_PEX_AMAP_CTRL(win_num));
> + csr_writel(pcie, val, PAB_PEX_AMAP_CTRL(win_num));
> }
>
> -void mobiveil_pcie_disable_ob_win(struct mobiveil_pcie *pci, int win_num)
> +void mobiveil_pcie_disable_ob_win(struct mobiveil_pcie *pcie, int win_num)
> {
> u32 val;
>
> - val = csr_readl(pci, PAB_AXI_AMAP_CTRL(win_num));
> + val = csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num));
> val &= ~(1 << WIN_ENABLE_SHIFT);
> - csr_writel(pci, val, PAB_AXI_AMAP_CTRL(win_num));
> + csr_writel(pcie, val, PAB_AXI_AMAP_CTRL(win_num));
> +}
> +
> +void mobiveil_pcie_enable_bridge_pio(struct mobiveil_pcie *pcie)
> +{
> + u32 val;
> +
> + val = csr_readl(pcie, PAB_CTRL);
> + val |= 1 << AMBA_PIO_ENABLE_SHIFT;
> + val |= 1 << PEX_PIO_ENABLE_SHIFT;
> + csr_writel(pcie, val, PAB_CTRL);
> +}
> +
> +void mobiveil_pcie_enable_engine_apio(struct mobiveil_pcie *pcie)
> +{
> + u32 val;
> +
> + val = csr_readl(pcie, PAB_AXI_PIO_CTRL);
> + val |= APIO_EN_MASK;
> + csr_writel(pcie, val, PAB_AXI_PIO_CTRL);
> +}
> +
> +void mobiveil_pcie_enable_engine_ppio(struct mobiveil_pcie *pcie)
> +{
> + u32 val;
> +
> + val = csr_readl(pcie, PAB_PEX_PIO_CTRL);
> + val |= 1 << PIO_ENABLE_SHIFT;
> + csr_writel(pcie, val, PAB_PEX_PIO_CTRL);
> +}
> +
> +void mobiveil_pcie_enable_msi_ep(struct mobiveil_pcie *pcie)
> +{
> + u32 val;
> +
> + val = csr_readl(pcie, PAB_INTP_AMBA_MISC_ENB);
> + val |= 1 << 0;
> + csr_writel(pcie, val, PAB_INTP_AMBA_MISC_ENB);
> }
> diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil.h b/drivers/pci/controller/mobiveil/pcie-mobiveil.h
> index f0e2e4a..275c68f 100644
> --- a/drivers/pci/controller/mobiveil/pcie-mobiveil.h
> +++ b/drivers/pci/controller/mobiveil/pcie-mobiveil.h
> @@ -15,6 +15,10 @@
> #include <linux/msi.h>
> #include "../../pci.h"
>
> +#include <linux/pci-epc.h>
> +#include <linux/pci-epf.h>
> +
> +#define MAX_IATU_OUT 256
> /* register offsets and bit positions */
>
> /*
> @@ -40,6 +44,9 @@
> #define PAGE_SEL_MASK 0x3f
> #define PAGE_LO_MASK 0x3ff
> #define PAGE_SEL_OFFSET_SHIFT 10
> +#define FUNC_SEL_SHIFT 19
> +#define FUNC_SEL_MASK 0x1ff
> +#define MSI_SW_CTRL_EN (1 << 29)
>
> #define PAB_ACTIVITY_STAT 0x81c
>
> @@ -100,6 +107,19 @@
> #define PAB_PEX_AMAP_PEX_WIN_L(win) PAB_REG_ADDR(0x4ba8, win)
> #define PAB_PEX_AMAP_PEX_WIN_H(win) PAB_REG_ADDR(0x4bac, win)
>
> +/* PPIO WINs EP mode */
> +#define PAB_PEX_BAR_AMAP(func, bar) (0x1ba0 + 0x20 * func + 4 * bar)
> +#define PAB_EXT_PEX_BAR_AMAP(func, bar) (0x84a0 + 0x20 * func + 4 * bar)
> +#define PEX_BAR_AMAP_EN (1 << 0)
> +
> +#define PAB_AXI_AMAP_PCI_HDR_PARAM(idx) (0x5ba0 + 0x04 * idx)
> +#define PAB_MSIX_TABLE_PBA_ACCESS 0xD000
> +
> +#define GPEX_BAR_ENABLE 0x4D4
> +#define GPEX_BAR_SIZE_LDW 0x4D8
> +#define GPEX_BAR_SIZE_UDW 0x4DC
> +#define GPEX_BAR_SELECT 0x4E0
> +
> /* starting offset of INTX bits in status register */
> #define PAB_INTX_START 5
>
> @@ -137,6 +157,7 @@
> ((off >> PAGE_SEL_OFFSET_SHIFT) & PAGE_SEL_MASK)
>
> struct mobiveil_pcie;
> +struct mobiveil_pcie_ep;
>
> struct mobiveil_msi { /* MSI information */
> struct mutex lock; /* protect bitmap variable */
> @@ -169,6 +190,29 @@ struct mobiveil_pab_ops {
> int (*host_init)(struct mobiveil_pcie *pcie);
> };
>
> +struct mobiveil_pcie_ep_ops {
> + void (*ep_init)(struct mobiveil_pcie_ep *ep);
> + int (*raise_irq)(struct mobiveil_pcie_ep *ep, u8 func_no,
> + enum pci_epc_irq_type type, u16 interrupt_num);
> +};
> +
> +struct mobiveil_pcie_ep {
> + struct pci_epc *epc;
> + struct mobiveil_pcie_ep_ops *ops;
> + phys_addr_t phys_base;
> + size_t addr_size;
> + size_t page_size;
> + phys_addr_t *outbound_addr;
> + unsigned long *ob_window_map;
> + u32 num_ob_windows;
> + void __iomem *msi_mem;
> + phys_addr_t msi_mem_phys;
> + u8 msi_cap; /* MSI capability offset */
> + u8 msix_cap; /* MSI-X capability offset */
> + u8 bar_num;
> + u32 pf_num;
> +};
> +
> struct mobiveil_pcie {
> struct platform_device *pdev;
> struct list_head *resources;
> @@ -181,7 +225,10 @@ struct mobiveil_pcie {
> u32 ib_wins_configured; /* configured inbound windows */
> const struct mobiveil_pab_ops *ops;
> struct root_port rp;
> + struct mobiveil_pcie_ep ep;
> };
> +#define to_mobiveil_pcie_from_ep(endpoint) \
> + container_of((endpoint), struct mobiveil_pcie, ep)
>
> int mobiveil_pcie_host_probe(struct mobiveil_pcie *pcie);
> int mobiveil_host_init(struct mobiveil_pcie *pcie, bool reinit);
> @@ -226,4 +273,21 @@ static inline void csr_writeb(struct mobiveil_pcie *pcie, u32 val, u32 off)
> csr_write(pcie, val, off, 0x1);
> }
>
> +void program_ib_windows_ep(struct mobiveil_pcie *pcie, u8 func_no,
> + int bar, u64 phys);
> +int program_ob_windows_ep(struct mobiveil_pcie *pcie, int win_num, int type,
> + u64 phys, u64 bus_addr, u8 func, u64 size);
> +void mobiveil_pcie_disable_ib_win_ep(struct mobiveil_pcie *pci,
> + u8 func_no, u8 bar);
> +int mobiveil_pcie_ep_init(struct mobiveil_pcie_ep *ep);
> +int mobiveil_pcie_ep_raise_legacy_irq(struct mobiveil_pcie_ep *ep, u8 func_no);
> +int mobiveil_pcie_ep_raise_msi_irq(struct mobiveil_pcie_ep *ep, u8 func_no,
> + u8 interrupt_num);
> +int mobiveil_pcie_ep_raise_msix_irq(struct mobiveil_pcie_ep *ep, u8 func_no,
> + u16 interrupt_num);
> +void mobiveil_pcie_ep_reset_bar(struct mobiveil_pcie *pci, enum pci_barno bar);
> +void mobiveil_pcie_enable_bridge_pio(struct mobiveil_pcie *pci);
> +void mobiveil_pcie_enable_engine_apio(struct mobiveil_pcie *pci);
> +void mobiveil_pcie_enable_engine_ppio(struct mobiveil_pcie *pci);
> +void mobiveil_pcie_enable_msi_ep(struct mobiveil_pcie *pci);
> #endif /* _PCIE_MOBIVEIL_H */
> --
> 1.7.1
>

Please review and fix macro alignments, otherwise looks ok.
Reviewed-by: Subrahmanya Lingappa <[email protected]>

2019-03-08 06:56:04

by Bao Xiaowei

[permalink] [raw]
Subject: RE: [PATCH 1/6] PCI: mobiveil: Add the EP mode support



-----Original Message-----
From: Subrahmanya Lingappa <[email protected]>
Sent: 2019年3月8日 14:45
To: Xiaowei Bao <[email protected]>
Cc: Bjorn Helgaas <[email protected]>; Z.q. Hou <[email protected]>; [email protected]; [email protected]; [email protected]; Leo Li <[email protected]>; [email protected]; Lorenzo Pieralisi <[email protected]>; [email protected]; [email protected]; M.h. Lian <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]
Subject: Re: [PATCH 1/6] PCI: mobiveil: Add the EP mode support

Xiaowei,


On Mon, Feb 18, 2019 at 3:22 PM Xiaowei Bao <[email protected]> wrote:
>
> Add the EP mode support for Mobiveil base on endpoint framework.
>
> Signed-off-by: Xiaowei Bao <[email protected]>
> ---
> depends on:
> https://emea01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fpatc
> hwork.ozlabs.org%2Fproject%2Flinux-pci%2Flist%2F%3Fseries%3D88754&amp;
> data=02%7C01%7Cxiaowei.bao%40nxp.com%7C81bba6e6911b4c04fb0808d6a3910ce
> e%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0%7C0%7C636876240720287700&amp;
> sdata=i2gZUXQxRPr8R82KOuBDZZWiaKJBtXYwB9JV7SwHTXY%3D&amp;reserved=0
>
> drivers/pci/controller/mobiveil/Kconfig | 5 +
> drivers/pci/controller/mobiveil/Makefile | 1 +
> drivers/pci/controller/mobiveil/pcie-mobiveil-ep.c | 527 ++++++++++++++++++++
> drivers/pci/controller/mobiveil/pcie-mobiveil.c | 100 ++++-
> drivers/pci/controller/mobiveil/pcie-mobiveil.h | 64 +++
> 5 files changed, 691 insertions(+), 6 deletions(-) create mode
> 100644 drivers/pci/controller/mobiveil/pcie-mobiveil-ep.c
>
> diff --git a/drivers/pci/controller/mobiveil/Kconfig
> b/drivers/pci/controller/mobiveil/Kconfig
> index 3ddb7d6..c037db6 100644
> --- a/drivers/pci/controller/mobiveil/Kconfig
> +++ b/drivers/pci/controller/mobiveil/Kconfig
> @@ -11,6 +11,11 @@ config PCIE_MOBIVEIL_HOST
> depends on PCI_MSI_IRQ_DOMAIN
> select PCIE_MOBIVEIL
>
> +config PCIE_MOBIVEIL_EP
> + bool
> + depends on PCI_ENDPOINT
> + select PCIE_MOBIVEIL
> +
> config PCIE_MOBIVEIL_PLAT
> bool "Mobiveil AXI PCIe controller"
> depends on ARCH_ZYNQMP || COMPILE_TEST diff --git
> a/drivers/pci/controller/mobiveil/Makefile
> b/drivers/pci/controller/mobiveil/Makefile
> index ff66774..4f520b7 100644
> --- a/drivers/pci/controller/mobiveil/Makefile
> +++ b/drivers/pci/controller/mobiveil/Makefile
> @@ -1,5 +1,6 @@
> # SPDX-License-Identifier: GPL-2.0
> obj-$(CONFIG_PCIE_MOBIVEIL) += pcie-mobiveil.o
> obj-$(CONFIG_PCIE_MOBIVEIL_HOST) += pcie-mobiveil-host.o
> +obj-$(CONFIG_PCIE_MOBIVEIL_EP) += pcie-mobiveil-ep.o
> obj-$(CONFIG_PCIE_MOBIVEIL_PLAT) += pcie-mobiveil-plat.o
> obj-$(CONFIG_PCI_LAYERSCAPE_GEN4) += pci-layerscape-gen4.o diff --git
> a/drivers/pci/controller/mobiveil/pcie-mobiveil-ep.c
> b/drivers/pci/controller/mobiveil/pcie-mobiveil-ep.c
> new file mode 100644
> index 0000000..16ca7fb
> --- /dev/null
> +++ b/drivers/pci/controller/mobiveil/pcie-mobiveil-ep.c
> @@ -0,0 +1,527 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/**
> + * Mobiveil PCIe Endpoint controller driver
> + *
> + * Copyright (C) 2018 NXP Semiconductor.
> + * Author: Xiaowei Bao <[email protected]> */
> +
> +#include <linux/of.h>
> +#include <linux/pci-epc.h>
> +#include <linux/pci-epf.h>
> +#include <linux/platform_device.h>
> +#include "pcie-mobiveil.h"
> +
> +void mobiveil_pcie_ep_linkup(struct mobiveil_pcie_ep *ep) {
> + struct pci_epc *epc = ep->epc;
> +
> + pci_epc_linkup(epc);
> +}
> +
> +static void __mobiveil_pcie_ep_reset_bar(struct mobiveil_pcie *pcie,
> + enum pci_barno bar) {
> + csr_writel(pcie, bar, GPEX_BAR_SELECT);
> + csr_writel(pcie, 0, GPEX_BAR_SIZE_LDW);
> + csr_writel(pcie, 0, GPEX_BAR_SIZE_UDW); }
> +
> +void mobiveil_pcie_ep_reset_bar(struct mobiveil_pcie *pcie,
> + enum pci_barno bar) {
> + __mobiveil_pcie_ep_reset_bar(pcie, bar); }
> +
> +static u8 __mobiveil_pcie_ep_find_next_cap(struct mobiveil_pcie *pcie,
> + u8 cap_ptr, u8 cap) {
> + u8 cap_id, next_cap_ptr;
> + u16 reg;
> +
> + reg = csr_readw(pcie, cap_ptr);
> + next_cap_ptr = (reg & 0xff00) >> 8;
> + cap_id = (reg & 0x00ff);
> +
> + if (cap_id == cap)
> + return cap_ptr;
> +
> + if (!next_cap_ptr || cap_id > PCI_CAP_ID_MAX)
> + return 0;
> +
> + return __mobiveil_pcie_ep_find_next_cap(pcie, next_cap_ptr,
> +cap); }
> +
> +static u8 mobiveil_pcie_ep_find_capability(struct mobiveil_pcie *pcie,
> + u8 cap) {
> + u8 next_cap_ptr;
> + u16 reg;
> +
> + reg = csr_readw(pcie, PCI_CAPABILITY_LIST);
> + next_cap_ptr = (reg & 0x00ff);
> +
> + if (!next_cap_ptr)
> + return 0;
> +
> + return __mobiveil_pcie_ep_find_next_cap(pcie, next_cap_ptr,
> +cap); }
> +
> +static int mobiveil_pcie_ep_write_header(struct pci_epc *epc, u8 func_no,
> + struct pci_epf_header *hdr) {
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> +
> + csr_writew(pcie, hdr->vendorid, PCI_VENDOR_ID);
> + csr_writew(pcie, hdr->deviceid, PCI_DEVICE_ID);
> + csr_writeb(pcie, hdr->revid, PCI_REVISION_ID);
> + csr_writeb(pcie, hdr->progif_code, PCI_CLASS_PROG);
> + csr_writew(pcie, hdr->subclass_code | hdr->baseclass_code << 8,
> + PCI_CLASS_DEVICE);
> + csr_writeb(pcie, hdr->cache_line_size, PCI_CACHE_LINE_SIZE);
> + csr_writew(pcie, hdr->subsys_vendor_id, PCI_SUBSYSTEM_VENDOR_ID);
> + csr_writew(pcie, hdr->subsys_id, PCI_SUBSYSTEM_ID);
> + csr_writeb(pcie, hdr->interrupt_pin, PCI_INTERRUPT_PIN);
> +
> + return 0;
> +}
> +
> +static int mobiveil_pcie_ep_inbound_atu(struct mobiveil_pcie_ep *ep,
> + u8 func_no, enum pci_barno bar,
> + dma_addr_t cpu_addr) {
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> +
> + program_ib_windows_ep(pcie, func_no, bar, cpu_addr);
> +
> + return 0;
> +}
> +
> +static int mobiveil_pcie_ep_outbound_atu(struct mobiveil_pcie_ep *ep,
> + phys_addr_t phys_addr,
> + u64 pci_addr, u8 func_no,
> + size_t size) {
> + int ret;
> + u32 free_win;
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> +
> + free_win = find_first_zero_bit(ep->ob_window_map, ep->num_ob_windows);
> + if (free_win >= ep->num_ob_windows) {
> + dev_err(&pcie->pdev->dev, "No free outbound window\n");
> + return -EINVAL;
> + }
> +
> + ret = program_ob_windows_ep(pcie, free_win, MEM_WINDOW_TYPE,
> + phys_addr, pci_addr, func_no, size);
> + if (ret < 0) {
> + dev_err(&pcie->pdev->dev, "Failed to program IB window\n");
> + return ret;
> + }
> +
> + set_bit(free_win, ep->ob_window_map);
> + ep->outbound_addr[free_win] = phys_addr;
> +
> + return 0;
> +}
> +
> +static void mobiveil_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no,
> + struct pci_epf_bar *epf_bar) {
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + enum pci_barno bar = epf_bar->barno;
> +
> + if (bar < ep->bar_num) {
> + __mobiveil_pcie_ep_reset_bar(pcie,
> + func_no * ep->bar_num +
> + bar);
> +
> + mobiveil_pcie_disable_ib_win_ep(pcie, func_no, bar);
> + }
> +}
> +
> +static int mobiveil_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no,
> + struct pci_epf_bar *epf_bar) {
> + int ret;
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + enum pci_barno bar = epf_bar->barno;
> + size_t size = epf_bar->size;
> +
> + if (bar < ep->bar_num) {
> + ret = mobiveil_pcie_ep_inbound_atu(ep, func_no, bar,
> + epf_bar->phys_addr);
> + if (ret)
> + return ret;
> +
> + csr_writel(pcie, func_no * ep->bar_num + bar,
> + GPEX_BAR_SELECT);
> + csr_writel(pcie, lower_32_bits(~(size - 1)),
> + GPEX_BAR_SIZE_LDW);
> + csr_writel(pcie, upper_32_bits(~(size - 1)),
> + GPEX_BAR_SIZE_UDW);
> + }
> +
> + return 0;
> +}
> +
> +static int mobiveil_pcie_find_index(struct mobiveil_pcie_ep *ep,
> + phys_addr_t addr,
> + u32 *atu_index) {
> + u32 index;
> +
> + for (index = 0; index < ep->num_ob_windows; index++) {
> + if (ep->outbound_addr[index] != addr)
> + continue;
> + *atu_index = index;
> + return 0;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static void mobiveil_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no,
> + phys_addr_t addr) {
> + int ret;
> + u32 atu_index;
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> +
> + ret = mobiveil_pcie_find_index(ep, addr, &atu_index);
> + if (ret < 0)
> + return;
> +
> + mobiveil_pcie_disable_ob_win(pcie, atu_index);
> + clear_bit(atu_index, ep->ob_window_map); }
> +
> +static int mobiveil_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no,
> + phys_addr_t addr,
> + u64 pci_addr, size_t size) {
> + int ret;
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> +
> + ret = mobiveil_pcie_ep_outbound_atu(ep, addr, pci_addr, func_no, size);
> + if (ret) {
> + dev_err(&pcie->pdev->dev, "Failed to enable address\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int mobiveil_pcie_ep_get_msi(struct pci_epc *epc, u8 func_no)
> +{
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + u32 val, reg;
> +
> + if (!ep->msi_cap)
> + return -EINVAL;
> +
> + reg = ep->msi_cap + PCI_MSI_FLAGS;
> + val = csr_readw(pcie, reg);
> + if (!(val & PCI_MSI_FLAGS_ENABLE))
> + return -EINVAL;
> +
> + val = (val & PCI_MSI_FLAGS_QSIZE) >> 4;
> +
> + return val;
> +}
> +
> +static int mobiveil_pcie_ep_set_msi(struct pci_epc *epc,
> + u8 func_no, u8 interrupts) {
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + u32 val, reg;
> +
> + if (!ep->msi_cap)
> + return -EINVAL;
> +
> + reg = ep->msi_cap + PCI_MSI_FLAGS;
> + val = csr_readw(pcie, reg);
> + val &= ~PCI_MSI_FLAGS_QMASK;
> + val |= (interrupts << 1) & PCI_MSI_FLAGS_QMASK;
> + csr_writew(pcie, val, reg);
> +
> + return 0;
> +}
> +
> +static int mobiveil_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no)
> +{
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + u32 val, reg;
> +
> + if (!ep->msix_cap)
> + return -EINVAL;
> +
> + reg = ep->msix_cap + PCI_MSIX_FLAGS;
> + val = csr_readw(pcie, reg);
> + if (!(val & PCI_MSIX_FLAGS_ENABLE))
> + return -EINVAL;
> +
> + val &= PCI_MSIX_FLAGS_QSIZE;
> +
> + return val;
> +}
> +
> +static int mobiveil_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no,
> + u16 interrupts) {
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + u32 val, reg;
> +
> + if (!ep->msix_cap)
> + return -EINVAL;
> +
> + reg = ep->msix_cap + PCI_MSIX_FLAGS;
> + val = csr_readw(pcie, reg);
> + val &= ~PCI_MSIX_FLAGS_QSIZE;
> + val |= interrupts;
> + csr_writew(pcie, val, reg);
> +
> + return 0;
> +}
> +
> +static int mobiveil_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no,
> + enum pci_epc_irq_type type,
> + u16 interrupt_num) {
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> +
> + if (!ep->ops->raise_irq)
> + return -EINVAL;
> +
> + return ep->ops->raise_irq(ep, func_no, type, interrupt_num); }
> +
> +static const struct pci_epc_ops epc_ops = {
> + .write_header = mobiveil_pcie_ep_write_header,
> + .set_bar = mobiveil_pcie_ep_set_bar,
> + .clear_bar = mobiveil_pcie_ep_clear_bar,
> + .map_addr = mobiveil_pcie_ep_map_addr,
> + .unmap_addr = mobiveil_pcie_ep_unmap_addr,
> + .set_msi = mobiveil_pcie_ep_set_msi,
> + .get_msi = mobiveil_pcie_ep_get_msi,
> + .set_msix = mobiveil_pcie_ep_set_msix,
> + .get_msix = mobiveil_pcie_ep_get_msix,
> + .raise_irq = mobiveil_pcie_ep_raise_irq,
> +};
> +
> +int mobiveil_pcie_ep_raise_legacy_irq(struct mobiveil_pcie_ep *ep, u8
> +func_no) {
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> +
> + dev_err(&pcie->pdev->dev, "EP cannot trigger legacy IRQs\n");
> +
> + return -EINVAL;
> +}
> +
> +int mobiveil_pcie_ep_raise_msi_irq(struct mobiveil_pcie_ep *ep, u8 func_no,
> + u8 interrupt_num) {
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + struct pci_epc *epc = ep->epc;
> + u16 msg_ctrl, msg_data;
> + u32 msg_addr_lower, msg_addr_upper, reg;
> + u64 msg_addr;
> + u32 func_num;
> + bool has_upper;
> + int ret;
> +
> + if (!ep->msi_cap)
> + return -EINVAL;
> +
> + /*
> + * In order ot get the PF's MSI capability register value from config
> + * space we need to set the PF number to the PAB_CTRL register.
> + */
> + func_num = csr_readl(pcie, PAB_CTRL);
> + func_num &= ~(FUNC_SEL_MASK << FUNC_SEL_SHIFT);
> + func_num |= (func_no & FUNC_SEL_MASK) << FUNC_SEL_SHIFT;
> + csr_writel(pcie, func_num, PAB_CTRL);
> +
> + reg = ep->msi_cap + PCI_MSI_FLAGS;
> + msg_ctrl = csr_readw(pcie, reg);
> + has_upper = !!(msg_ctrl & PCI_MSI_FLAGS_64BIT);
> + reg = ep->msi_cap + PCI_MSI_ADDRESS_LO;
> + msg_addr_lower = csr_readl(pcie, reg);
> + if (has_upper) {
> + reg = ep->msi_cap + PCI_MSI_ADDRESS_HI;
> + msg_addr_upper = csr_readl(pcie, reg);
> + reg = ep->msi_cap + PCI_MSI_DATA_64;
> + msg_data = csr_readw(pcie, reg);
> + } else {
> + msg_addr_upper = 0;
> + reg = ep->msi_cap + PCI_MSI_DATA_32;
> + msg_data = csr_readw(pcie, reg);
> + }
> + msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower;
> +
> + /*
> + * clear the FUNC_SEL_SHIFT bits when access other registers except
> + * config space register.
> + */
> + func_num = csr_readl(pcie, PAB_CTRL);
> + func_num &= ~(FUNC_SEL_MASK << FUNC_SEL_SHIFT);
> + csr_writel(pcie, func_num, PAB_CTRL);
> +
> + ret = mobiveil_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys,
> + msg_addr, epc->mem->page_size);
> + if (ret)
> + return ret;
> +
> + writel(msg_data | (interrupt_num - 1), ep->msi_mem);
> +
> + mobiveil_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys);
> +
> + return 0;
> +}
> +
> +int mobiveil_pcie_ep_raise_msix_irq(struct mobiveil_pcie_ep *ep, u8 func_no,
> + u16 interrupt_num) {
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + struct pci_epc *epc = ep->epc;
> + u32 msg_addr_upper, msg_addr_lower;
> + u32 msg_data;
> + u64 msg_addr;
> + u32 func_num;
> + int ret;
> +
> + /*
> + * In order ot get the PF's MSI capability register value from config
> + * space we need to set the PF number to the PAB_CTRL register.
> + */
> + func_num = csr_readl(pcie, PAB_CTRL);
> + func_num &= ~(FUNC_SEL_MASK << FUNC_SEL_SHIFT);
> + func_num |= (func_no & FUNC_SEL_MASK) << FUNC_SEL_SHIFT;
> + csr_writel(pcie, func_num, PAB_CTRL);
> +
> + msg_addr_lower = csr_readl(pcie, PAB_MSIX_TABLE_PBA_ACCESS +
> + PCI_MSIX_ENTRY_LOWER_ADDR +
> + (interrupt_num - 1) * PCI_MSIX_ENTRY_SIZE);
> + msg_addr_upper = csr_readl(pcie, PAB_MSIX_TABLE_PBA_ACCESS +
> + PCI_MSIX_ENTRY_UPPER_ADDR +
> + (interrupt_num - 1) * PCI_MSIX_ENTRY_SIZE);
> + msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower;
> + msg_data = csr_readl(pcie, PAB_MSIX_TABLE_PBA_ACCESS +
> + PCI_MSIX_ENTRY_DATA +
> + (interrupt_num - 1) *
> + PCI_MSIX_ENTRY_SIZE);
> +
> + /*
> + * clear the FUNC_SEL_SHIFT bits when access other registers except
> + * config space registers.
> + */
> + func_num = csr_readl(pcie, PAB_CTRL);
> + func_num &= ~(FUNC_SEL_MASK << FUNC_SEL_SHIFT);
> + csr_writel(pcie, func_num, PAB_CTRL);
> +
> + ret = mobiveil_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys,
> + msg_addr, epc->mem->page_size);
> + if (ret)
> + return ret;
> +
> + writel(msg_data, ep->msi_mem);
> +
> + mobiveil_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys);
> +
> + return 0;
> +}
> +
> +void mobiveil_pcie_ep_exit(struct mobiveil_pcie_ep *ep) {
> + struct pci_epc *epc = ep->epc;
> +
> + pci_epc_mem_free_addr(epc, ep->msi_mem_phys, ep->msi_mem,
> + epc->mem->page_size);
> +
> + pci_epc_mem_exit(epc);
> +}
> +
> +int mobiveil_pcie_ep_init(struct mobiveil_pcie_ep *ep) {
> + int ret;
> + void *addr;
> + struct pci_epc *epc;
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + struct device *dev = &pcie->pdev->dev;
> + struct device_node *np = dev->of_node;
> +
> + if (!pcie->csr_axi_slave_base) {
> + dev_err(dev, "csr_base is not populated\n");
> + return -EINVAL;
> + }
> +
> + ret = of_property_read_u32(np, "num-ob-windows", &ep->num_ob_windows);
> + if (ret < 0) {
> + dev_err(dev, "Unable to read *num-ob-windows* property\n");
> + return ret;
> + }
> +
> + if (ep->num_ob_windows > MAX_IATU_OUT) {
> + dev_err(dev, "Invalid *num-ob-windows*\n");
> + return -EINVAL;
> + }
> + ep->ob_window_map = devm_kcalloc(dev,
> + BITS_TO_LONGS(ep->num_ob_windows),
> + sizeof(long),
> + GFP_KERNEL);
> + if (!ep->ob_window_map)
> + return -ENOMEM;
> +
> + addr = devm_kcalloc(dev, ep->num_ob_windows, sizeof(phys_addr_t),
> + GFP_KERNEL);
> + if (!addr)
> + return -ENOMEM;
> + ep->outbound_addr = addr;
> +
> + mobiveil_pcie_enable_bridge_pio(pcie);
> + mobiveil_pcie_enable_engine_apio(pcie);
> + mobiveil_pcie_enable_engine_ppio(pcie);
> + mobiveil_pcie_enable_msi_ep(pcie);
> +
> + epc = devm_pci_epc_create(dev, &epc_ops);
> + if (IS_ERR(epc)) {
> + dev_err(dev, "Failed to create epc device\n");
> + return PTR_ERR(epc);
> + }
> +
> + ep->epc = epc;
> + epc_set_drvdata(epc, ep);
> +
> + ep->msi_cap = mobiveil_pcie_ep_find_capability(pcie,
> + PCI_CAP_ID_MSI);
> +
> + ep->msix_cap = mobiveil_pcie_ep_find_capability(pcie,
> +
> + PCI_CAP_ID_MSIX);
> +
> + if (ep->ops->ep_init)
> + ep->ops->ep_init(ep);
> +
> + epc->max_functions = ep->pf_num;
> +
> + ret = __pci_epc_mem_init(epc, ep->phys_base, ep->addr_size,
> + ep->page_size);
> + if (ret < 0) {
> + dev_err(dev, "Failed to initialize address space\n");
> + return ret;
> + }
> +
> + ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys,
> + epc->mem->page_size);
> + if (!ep->msi_mem) {
> + dev_err(dev, "Failed to reserve memory for MSI/MSI-X\n");
> + return -ENOMEM;
> + }
> +
> + return 0;
> +}
> diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil.c
> b/drivers/pci/controller/mobiveil/pcie-mobiveil.c
> index 49d471b..d1fdfed 100644
> --- a/drivers/pci/controller/mobiveil/pcie-mobiveil.c
> +++ b/drivers/pci/controller/mobiveil/pcie-mobiveil.c
> @@ -210,6 +210,57 @@ void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, u64 cpu_addr,
> pcie->ob_wins_configured++;
> }
>
> +int program_ob_windows_ep(struct mobiveil_pcie *pcie, int win_num, int type,
> + u64 phys, u64 bus_addr, u8 func, u64 size) {
> + u32 val;
> + u32 size_h, size_l;
> +
> + if (size & (size - 1))
> + size = 1 << (1 + ilog2(size));
> +
> + size_h = upper_32_bits(~(size - 1));
> + size_l = lower_32_bits(~(size - 1));
> +
> + val = csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num));
> + val &= ~(WIN_TYPE_MASK << WIN_TYPE_SHIFT |
> + WIN_SIZE_MASK << WIN_SIZE_SHIFT);
> + val |= 1 << WIN_ENABLE_SHIFT | type << WIN_TYPE_SHIFT |
> + (size_l & (WIN_SIZE_MASK << WIN_SIZE_SHIFT));
> + csr_writel(pcie, val, PAB_AXI_AMAP_CTRL(win_num));
> +
> + csr_writel(pcie, func, PAB_AXI_AMAP_PCI_HDR_PARAM(win_num));
> + csr_writel(pcie, lower_32_bits(phys), PAB_AXI_AMAP_AXI_WIN(win_num));
> + csr_writel(pcie, upper_32_bits(phys),
> + PAB_EXT_AXI_AMAP_AXI_WIN(win_num));
> + csr_writel(pcie, lower_32_bits(bus_addr),
> + PAB_AXI_AMAP_PEX_WIN_L(win_num));
> + csr_writel(pcie, upper_32_bits(bus_addr),
> + PAB_AXI_AMAP_PEX_WIN_H(win_num));
> + csr_writel(pcie, size_h, PAB_EXT_AXI_AMAP_SIZE(win_num));
> +
> + return 0;
> +}
> +
> +void program_ib_windows_ep(struct mobiveil_pcie *pcie, u8 func_no,
> + int bar, u64 phys) {
> + csr_writel(pcie, upper_32_bits(phys),
> + PAB_EXT_PEX_BAR_AMAP(func_no, bar));
> + csr_writel(pcie, lower_32_bits(phys) | PEX_BAR_AMAP_EN,
> + PAB_PEX_BAR_AMAP(func_no, bar)); }
> +
> +void mobiveil_pcie_disable_ib_win_ep(struct mobiveil_pcie *pcie,
> + u8 func_no, u8 bar) {
> + u32 val;
> +
> + val = csr_readl(pcie, PAB_PEX_BAR_AMAP(func_no, bar));
> + val &= ~(1 << 0);
> + csr_writel(pcie, val, PAB_PEX_BAR_AMAP(func_no, bar)); }
> +
> int mobiveil_bringup_link(struct mobiveil_pcie *pcie) {
> int retries;
> @@ -227,20 +278,57 @@ int mobiveil_bringup_link(struct mobiveil_pcie *pcie)
> return -ETIMEDOUT;
> }
>
> -void mobiveil_pcie_disable_ib_win(struct mobiveil_pcie *pci, int
> win_num)
> +void mobiveil_pcie_disable_ib_win(struct mobiveil_pcie *pcie, int
> +win_num)
> {
> u32 val;
>
> - val = csr_readl(pci, PAB_PEX_AMAP_CTRL(win_num));
> + val = csr_readl(pcie, PAB_PEX_AMAP_CTRL(win_num));
> val &= ~(1 << AMAP_CTRL_EN_SHIFT);
> - csr_writel(pci, val, PAB_PEX_AMAP_CTRL(win_num));
> + csr_writel(pcie, val, PAB_PEX_AMAP_CTRL(win_num));
> }
>
> -void mobiveil_pcie_disable_ob_win(struct mobiveil_pcie *pci, int
> win_num)
> +void mobiveil_pcie_disable_ob_win(struct mobiveil_pcie *pcie, int
> +win_num)
> {
> u32 val;
>
> - val = csr_readl(pci, PAB_AXI_AMAP_CTRL(win_num));
> + val = csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num));
> val &= ~(1 << WIN_ENABLE_SHIFT);
> - csr_writel(pci, val, PAB_AXI_AMAP_CTRL(win_num));
> + csr_writel(pcie, val, PAB_AXI_AMAP_CTRL(win_num)); }
> +
> +void mobiveil_pcie_enable_bridge_pio(struct mobiveil_pcie *pcie) {
> + u32 val;
> +
> + val = csr_readl(pcie, PAB_CTRL);
> + val |= 1 << AMBA_PIO_ENABLE_SHIFT;
> + val |= 1 << PEX_PIO_ENABLE_SHIFT;
> + csr_writel(pcie, val, PAB_CTRL); }
> +
> +void mobiveil_pcie_enable_engine_apio(struct mobiveil_pcie *pcie) {
> + u32 val;
> +
> + val = csr_readl(pcie, PAB_AXI_PIO_CTRL);
> + val |= APIO_EN_MASK;
> + csr_writel(pcie, val, PAB_AXI_PIO_CTRL); }
> +
> +void mobiveil_pcie_enable_engine_ppio(struct mobiveil_pcie *pcie) {
> + u32 val;
> +
> + val = csr_readl(pcie, PAB_PEX_PIO_CTRL);
> + val |= 1 << PIO_ENABLE_SHIFT;
> + csr_writel(pcie, val, PAB_PEX_PIO_CTRL); }
> +
> +void mobiveil_pcie_enable_msi_ep(struct mobiveil_pcie *pcie) {
> + u32 val;
> +
> + val = csr_readl(pcie, PAB_INTP_AMBA_MISC_ENB);
> + val |= 1 << 0;
> + csr_writel(pcie, val, PAB_INTP_AMBA_MISC_ENB);
> }
> diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil.h
> b/drivers/pci/controller/mobiveil/pcie-mobiveil.h
> index f0e2e4a..275c68f 100644
> --- a/drivers/pci/controller/mobiveil/pcie-mobiveil.h
> +++ b/drivers/pci/controller/mobiveil/pcie-mobiveil.h
> @@ -15,6 +15,10 @@
> #include <linux/msi.h>
> #include "../../pci.h"
>
> +#include <linux/pci-epc.h>
> +#include <linux/pci-epf.h>
> +
> +#define MAX_IATU_OUT 256
> /* register offsets and bit positions */
>
> /*
> @@ -40,6 +44,9 @@
> #define PAGE_SEL_MASK 0x3f
> #define PAGE_LO_MASK 0x3ff
> #define PAGE_SEL_OFFSET_SHIFT 10
> +#define FUNC_SEL_SHIFT 19
> +#define FUNC_SEL_MASK 0x1ff
> +#define MSI_SW_CTRL_EN (1 << 29)
>
> #define PAB_ACTIVITY_STAT 0x81c
>
> @@ -100,6 +107,19 @@
> #define PAB_PEX_AMAP_PEX_WIN_L(win) PAB_REG_ADDR(0x4ba8, win)
> #define PAB_PEX_AMAP_PEX_WIN_H(win) PAB_REG_ADDR(0x4bac, win)
>
> +/* PPIO WINs EP mode */
> +#define PAB_PEX_BAR_AMAP(func, bar) (0x1ba0 + 0x20 * func + 4 * bar)
> +#define PAB_EXT_PEX_BAR_AMAP(func, bar) (0x84a0 + 0x20 * func + 4 * bar)
> +#define PEX_BAR_AMAP_EN (1 << 0)
> +
> +#define PAB_AXI_AMAP_PCI_HDR_PARAM(idx) (0x5ba0 + 0x04 * idx)
> +#define PAB_MSIX_TABLE_PBA_ACCESS 0xD000
> +
> +#define GPEX_BAR_ENABLE 0x4D4
> +#define GPEX_BAR_SIZE_LDW 0x4D8
> +#define GPEX_BAR_SIZE_UDW 0x4DC
> +#define GPEX_BAR_SELECT 0x4E0
> +
> /* starting offset of INTX bits in status register */
> #define PAB_INTX_START 5
>
> @@ -137,6 +157,7 @@
> ((off >> PAGE_SEL_OFFSET_SHIFT) & PAGE_SEL_MASK)
>
> struct mobiveil_pcie;
> +struct mobiveil_pcie_ep;
>
> struct mobiveil_msi { /* MSI information */
> struct mutex lock; /* protect bitmap variable */
> @@ -169,6 +190,29 @@ struct mobiveil_pab_ops {
> int (*host_init)(struct mobiveil_pcie *pcie); };
>
> +struct mobiveil_pcie_ep_ops {
> + void (*ep_init)(struct mobiveil_pcie_ep *ep);
> + int (*raise_irq)(struct mobiveil_pcie_ep *ep, u8 func_no,
> + enum pci_epc_irq_type type, u16
> +interrupt_num); };
> +
> +struct mobiveil_pcie_ep {
> + struct pci_epc *epc;
> + struct mobiveil_pcie_ep_ops *ops;
> + phys_addr_t phys_base;
> + size_t addr_size;
> + size_t page_size;
> + phys_addr_t *outbound_addr;
> + unsigned long *ob_window_map;
> + u32 num_ob_windows;
> + void __iomem *msi_mem;
> + phys_addr_t msi_mem_phys;
> + u8 msi_cap; /* MSI capability offset */
> + u8 msix_cap; /* MSI-X capability offset */
> + u8 bar_num;
> + u32 pf_num;
> +};
> +
> struct mobiveil_pcie {
> struct platform_device *pdev;
> struct list_head *resources;
> @@ -181,7 +225,10 @@ struct mobiveil_pcie {
> u32 ib_wins_configured; /* configured inbound windows */
> const struct mobiveil_pab_ops *ops;
> struct root_port rp;
> + struct mobiveil_pcie_ep ep;
> };
> +#define to_mobiveil_pcie_from_ep(endpoint) \
> + container_of((endpoint), struct
> +mobiveil_pcie, ep)
>
> int mobiveil_pcie_host_probe(struct mobiveil_pcie *pcie); int
> mobiveil_host_init(struct mobiveil_pcie *pcie, bool reinit); @@ -226,4
> +273,21 @@ static inline void csr_writeb(struct mobiveil_pcie *pcie, u32 val, u32 off)
> csr_write(pcie, val, off, 0x1); }
>
> +void program_ib_windows_ep(struct mobiveil_pcie *pcie, u8 func_no,
> + int bar, u64 phys); int
> +program_ob_windows_ep(struct mobiveil_pcie *pcie, int win_num, int type,
> + u64 phys, u64 bus_addr, u8 func, u64 size);
> +void mobiveil_pcie_disable_ib_win_ep(struct mobiveil_pcie *pci,
> + u8 func_no, u8 bar); int
> +mobiveil_pcie_ep_init(struct mobiveil_pcie_ep *ep); int
> +mobiveil_pcie_ep_raise_legacy_irq(struct mobiveil_pcie_ep *ep, u8
> +func_no); int mobiveil_pcie_ep_raise_msi_irq(struct mobiveil_pcie_ep *ep, u8 func_no,
> + u8 interrupt_num); int
> +mobiveil_pcie_ep_raise_msix_irq(struct mobiveil_pcie_ep *ep, u8 func_no,
> + u16 interrupt_num); void
> +mobiveil_pcie_ep_reset_bar(struct mobiveil_pcie *pci, enum pci_barno
> +bar); void mobiveil_pcie_enable_bridge_pio(struct mobiveil_pcie
> +*pci); void mobiveil_pcie_enable_engine_apio(struct mobiveil_pcie
> +*pci); void mobiveil_pcie_enable_engine_ppio(struct mobiveil_pcie
> +*pci); void mobiveil_pcie_enable_msi_ep(struct mobiveil_pcie *pci);
> #endif /* _PCIE_MOBIVEIL_H */
> --
> 1.7.1
>

Please review and fix macro alignments, otherwise looks ok.
[Xiaowei Bao] OK, thanks.
Reviewed-by: Subrahmanya Lingappa <[email protected]>

2019-04-25 15:34:14

by Lorenzo Pieralisi

[permalink] [raw]
Subject: Re: [PATCH 1/6] PCI: mobiveil: Add the EP mode support

On Mon, Feb 18, 2019 at 05:46:38PM +0800, Xiaowei Bao wrote:
> Add the EP mode support for Mobiveil base on endpoint framework.
>
> Signed-off-by: Xiaowei Bao <[email protected]>
> ---
> depends on: http://patchwork.ozlabs.org/project/linux-pci/list/?series=88754

You will have to rebase it on top of the updated series:

https://patchwork.ozlabs.org/user/todo/linux-pci/?series=102378
https://patchwork.ozlabs.org/user/todo/linux-pci/?series=102396

I will mark it as superseded in patchwork, thanks.

Lorenzo

> drivers/pci/controller/mobiveil/Kconfig | 5 +
> drivers/pci/controller/mobiveil/Makefile | 1 +
> drivers/pci/controller/mobiveil/pcie-mobiveil-ep.c | 527 ++++++++++++++++++++
> drivers/pci/controller/mobiveil/pcie-mobiveil.c | 100 ++++-
> drivers/pci/controller/mobiveil/pcie-mobiveil.h | 64 +++
> 5 files changed, 691 insertions(+), 6 deletions(-)
> create mode 100644 drivers/pci/controller/mobiveil/pcie-mobiveil-ep.c
>
> diff --git a/drivers/pci/controller/mobiveil/Kconfig b/drivers/pci/controller/mobiveil/Kconfig
> index 3ddb7d6..c037db6 100644
> --- a/drivers/pci/controller/mobiveil/Kconfig
> +++ b/drivers/pci/controller/mobiveil/Kconfig
> @@ -11,6 +11,11 @@ config PCIE_MOBIVEIL_HOST
> depends on PCI_MSI_IRQ_DOMAIN
> select PCIE_MOBIVEIL
>
> +config PCIE_MOBIVEIL_EP
> + bool
> + depends on PCI_ENDPOINT
> + select PCIE_MOBIVEIL
> +
> config PCIE_MOBIVEIL_PLAT
> bool "Mobiveil AXI PCIe controller"
> depends on ARCH_ZYNQMP || COMPILE_TEST
> diff --git a/drivers/pci/controller/mobiveil/Makefile b/drivers/pci/controller/mobiveil/Makefile
> index ff66774..4f520b7 100644
> --- a/drivers/pci/controller/mobiveil/Makefile
> +++ b/drivers/pci/controller/mobiveil/Makefile
> @@ -1,5 +1,6 @@
> # SPDX-License-Identifier: GPL-2.0
> obj-$(CONFIG_PCIE_MOBIVEIL) += pcie-mobiveil.o
> obj-$(CONFIG_PCIE_MOBIVEIL_HOST) += pcie-mobiveil-host.o
> +obj-$(CONFIG_PCIE_MOBIVEIL_EP) += pcie-mobiveil-ep.o
> obj-$(CONFIG_PCIE_MOBIVEIL_PLAT) += pcie-mobiveil-plat.o
> obj-$(CONFIG_PCI_LAYERSCAPE_GEN4) += pci-layerscape-gen4.o
> diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil-ep.c b/drivers/pci/controller/mobiveil/pcie-mobiveil-ep.c
> new file mode 100644
> index 0000000..16ca7fb
> --- /dev/null
> +++ b/drivers/pci/controller/mobiveil/pcie-mobiveil-ep.c
> @@ -0,0 +1,527 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/**
> + * Mobiveil PCIe Endpoint controller driver
> + *
> + * Copyright (C) 2018 NXP Semiconductor.
> + * Author: Xiaowei Bao <[email protected]>
> + */
> +
> +#include <linux/of.h>
> +#include <linux/pci-epc.h>
> +#include <linux/pci-epf.h>
> +#include <linux/platform_device.h>
> +#include "pcie-mobiveil.h"
> +
> +void mobiveil_pcie_ep_linkup(struct mobiveil_pcie_ep *ep)
> +{
> + struct pci_epc *epc = ep->epc;
> +
> + pci_epc_linkup(epc);
> +}
> +
> +static void __mobiveil_pcie_ep_reset_bar(struct mobiveil_pcie *pcie,
> + enum pci_barno bar)
> +{
> + csr_writel(pcie, bar, GPEX_BAR_SELECT);
> + csr_writel(pcie, 0, GPEX_BAR_SIZE_LDW);
> + csr_writel(pcie, 0, GPEX_BAR_SIZE_UDW);
> +}
> +
> +void mobiveil_pcie_ep_reset_bar(struct mobiveil_pcie *pcie,
> + enum pci_barno bar)
> +{
> + __mobiveil_pcie_ep_reset_bar(pcie, bar);
> +}
> +
> +static u8 __mobiveil_pcie_ep_find_next_cap(struct mobiveil_pcie *pcie,
> + u8 cap_ptr, u8 cap)
> +{
> + u8 cap_id, next_cap_ptr;
> + u16 reg;
> +
> + reg = csr_readw(pcie, cap_ptr);
> + next_cap_ptr = (reg & 0xff00) >> 8;
> + cap_id = (reg & 0x00ff);
> +
> + if (cap_id == cap)
> + return cap_ptr;
> +
> + if (!next_cap_ptr || cap_id > PCI_CAP_ID_MAX)
> + return 0;
> +
> + return __mobiveil_pcie_ep_find_next_cap(pcie, next_cap_ptr, cap);
> +}
> +
> +static u8 mobiveil_pcie_ep_find_capability(struct mobiveil_pcie *pcie,
> + u8 cap)
> +{
> + u8 next_cap_ptr;
> + u16 reg;
> +
> + reg = csr_readw(pcie, PCI_CAPABILITY_LIST);
> + next_cap_ptr = (reg & 0x00ff);
> +
> + if (!next_cap_ptr)
> + return 0;
> +
> + return __mobiveil_pcie_ep_find_next_cap(pcie, next_cap_ptr, cap);
> +}
> +
> +static int mobiveil_pcie_ep_write_header(struct pci_epc *epc, u8 func_no,
> + struct pci_epf_header *hdr)
> +{
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> +
> + csr_writew(pcie, hdr->vendorid, PCI_VENDOR_ID);
> + csr_writew(pcie, hdr->deviceid, PCI_DEVICE_ID);
> + csr_writeb(pcie, hdr->revid, PCI_REVISION_ID);
> + csr_writeb(pcie, hdr->progif_code, PCI_CLASS_PROG);
> + csr_writew(pcie, hdr->subclass_code | hdr->baseclass_code << 8,
> + PCI_CLASS_DEVICE);
> + csr_writeb(pcie, hdr->cache_line_size, PCI_CACHE_LINE_SIZE);
> + csr_writew(pcie, hdr->subsys_vendor_id, PCI_SUBSYSTEM_VENDOR_ID);
> + csr_writew(pcie, hdr->subsys_id, PCI_SUBSYSTEM_ID);
> + csr_writeb(pcie, hdr->interrupt_pin, PCI_INTERRUPT_PIN);
> +
> + return 0;
> +}
> +
> +static int mobiveil_pcie_ep_inbound_atu(struct mobiveil_pcie_ep *ep,
> + u8 func_no, enum pci_barno bar,
> + dma_addr_t cpu_addr)
> +{
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> +
> + program_ib_windows_ep(pcie, func_no, bar, cpu_addr);
> +
> + return 0;
> +}
> +
> +static int mobiveil_pcie_ep_outbound_atu(struct mobiveil_pcie_ep *ep,
> + phys_addr_t phys_addr,
> + u64 pci_addr, u8 func_no,
> + size_t size)
> +{
> + int ret;
> + u32 free_win;
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> +
> + free_win = find_first_zero_bit(ep->ob_window_map, ep->num_ob_windows);
> + if (free_win >= ep->num_ob_windows) {
> + dev_err(&pcie->pdev->dev, "No free outbound window\n");
> + return -EINVAL;
> + }
> +
> + ret = program_ob_windows_ep(pcie, free_win, MEM_WINDOW_TYPE,
> + phys_addr, pci_addr, func_no, size);
> + if (ret < 0) {
> + dev_err(&pcie->pdev->dev, "Failed to program IB window\n");
> + return ret;
> + }
> +
> + set_bit(free_win, ep->ob_window_map);
> + ep->outbound_addr[free_win] = phys_addr;
> +
> + return 0;
> +}
> +
> +static void mobiveil_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no,
> + struct pci_epf_bar *epf_bar)
> +{
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + enum pci_barno bar = epf_bar->barno;
> +
> + if (bar < ep->bar_num) {
> + __mobiveil_pcie_ep_reset_bar(pcie,
> + func_no * ep->bar_num + bar);
> +
> + mobiveil_pcie_disable_ib_win_ep(pcie, func_no, bar);
> + }
> +}
> +
> +static int mobiveil_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no,
> + struct pci_epf_bar *epf_bar)
> +{
> + int ret;
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + enum pci_barno bar = epf_bar->barno;
> + size_t size = epf_bar->size;
> +
> + if (bar < ep->bar_num) {
> + ret = mobiveil_pcie_ep_inbound_atu(ep, func_no, bar,
> + epf_bar->phys_addr);
> + if (ret)
> + return ret;
> +
> + csr_writel(pcie, func_no * ep->bar_num + bar,
> + GPEX_BAR_SELECT);
> + csr_writel(pcie, lower_32_bits(~(size - 1)),
> + GPEX_BAR_SIZE_LDW);
> + csr_writel(pcie, upper_32_bits(~(size - 1)),
> + GPEX_BAR_SIZE_UDW);
> + }
> +
> + return 0;
> +}
> +
> +static int mobiveil_pcie_find_index(struct mobiveil_pcie_ep *ep,
> + phys_addr_t addr,
> + u32 *atu_index)
> +{
> + u32 index;
> +
> + for (index = 0; index < ep->num_ob_windows; index++) {
> + if (ep->outbound_addr[index] != addr)
> + continue;
> + *atu_index = index;
> + return 0;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static void mobiveil_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no,
> + phys_addr_t addr)
> +{
> + int ret;
> + u32 atu_index;
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> +
> + ret = mobiveil_pcie_find_index(ep, addr, &atu_index);
> + if (ret < 0)
> + return;
> +
> + mobiveil_pcie_disable_ob_win(pcie, atu_index);
> + clear_bit(atu_index, ep->ob_window_map);
> +}
> +
> +static int mobiveil_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no,
> + phys_addr_t addr,
> + u64 pci_addr, size_t size)
> +{
> + int ret;
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> +
> + ret = mobiveil_pcie_ep_outbound_atu(ep, addr, pci_addr, func_no, size);
> + if (ret) {
> + dev_err(&pcie->pdev->dev, "Failed to enable address\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int mobiveil_pcie_ep_get_msi(struct pci_epc *epc, u8 func_no)
> +{
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + u32 val, reg;
> +
> + if (!ep->msi_cap)
> + return -EINVAL;
> +
> + reg = ep->msi_cap + PCI_MSI_FLAGS;
> + val = csr_readw(pcie, reg);
> + if (!(val & PCI_MSI_FLAGS_ENABLE))
> + return -EINVAL;
> +
> + val = (val & PCI_MSI_FLAGS_QSIZE) >> 4;
> +
> + return val;
> +}
> +
> +static int mobiveil_pcie_ep_set_msi(struct pci_epc *epc,
> + u8 func_no, u8 interrupts)
> +{
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + u32 val, reg;
> +
> + if (!ep->msi_cap)
> + return -EINVAL;
> +
> + reg = ep->msi_cap + PCI_MSI_FLAGS;
> + val = csr_readw(pcie, reg);
> + val &= ~PCI_MSI_FLAGS_QMASK;
> + val |= (interrupts << 1) & PCI_MSI_FLAGS_QMASK;
> + csr_writew(pcie, val, reg);
> +
> + return 0;
> +}
> +
> +static int mobiveil_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no)
> +{
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + u32 val, reg;
> +
> + if (!ep->msix_cap)
> + return -EINVAL;
> +
> + reg = ep->msix_cap + PCI_MSIX_FLAGS;
> + val = csr_readw(pcie, reg);
> + if (!(val & PCI_MSIX_FLAGS_ENABLE))
> + return -EINVAL;
> +
> + val &= PCI_MSIX_FLAGS_QSIZE;
> +
> + return val;
> +}
> +
> +static int mobiveil_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no,
> + u16 interrupts)
> +{
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + u32 val, reg;
> +
> + if (!ep->msix_cap)
> + return -EINVAL;
> +
> + reg = ep->msix_cap + PCI_MSIX_FLAGS;
> + val = csr_readw(pcie, reg);
> + val &= ~PCI_MSIX_FLAGS_QSIZE;
> + val |= interrupts;
> + csr_writew(pcie, val, reg);
> +
> + return 0;
> +}
> +
> +static int mobiveil_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no,
> + enum pci_epc_irq_type type,
> + u16 interrupt_num)
> +{
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> +
> + if (!ep->ops->raise_irq)
> + return -EINVAL;
> +
> + return ep->ops->raise_irq(ep, func_no, type, interrupt_num);
> +}
> +
> +static const struct pci_epc_ops epc_ops = {
> + .write_header = mobiveil_pcie_ep_write_header,
> + .set_bar = mobiveil_pcie_ep_set_bar,
> + .clear_bar = mobiveil_pcie_ep_clear_bar,
> + .map_addr = mobiveil_pcie_ep_map_addr,
> + .unmap_addr = mobiveil_pcie_ep_unmap_addr,
> + .set_msi = mobiveil_pcie_ep_set_msi,
> + .get_msi = mobiveil_pcie_ep_get_msi,
> + .set_msix = mobiveil_pcie_ep_set_msix,
> + .get_msix = mobiveil_pcie_ep_get_msix,
> + .raise_irq = mobiveil_pcie_ep_raise_irq,
> +};
> +
> +int mobiveil_pcie_ep_raise_legacy_irq(struct mobiveil_pcie_ep *ep, u8 func_no)
> +{
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> +
> + dev_err(&pcie->pdev->dev, "EP cannot trigger legacy IRQs\n");
> +
> + return -EINVAL;
> +}
> +
> +int mobiveil_pcie_ep_raise_msi_irq(struct mobiveil_pcie_ep *ep, u8 func_no,
> + u8 interrupt_num)
> +{
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + struct pci_epc *epc = ep->epc;
> + u16 msg_ctrl, msg_data;
> + u32 msg_addr_lower, msg_addr_upper, reg;
> + u64 msg_addr;
> + u32 func_num;
> + bool has_upper;
> + int ret;
> +
> + if (!ep->msi_cap)
> + return -EINVAL;
> +
> + /*
> + * In order ot get the PF's MSI capability register value from config
> + * space we need to set the PF number to the PAB_CTRL register.
> + */
> + func_num = csr_readl(pcie, PAB_CTRL);
> + func_num &= ~(FUNC_SEL_MASK << FUNC_SEL_SHIFT);
> + func_num |= (func_no & FUNC_SEL_MASK) << FUNC_SEL_SHIFT;
> + csr_writel(pcie, func_num, PAB_CTRL);
> +
> + reg = ep->msi_cap + PCI_MSI_FLAGS;
> + msg_ctrl = csr_readw(pcie, reg);
> + has_upper = !!(msg_ctrl & PCI_MSI_FLAGS_64BIT);
> + reg = ep->msi_cap + PCI_MSI_ADDRESS_LO;
> + msg_addr_lower = csr_readl(pcie, reg);
> + if (has_upper) {
> + reg = ep->msi_cap + PCI_MSI_ADDRESS_HI;
> + msg_addr_upper = csr_readl(pcie, reg);
> + reg = ep->msi_cap + PCI_MSI_DATA_64;
> + msg_data = csr_readw(pcie, reg);
> + } else {
> + msg_addr_upper = 0;
> + reg = ep->msi_cap + PCI_MSI_DATA_32;
> + msg_data = csr_readw(pcie, reg);
> + }
> + msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower;
> +
> + /*
> + * clear the FUNC_SEL_SHIFT bits when access other registers except
> + * config space register.
> + */
> + func_num = csr_readl(pcie, PAB_CTRL);
> + func_num &= ~(FUNC_SEL_MASK << FUNC_SEL_SHIFT);
> + csr_writel(pcie, func_num, PAB_CTRL);
> +
> + ret = mobiveil_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys,
> + msg_addr, epc->mem->page_size);
> + if (ret)
> + return ret;
> +
> + writel(msg_data | (interrupt_num - 1), ep->msi_mem);
> +
> + mobiveil_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys);
> +
> + return 0;
> +}
> +
> +int mobiveil_pcie_ep_raise_msix_irq(struct mobiveil_pcie_ep *ep, u8 func_no,
> + u16 interrupt_num)
> +{
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + struct pci_epc *epc = ep->epc;
> + u32 msg_addr_upper, msg_addr_lower;
> + u32 msg_data;
> + u64 msg_addr;
> + u32 func_num;
> + int ret;
> +
> + /*
> + * In order ot get the PF's MSI capability register value from config
> + * space we need to set the PF number to the PAB_CTRL register.
> + */
> + func_num = csr_readl(pcie, PAB_CTRL);
> + func_num &= ~(FUNC_SEL_MASK << FUNC_SEL_SHIFT);
> + func_num |= (func_no & FUNC_SEL_MASK) << FUNC_SEL_SHIFT;
> + csr_writel(pcie, func_num, PAB_CTRL);
> +
> + msg_addr_lower = csr_readl(pcie, PAB_MSIX_TABLE_PBA_ACCESS +
> + PCI_MSIX_ENTRY_LOWER_ADDR +
> + (interrupt_num - 1) * PCI_MSIX_ENTRY_SIZE);
> + msg_addr_upper = csr_readl(pcie, PAB_MSIX_TABLE_PBA_ACCESS +
> + PCI_MSIX_ENTRY_UPPER_ADDR +
> + (interrupt_num - 1) * PCI_MSIX_ENTRY_SIZE);
> + msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower;
> + msg_data = csr_readl(pcie, PAB_MSIX_TABLE_PBA_ACCESS +
> + PCI_MSIX_ENTRY_DATA +
> + (interrupt_num - 1) * PCI_MSIX_ENTRY_SIZE);
> +
> + /*
> + * clear the FUNC_SEL_SHIFT bits when access other registers except
> + * config space registers.
> + */
> + func_num = csr_readl(pcie, PAB_CTRL);
> + func_num &= ~(FUNC_SEL_MASK << FUNC_SEL_SHIFT);
> + csr_writel(pcie, func_num, PAB_CTRL);
> +
> + ret = mobiveil_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys,
> + msg_addr, epc->mem->page_size);
> + if (ret)
> + return ret;
> +
> + writel(msg_data, ep->msi_mem);
> +
> + mobiveil_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys);
> +
> + return 0;
> +}
> +
> +void mobiveil_pcie_ep_exit(struct mobiveil_pcie_ep *ep)
> +{
> + struct pci_epc *epc = ep->epc;
> +
> + pci_epc_mem_free_addr(epc, ep->msi_mem_phys, ep->msi_mem,
> + epc->mem->page_size);
> +
> + pci_epc_mem_exit(epc);
> +}
> +
> +int mobiveil_pcie_ep_init(struct mobiveil_pcie_ep *ep)
> +{
> + int ret;
> + void *addr;
> + struct pci_epc *epc;
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + struct device *dev = &pcie->pdev->dev;
> + struct device_node *np = dev->of_node;
> +
> + if (!pcie->csr_axi_slave_base) {
> + dev_err(dev, "csr_base is not populated\n");
> + return -EINVAL;
> + }
> +
> + ret = of_property_read_u32(np, "num-ob-windows", &ep->num_ob_windows);
> + if (ret < 0) {
> + dev_err(dev, "Unable to read *num-ob-windows* property\n");
> + return ret;
> + }
> +
> + if (ep->num_ob_windows > MAX_IATU_OUT) {
> + dev_err(dev, "Invalid *num-ob-windows*\n");
> + return -EINVAL;
> + }
> + ep->ob_window_map = devm_kcalloc(dev,
> + BITS_TO_LONGS(ep->num_ob_windows),
> + sizeof(long),
> + GFP_KERNEL);
> + if (!ep->ob_window_map)
> + return -ENOMEM;
> +
> + addr = devm_kcalloc(dev, ep->num_ob_windows, sizeof(phys_addr_t),
> + GFP_KERNEL);
> + if (!addr)
> + return -ENOMEM;
> + ep->outbound_addr = addr;
> +
> + mobiveil_pcie_enable_bridge_pio(pcie);
> + mobiveil_pcie_enable_engine_apio(pcie);
> + mobiveil_pcie_enable_engine_ppio(pcie);
> + mobiveil_pcie_enable_msi_ep(pcie);
> +
> + epc = devm_pci_epc_create(dev, &epc_ops);
> + if (IS_ERR(epc)) {
> + dev_err(dev, "Failed to create epc device\n");
> + return PTR_ERR(epc);
> + }
> +
> + ep->epc = epc;
> + epc_set_drvdata(epc, ep);
> +
> + ep->msi_cap = mobiveil_pcie_ep_find_capability(pcie, PCI_CAP_ID_MSI);
> +
> + ep->msix_cap = mobiveil_pcie_ep_find_capability(pcie,
> + PCI_CAP_ID_MSIX);
> +
> + if (ep->ops->ep_init)
> + ep->ops->ep_init(ep);
> +
> + epc->max_functions = ep->pf_num;
> +
> + ret = __pci_epc_mem_init(epc, ep->phys_base, ep->addr_size,
> + ep->page_size);
> + if (ret < 0) {
> + dev_err(dev, "Failed to initialize address space\n");
> + return ret;
> + }
> +
> + ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys,
> + epc->mem->page_size);
> + if (!ep->msi_mem) {
> + dev_err(dev, "Failed to reserve memory for MSI/MSI-X\n");
> + return -ENOMEM;
> + }
> +
> + return 0;
> +}
> diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil.c b/drivers/pci/controller/mobiveil/pcie-mobiveil.c
> index 49d471b..d1fdfed 100644
> --- a/drivers/pci/controller/mobiveil/pcie-mobiveil.c
> +++ b/drivers/pci/controller/mobiveil/pcie-mobiveil.c
> @@ -210,6 +210,57 @@ void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, u64 cpu_addr,
> pcie->ob_wins_configured++;
> }
>
> +int program_ob_windows_ep(struct mobiveil_pcie *pcie, int win_num, int type,
> + u64 phys, u64 bus_addr, u8 func, u64 size)
> +{
> + u32 val;
> + u32 size_h, size_l;
> +
> + if (size & (size - 1))
> + size = 1 << (1 + ilog2(size));
> +
> + size_h = upper_32_bits(~(size - 1));
> + size_l = lower_32_bits(~(size - 1));
> +
> + val = csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num));
> + val &= ~(WIN_TYPE_MASK << WIN_TYPE_SHIFT |
> + WIN_SIZE_MASK << WIN_SIZE_SHIFT);
> + val |= 1 << WIN_ENABLE_SHIFT | type << WIN_TYPE_SHIFT |
> + (size_l & (WIN_SIZE_MASK << WIN_SIZE_SHIFT));
> + csr_writel(pcie, val, PAB_AXI_AMAP_CTRL(win_num));
> +
> + csr_writel(pcie, func, PAB_AXI_AMAP_PCI_HDR_PARAM(win_num));
> + csr_writel(pcie, lower_32_bits(phys), PAB_AXI_AMAP_AXI_WIN(win_num));
> + csr_writel(pcie, upper_32_bits(phys),
> + PAB_EXT_AXI_AMAP_AXI_WIN(win_num));
> + csr_writel(pcie, lower_32_bits(bus_addr),
> + PAB_AXI_AMAP_PEX_WIN_L(win_num));
> + csr_writel(pcie, upper_32_bits(bus_addr),
> + PAB_AXI_AMAP_PEX_WIN_H(win_num));
> + csr_writel(pcie, size_h, PAB_EXT_AXI_AMAP_SIZE(win_num));
> +
> + return 0;
> +}
> +
> +void program_ib_windows_ep(struct mobiveil_pcie *pcie, u8 func_no,
> + int bar, u64 phys)
> +{
> + csr_writel(pcie, upper_32_bits(phys),
> + PAB_EXT_PEX_BAR_AMAP(func_no, bar));
> + csr_writel(pcie, lower_32_bits(phys) | PEX_BAR_AMAP_EN,
> + PAB_PEX_BAR_AMAP(func_no, bar));
> +}
> +
> +void mobiveil_pcie_disable_ib_win_ep(struct mobiveil_pcie *pcie,
> + u8 func_no, u8 bar)
> +{
> + u32 val;
> +
> + val = csr_readl(pcie, PAB_PEX_BAR_AMAP(func_no, bar));
> + val &= ~(1 << 0);
> + csr_writel(pcie, val, PAB_PEX_BAR_AMAP(func_no, bar));
> +}
> +
> int mobiveil_bringup_link(struct mobiveil_pcie *pcie)
> {
> int retries;
> @@ -227,20 +278,57 @@ int mobiveil_bringup_link(struct mobiveil_pcie *pcie)
> return -ETIMEDOUT;
> }
>
> -void mobiveil_pcie_disable_ib_win(struct mobiveil_pcie *pci, int win_num)
> +void mobiveil_pcie_disable_ib_win(struct mobiveil_pcie *pcie, int win_num)
> {
> u32 val;
>
> - val = csr_readl(pci, PAB_PEX_AMAP_CTRL(win_num));
> + val = csr_readl(pcie, PAB_PEX_AMAP_CTRL(win_num));
> val &= ~(1 << AMAP_CTRL_EN_SHIFT);
> - csr_writel(pci, val, PAB_PEX_AMAP_CTRL(win_num));
> + csr_writel(pcie, val, PAB_PEX_AMAP_CTRL(win_num));
> }
>
> -void mobiveil_pcie_disable_ob_win(struct mobiveil_pcie *pci, int win_num)
> +void mobiveil_pcie_disable_ob_win(struct mobiveil_pcie *pcie, int win_num)
> {
> u32 val;
>
> - val = csr_readl(pci, PAB_AXI_AMAP_CTRL(win_num));
> + val = csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num));
> val &= ~(1 << WIN_ENABLE_SHIFT);
> - csr_writel(pci, val, PAB_AXI_AMAP_CTRL(win_num));
> + csr_writel(pcie, val, PAB_AXI_AMAP_CTRL(win_num));
> +}
> +
> +void mobiveil_pcie_enable_bridge_pio(struct mobiveil_pcie *pcie)
> +{
> + u32 val;
> +
> + val = csr_readl(pcie, PAB_CTRL);
> + val |= 1 << AMBA_PIO_ENABLE_SHIFT;
> + val |= 1 << PEX_PIO_ENABLE_SHIFT;
> + csr_writel(pcie, val, PAB_CTRL);
> +}
> +
> +void mobiveil_pcie_enable_engine_apio(struct mobiveil_pcie *pcie)
> +{
> + u32 val;
> +
> + val = csr_readl(pcie, PAB_AXI_PIO_CTRL);
> + val |= APIO_EN_MASK;
> + csr_writel(pcie, val, PAB_AXI_PIO_CTRL);
> +}
> +
> +void mobiveil_pcie_enable_engine_ppio(struct mobiveil_pcie *pcie)
> +{
> + u32 val;
> +
> + val = csr_readl(pcie, PAB_PEX_PIO_CTRL);
> + val |= 1 << PIO_ENABLE_SHIFT;
> + csr_writel(pcie, val, PAB_PEX_PIO_CTRL);
> +}
> +
> +void mobiveil_pcie_enable_msi_ep(struct mobiveil_pcie *pcie)
> +{
> + u32 val;
> +
> + val = csr_readl(pcie, PAB_INTP_AMBA_MISC_ENB);
> + val |= 1 << 0;
> + csr_writel(pcie, val, PAB_INTP_AMBA_MISC_ENB);
> }
> diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil.h b/drivers/pci/controller/mobiveil/pcie-mobiveil.h
> index f0e2e4a..275c68f 100644
> --- a/drivers/pci/controller/mobiveil/pcie-mobiveil.h
> +++ b/drivers/pci/controller/mobiveil/pcie-mobiveil.h
> @@ -15,6 +15,10 @@
> #include <linux/msi.h>
> #include "../../pci.h"
>
> +#include <linux/pci-epc.h>
> +#include <linux/pci-epf.h>
> +
> +#define MAX_IATU_OUT 256
> /* register offsets and bit positions */
>
> /*
> @@ -40,6 +44,9 @@
> #define PAGE_SEL_MASK 0x3f
> #define PAGE_LO_MASK 0x3ff
> #define PAGE_SEL_OFFSET_SHIFT 10
> +#define FUNC_SEL_SHIFT 19
> +#define FUNC_SEL_MASK 0x1ff
> +#define MSI_SW_CTRL_EN (1 << 29)
>
> #define PAB_ACTIVITY_STAT 0x81c
>
> @@ -100,6 +107,19 @@
> #define PAB_PEX_AMAP_PEX_WIN_L(win) PAB_REG_ADDR(0x4ba8, win)
> #define PAB_PEX_AMAP_PEX_WIN_H(win) PAB_REG_ADDR(0x4bac, win)
>
> +/* PPIO WINs EP mode */
> +#define PAB_PEX_BAR_AMAP(func, bar) (0x1ba0 + 0x20 * func + 4 * bar)
> +#define PAB_EXT_PEX_BAR_AMAP(func, bar) (0x84a0 + 0x20 * func + 4 * bar)
> +#define PEX_BAR_AMAP_EN (1 << 0)
> +
> +#define PAB_AXI_AMAP_PCI_HDR_PARAM(idx) (0x5ba0 + 0x04 * idx)
> +#define PAB_MSIX_TABLE_PBA_ACCESS 0xD000
> +
> +#define GPEX_BAR_ENABLE 0x4D4
> +#define GPEX_BAR_SIZE_LDW 0x4D8
> +#define GPEX_BAR_SIZE_UDW 0x4DC
> +#define GPEX_BAR_SELECT 0x4E0
> +
> /* starting offset of INTX bits in status register */
> #define PAB_INTX_START 5
>
> @@ -137,6 +157,7 @@
> ((off >> PAGE_SEL_OFFSET_SHIFT) & PAGE_SEL_MASK)
>
> struct mobiveil_pcie;
> +struct mobiveil_pcie_ep;
>
> struct mobiveil_msi { /* MSI information */
> struct mutex lock; /* protect bitmap variable */
> @@ -169,6 +190,29 @@ struct mobiveil_pab_ops {
> int (*host_init)(struct mobiveil_pcie *pcie);
> };
>
> +struct mobiveil_pcie_ep_ops {
> + void (*ep_init)(struct mobiveil_pcie_ep *ep);
> + int (*raise_irq)(struct mobiveil_pcie_ep *ep, u8 func_no,
> + enum pci_epc_irq_type type, u16 interrupt_num);
> +};
> +
> +struct mobiveil_pcie_ep {
> + struct pci_epc *epc;
> + struct mobiveil_pcie_ep_ops *ops;
> + phys_addr_t phys_base;
> + size_t addr_size;
> + size_t page_size;
> + phys_addr_t *outbound_addr;
> + unsigned long *ob_window_map;
> + u32 num_ob_windows;
> + void __iomem *msi_mem;
> + phys_addr_t msi_mem_phys;
> + u8 msi_cap; /* MSI capability offset */
> + u8 msix_cap; /* MSI-X capability offset */
> + u8 bar_num;
> + u32 pf_num;
> +};
> +
> struct mobiveil_pcie {
> struct platform_device *pdev;
> struct list_head *resources;
> @@ -181,7 +225,10 @@ struct mobiveil_pcie {
> u32 ib_wins_configured; /* configured inbound windows */
> const struct mobiveil_pab_ops *ops;
> struct root_port rp;
> + struct mobiveil_pcie_ep ep;
> };
> +#define to_mobiveil_pcie_from_ep(endpoint) \
> + container_of((endpoint), struct mobiveil_pcie, ep)
>
> int mobiveil_pcie_host_probe(struct mobiveil_pcie *pcie);
> int mobiveil_host_init(struct mobiveil_pcie *pcie, bool reinit);
> @@ -226,4 +273,21 @@ static inline void csr_writeb(struct mobiveil_pcie *pcie, u32 val, u32 off)
> csr_write(pcie, val, off, 0x1);
> }
>
> +void program_ib_windows_ep(struct mobiveil_pcie *pcie, u8 func_no,
> + int bar, u64 phys);
> +int program_ob_windows_ep(struct mobiveil_pcie *pcie, int win_num, int type,
> + u64 phys, u64 bus_addr, u8 func, u64 size);
> +void mobiveil_pcie_disable_ib_win_ep(struct mobiveil_pcie *pci,
> + u8 func_no, u8 bar);
> +int mobiveil_pcie_ep_init(struct mobiveil_pcie_ep *ep);
> +int mobiveil_pcie_ep_raise_legacy_irq(struct mobiveil_pcie_ep *ep, u8 func_no);
> +int mobiveil_pcie_ep_raise_msi_irq(struct mobiveil_pcie_ep *ep, u8 func_no,
> + u8 interrupt_num);
> +int mobiveil_pcie_ep_raise_msix_irq(struct mobiveil_pcie_ep *ep, u8 func_no,
> + u16 interrupt_num);
> +void mobiveil_pcie_ep_reset_bar(struct mobiveil_pcie *pci, enum pci_barno bar);
> +void mobiveil_pcie_enable_bridge_pio(struct mobiveil_pcie *pci);
> +void mobiveil_pcie_enable_engine_apio(struct mobiveil_pcie *pci);
> +void mobiveil_pcie_enable_engine_ppio(struct mobiveil_pcie *pci);
> +void mobiveil_pcie_enable_msi_ep(struct mobiveil_pcie *pci);
> #endif /* _PCIE_MOBIVEIL_H */
> --
> 1.7.1
>

2019-04-29 02:40:02

by Bao Xiaowei

[permalink] [raw]
Subject: RE: [EXT] Re: [PATCH 1/6] PCI: mobiveil: Add the EP mode support

Hi Lorenzo,

Thanks a lot for your comments, I am waiting for this patches approve of http://patchwork.ozlabs.org/project/linux-pci/list/?series=102378, and once these patches approved, I will resent the patches base on the latest base.

Best regards
Xiaowei

-----Original Message-----
From: Lorenzo Pieralisi <[email protected]>
Sent: 2019??4??25?? 19:32
To: Xiaowei Bao <[email protected]>
Cc: [email protected]; Z.q. Hou <[email protected]>; [email protected]; [email protected]; [email protected]; Leo Li <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; M.h. Lian <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]
Subject: [EXT] Re: [PATCH 1/6] PCI: mobiveil: Add the EP mode support

Caution: EXT Email

On Mon, Feb 18, 2019 at 05:46:38PM +0800, Xiaowei Bao wrote:
> Add the EP mode support for Mobiveil base on endpoint framework.
>
> Signed-off-by: Xiaowei Bao <[email protected]>
> ---
> depends on:
> https://eur01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fpatch
> work.ozlabs.org%2Fproject%2Flinux-pci%2Flist%2F%3Fseries%3D88754&amp;d
> ata=02%7C01%7Cxiaowei.bao%40nxp.com%7C52755aabae3a4cca65ba08d6c971a7de
> %7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0%7C0%7C636917887360279903&amp;s
> data=9nw%2BwVKx11937Vh5ShwQNX97QzC3q9PJVTCQsbFbfjc%3D&amp;reserved=0

You will have to rebase it on top of the updated series:

https://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fpatchwork.ozlabs.org%2Fuser%2Ftodo%2Flinux-pci%2F%3Fseries%3D102378&amp;data=02%7C01%7Cxiaowei.bao%40nxp.com%7C52755aabae3a4cca65ba08d6c971a7de%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0%7C0%7C636917887360279903&amp;sdata=vqpwdXMzTtc4Uf2vt3vHxOL5PlHHYY%2F3Dsfj7CIsJfc%3D&amp;reserved=0
https://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fpatchwork.ozlabs.org%2Fuser%2Ftodo%2Flinux-pci%2F%3Fseries%3D102396&amp;data=02%7C01%7Cxiaowei.bao%40nxp.com%7C52755aabae3a4cca65ba08d6c971a7de%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0%7C0%7C636917887360279903&amp;sdata=D%2F5cTIiBy5CHU4K7K7n6GHcFlrt6RV%2Ff9jsjx8bmaY4%3D&amp;reserved=0

I will mark it as superseded in patchwork, thanks.

Lorenzo

> drivers/pci/controller/mobiveil/Kconfig | 5 +
> drivers/pci/controller/mobiveil/Makefile | 1 +
> drivers/pci/controller/mobiveil/pcie-mobiveil-ep.c | 527 ++++++++++++++++++++
> drivers/pci/controller/mobiveil/pcie-mobiveil.c | 100 ++++-
> drivers/pci/controller/mobiveil/pcie-mobiveil.h | 64 +++
> 5 files changed, 691 insertions(+), 6 deletions(-) create mode
> 100644 drivers/pci/controller/mobiveil/pcie-mobiveil-ep.c
>
> diff --git a/drivers/pci/controller/mobiveil/Kconfig
> b/drivers/pci/controller/mobiveil/Kconfig
> index 3ddb7d6..c037db6 100644
> --- a/drivers/pci/controller/mobiveil/Kconfig
> +++ b/drivers/pci/controller/mobiveil/Kconfig
> @@ -11,6 +11,11 @@ config PCIE_MOBIVEIL_HOST
> depends on PCI_MSI_IRQ_DOMAIN
> select PCIE_MOBIVEIL
>
> +config PCIE_MOBIVEIL_EP
> + bool
> + depends on PCI_ENDPOINT
> + select PCIE_MOBIVEIL
> +
> config PCIE_MOBIVEIL_PLAT
> bool "Mobiveil AXI PCIe controller"
> depends on ARCH_ZYNQMP || COMPILE_TEST diff --git
> a/drivers/pci/controller/mobiveil/Makefile
> b/drivers/pci/controller/mobiveil/Makefile
> index ff66774..4f520b7 100644
> --- a/drivers/pci/controller/mobiveil/Makefile
> +++ b/drivers/pci/controller/mobiveil/Makefile
> @@ -1,5 +1,6 @@
> # SPDX-License-Identifier: GPL-2.0
> obj-$(CONFIG_PCIE_MOBIVEIL) += pcie-mobiveil.o
> obj-$(CONFIG_PCIE_MOBIVEIL_HOST) += pcie-mobiveil-host.o
> +obj-$(CONFIG_PCIE_MOBIVEIL_EP) += pcie-mobiveil-ep.o
> obj-$(CONFIG_PCIE_MOBIVEIL_PLAT) += pcie-mobiveil-plat.o
> obj-$(CONFIG_PCI_LAYERSCAPE_GEN4) += pci-layerscape-gen4.o diff --git
> a/drivers/pci/controller/mobiveil/pcie-mobiveil-ep.c
> b/drivers/pci/controller/mobiveil/pcie-mobiveil-ep.c
> new file mode 100644
> index 0000000..16ca7fb
> --- /dev/null
> +++ b/drivers/pci/controller/mobiveil/pcie-mobiveil-ep.c
> @@ -0,0 +1,527 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/**
> + * Mobiveil PCIe Endpoint controller driver
> + *
> + * Copyright (C) 2018 NXP Semiconductor.
> + * Author: Xiaowei Bao <[email protected]> */
> +
> +#include <linux/of.h>
> +#include <linux/pci-epc.h>
> +#include <linux/pci-epf.h>
> +#include <linux/platform_device.h>
> +#include "pcie-mobiveil.h"
> +
> +void mobiveil_pcie_ep_linkup(struct mobiveil_pcie_ep *ep) {
> + struct pci_epc *epc = ep->epc;
> +
> + pci_epc_linkup(epc);
> +}
> +
> +static void __mobiveil_pcie_ep_reset_bar(struct mobiveil_pcie *pcie,
> + enum pci_barno bar) {
> + csr_writel(pcie, bar, GPEX_BAR_SELECT);
> + csr_writel(pcie, 0, GPEX_BAR_SIZE_LDW);
> + csr_writel(pcie, 0, GPEX_BAR_SIZE_UDW); }
> +
> +void mobiveil_pcie_ep_reset_bar(struct mobiveil_pcie *pcie,
> + enum pci_barno bar) {
> + __mobiveil_pcie_ep_reset_bar(pcie, bar); }
> +
> +static u8 __mobiveil_pcie_ep_find_next_cap(struct mobiveil_pcie *pcie,
> + u8 cap_ptr, u8 cap) {
> + u8 cap_id, next_cap_ptr;
> + u16 reg;
> +
> + reg = csr_readw(pcie, cap_ptr);
> + next_cap_ptr = (reg & 0xff00) >> 8;
> + cap_id = (reg & 0x00ff);
> +
> + if (cap_id == cap)
> + return cap_ptr;
> +
> + if (!next_cap_ptr || cap_id > PCI_CAP_ID_MAX)
> + return 0;
> +
> + return __mobiveil_pcie_ep_find_next_cap(pcie, next_cap_ptr,
> +cap); }
> +
> +static u8 mobiveil_pcie_ep_find_capability(struct mobiveil_pcie *pcie,
> + u8 cap) {
> + u8 next_cap_ptr;
> + u16 reg;
> +
> + reg = csr_readw(pcie, PCI_CAPABILITY_LIST);
> + next_cap_ptr = (reg & 0x00ff);
> +
> + if (!next_cap_ptr)
> + return 0;
> +
> + return __mobiveil_pcie_ep_find_next_cap(pcie, next_cap_ptr,
> +cap); }
> +
> +static int mobiveil_pcie_ep_write_header(struct pci_epc *epc, u8 func_no,
> + struct pci_epf_header *hdr) {
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> +
> + csr_writew(pcie, hdr->vendorid, PCI_VENDOR_ID);
> + csr_writew(pcie, hdr->deviceid, PCI_DEVICE_ID);
> + csr_writeb(pcie, hdr->revid, PCI_REVISION_ID);
> + csr_writeb(pcie, hdr->progif_code, PCI_CLASS_PROG);
> + csr_writew(pcie, hdr->subclass_code | hdr->baseclass_code << 8,
> + PCI_CLASS_DEVICE);
> + csr_writeb(pcie, hdr->cache_line_size, PCI_CACHE_LINE_SIZE);
> + csr_writew(pcie, hdr->subsys_vendor_id, PCI_SUBSYSTEM_VENDOR_ID);
> + csr_writew(pcie, hdr->subsys_id, PCI_SUBSYSTEM_ID);
> + csr_writeb(pcie, hdr->interrupt_pin, PCI_INTERRUPT_PIN);
> +
> + return 0;
> +}
> +
> +static int mobiveil_pcie_ep_inbound_atu(struct mobiveil_pcie_ep *ep,
> + u8 func_no, enum pci_barno bar,
> + dma_addr_t cpu_addr) {
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> +
> + program_ib_windows_ep(pcie, func_no, bar, cpu_addr);
> +
> + return 0;
> +}
> +
> +static int mobiveil_pcie_ep_outbound_atu(struct mobiveil_pcie_ep *ep,
> + phys_addr_t phys_addr,
> + u64 pci_addr, u8 func_no,
> + size_t size) {
> + int ret;
> + u32 free_win;
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> +
> + free_win = find_first_zero_bit(ep->ob_window_map, ep->num_ob_windows);
> + if (free_win >= ep->num_ob_windows) {
> + dev_err(&pcie->pdev->dev, "No free outbound window\n");
> + return -EINVAL;
> + }
> +
> + ret = program_ob_windows_ep(pcie, free_win, MEM_WINDOW_TYPE,
> + phys_addr, pci_addr, func_no, size);
> + if (ret < 0) {
> + dev_err(&pcie->pdev->dev, "Failed to program IB window\n");
> + return ret;
> + }
> +
> + set_bit(free_win, ep->ob_window_map);
> + ep->outbound_addr[free_win] = phys_addr;
> +
> + return 0;
> +}
> +
> +static void mobiveil_pcie_ep_clear_bar(struct pci_epc *epc, u8 func_no,
> + struct pci_epf_bar *epf_bar) {
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + enum pci_barno bar = epf_bar->barno;
> +
> + if (bar < ep->bar_num) {
> + __mobiveil_pcie_ep_reset_bar(pcie,
> + func_no * ep->bar_num +
> + bar);
> +
> + mobiveil_pcie_disable_ib_win_ep(pcie, func_no, bar);
> + }
> +}
> +
> +static int mobiveil_pcie_ep_set_bar(struct pci_epc *epc, u8 func_no,
> + struct pci_epf_bar *epf_bar) {
> + int ret;
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + enum pci_barno bar = epf_bar->barno;
> + size_t size = epf_bar->size;
> +
> + if (bar < ep->bar_num) {
> + ret = mobiveil_pcie_ep_inbound_atu(ep, func_no, bar,
> + epf_bar->phys_addr);
> + if (ret)
> + return ret;
> +
> + csr_writel(pcie, func_no * ep->bar_num + bar,
> + GPEX_BAR_SELECT);
> + csr_writel(pcie, lower_32_bits(~(size - 1)),
> + GPEX_BAR_SIZE_LDW);
> + csr_writel(pcie, upper_32_bits(~(size - 1)),
> + GPEX_BAR_SIZE_UDW);
> + }
> +
> + return 0;
> +}
> +
> +static int mobiveil_pcie_find_index(struct mobiveil_pcie_ep *ep,
> + phys_addr_t addr,
> + u32 *atu_index) {
> + u32 index;
> +
> + for (index = 0; index < ep->num_ob_windows; index++) {
> + if (ep->outbound_addr[index] != addr)
> + continue;
> + *atu_index = index;
> + return 0;
> + }
> +
> + return -EINVAL;
> +}
> +
> +static void mobiveil_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no,
> + phys_addr_t addr) {
> + int ret;
> + u32 atu_index;
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> +
> + ret = mobiveil_pcie_find_index(ep, addr, &atu_index);
> + if (ret < 0)
> + return;
> +
> + mobiveil_pcie_disable_ob_win(pcie, atu_index);
> + clear_bit(atu_index, ep->ob_window_map); }
> +
> +static int mobiveil_pcie_ep_map_addr(struct pci_epc *epc, u8 func_no,
> + phys_addr_t addr,
> + u64 pci_addr, size_t size) {
> + int ret;
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> +
> + ret = mobiveil_pcie_ep_outbound_atu(ep, addr, pci_addr, func_no, size);
> + if (ret) {
> + dev_err(&pcie->pdev->dev, "Failed to enable address\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int mobiveil_pcie_ep_get_msi(struct pci_epc *epc, u8 func_no)
> +{
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + u32 val, reg;
> +
> + if (!ep->msi_cap)
> + return -EINVAL;
> +
> + reg = ep->msi_cap + PCI_MSI_FLAGS;
> + val = csr_readw(pcie, reg);
> + if (!(val & PCI_MSI_FLAGS_ENABLE))
> + return -EINVAL;
> +
> + val = (val & PCI_MSI_FLAGS_QSIZE) >> 4;
> +
> + return val;
> +}
> +
> +static int mobiveil_pcie_ep_set_msi(struct pci_epc *epc,
> + u8 func_no, u8 interrupts) {
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + u32 val, reg;
> +
> + if (!ep->msi_cap)
> + return -EINVAL;
> +
> + reg = ep->msi_cap + PCI_MSI_FLAGS;
> + val = csr_readw(pcie, reg);
> + val &= ~PCI_MSI_FLAGS_QMASK;
> + val |= (interrupts << 1) & PCI_MSI_FLAGS_QMASK;
> + csr_writew(pcie, val, reg);
> +
> + return 0;
> +}
> +
> +static int mobiveil_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no)
> +{
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + u32 val, reg;
> +
> + if (!ep->msix_cap)
> + return -EINVAL;
> +
> + reg = ep->msix_cap + PCI_MSIX_FLAGS;
> + val = csr_readw(pcie, reg);
> + if (!(val & PCI_MSIX_FLAGS_ENABLE))
> + return -EINVAL;
> +
> + val &= PCI_MSIX_FLAGS_QSIZE;
> +
> + return val;
> +}
> +
> +static int mobiveil_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no,
> + u16 interrupts) {
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + u32 val, reg;
> +
> + if (!ep->msix_cap)
> + return -EINVAL;
> +
> + reg = ep->msix_cap + PCI_MSIX_FLAGS;
> + val = csr_readw(pcie, reg);
> + val &= ~PCI_MSIX_FLAGS_QSIZE;
> + val |= interrupts;
> + csr_writew(pcie, val, reg);
> +
> + return 0;
> +}
> +
> +static int mobiveil_pcie_ep_raise_irq(struct pci_epc *epc, u8 func_no,
> + enum pci_epc_irq_type type,
> + u16 interrupt_num) {
> + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc);
> +
> + if (!ep->ops->raise_irq)
> + return -EINVAL;
> +
> + return ep->ops->raise_irq(ep, func_no, type, interrupt_num); }
> +
> +static const struct pci_epc_ops epc_ops = {
> + .write_header = mobiveil_pcie_ep_write_header,
> + .set_bar = mobiveil_pcie_ep_set_bar,
> + .clear_bar = mobiveil_pcie_ep_clear_bar,
> + .map_addr = mobiveil_pcie_ep_map_addr,
> + .unmap_addr = mobiveil_pcie_ep_unmap_addr,
> + .set_msi = mobiveil_pcie_ep_set_msi,
> + .get_msi = mobiveil_pcie_ep_get_msi,
> + .set_msix = mobiveil_pcie_ep_set_msix,
> + .get_msix = mobiveil_pcie_ep_get_msix,
> + .raise_irq = mobiveil_pcie_ep_raise_irq,
> +};
> +
> +int mobiveil_pcie_ep_raise_legacy_irq(struct mobiveil_pcie_ep *ep, u8
> +func_no) {
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> +
> + dev_err(&pcie->pdev->dev, "EP cannot trigger legacy IRQs\n");
> +
> + return -EINVAL;
> +}
> +
> +int mobiveil_pcie_ep_raise_msi_irq(struct mobiveil_pcie_ep *ep, u8 func_no,
> + u8 interrupt_num) {
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + struct pci_epc *epc = ep->epc;
> + u16 msg_ctrl, msg_data;
> + u32 msg_addr_lower, msg_addr_upper, reg;
> + u64 msg_addr;
> + u32 func_num;
> + bool has_upper;
> + int ret;
> +
> + if (!ep->msi_cap)
> + return -EINVAL;
> +
> + /*
> + * In order ot get the PF's MSI capability register value from config
> + * space we need to set the PF number to the PAB_CTRL register.
> + */
> + func_num = csr_readl(pcie, PAB_CTRL);
> + func_num &= ~(FUNC_SEL_MASK << FUNC_SEL_SHIFT);
> + func_num |= (func_no & FUNC_SEL_MASK) << FUNC_SEL_SHIFT;
> + csr_writel(pcie, func_num, PAB_CTRL);
> +
> + reg = ep->msi_cap + PCI_MSI_FLAGS;
> + msg_ctrl = csr_readw(pcie, reg);
> + has_upper = !!(msg_ctrl & PCI_MSI_FLAGS_64BIT);
> + reg = ep->msi_cap + PCI_MSI_ADDRESS_LO;
> + msg_addr_lower = csr_readl(pcie, reg);
> + if (has_upper) {
> + reg = ep->msi_cap + PCI_MSI_ADDRESS_HI;
> + msg_addr_upper = csr_readl(pcie, reg);
> + reg = ep->msi_cap + PCI_MSI_DATA_64;
> + msg_data = csr_readw(pcie, reg);
> + } else {
> + msg_addr_upper = 0;
> + reg = ep->msi_cap + PCI_MSI_DATA_32;
> + msg_data = csr_readw(pcie, reg);
> + }
> + msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower;
> +
> + /*
> + * clear the FUNC_SEL_SHIFT bits when access other registers except
> + * config space register.
> + */
> + func_num = csr_readl(pcie, PAB_CTRL);
> + func_num &= ~(FUNC_SEL_MASK << FUNC_SEL_SHIFT);
> + csr_writel(pcie, func_num, PAB_CTRL);
> +
> + ret = mobiveil_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys,
> + msg_addr, epc->mem->page_size);
> + if (ret)
> + return ret;
> +
> + writel(msg_data | (interrupt_num - 1), ep->msi_mem);
> +
> + mobiveil_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys);
> +
> + return 0;
> +}
> +
> +int mobiveil_pcie_ep_raise_msix_irq(struct mobiveil_pcie_ep *ep, u8 func_no,
> + u16 interrupt_num) {
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + struct pci_epc *epc = ep->epc;
> + u32 msg_addr_upper, msg_addr_lower;
> + u32 msg_data;
> + u64 msg_addr;
> + u32 func_num;
> + int ret;
> +
> + /*
> + * In order ot get the PF's MSI capability register value from config
> + * space we need to set the PF number to the PAB_CTRL register.
> + */
> + func_num = csr_readl(pcie, PAB_CTRL);
> + func_num &= ~(FUNC_SEL_MASK << FUNC_SEL_SHIFT);
> + func_num |= (func_no & FUNC_SEL_MASK) << FUNC_SEL_SHIFT;
> + csr_writel(pcie, func_num, PAB_CTRL);
> +
> + msg_addr_lower = csr_readl(pcie, PAB_MSIX_TABLE_PBA_ACCESS +
> + PCI_MSIX_ENTRY_LOWER_ADDR +
> + (interrupt_num - 1) * PCI_MSIX_ENTRY_SIZE);
> + msg_addr_upper = csr_readl(pcie, PAB_MSIX_TABLE_PBA_ACCESS +
> + PCI_MSIX_ENTRY_UPPER_ADDR +
> + (interrupt_num - 1) * PCI_MSIX_ENTRY_SIZE);
> + msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower;
> + msg_data = csr_readl(pcie, PAB_MSIX_TABLE_PBA_ACCESS +
> + PCI_MSIX_ENTRY_DATA +
> + (interrupt_num - 1) * PCI_MSIX_ENTRY_SIZE);
> +
> + /*
> + * clear the FUNC_SEL_SHIFT bits when access other registers except
> + * config space registers.
> + */
> + func_num = csr_readl(pcie, PAB_CTRL);
> + func_num &= ~(FUNC_SEL_MASK << FUNC_SEL_SHIFT);
> + csr_writel(pcie, func_num, PAB_CTRL);
> +
> + ret = mobiveil_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys,
> + msg_addr, epc->mem->page_size);
> + if (ret)
> + return ret;
> +
> + writel(msg_data, ep->msi_mem);
> +
> + mobiveil_pcie_ep_unmap_addr(epc, func_no, ep->msi_mem_phys);
> +
> + return 0;
> +}
> +
> +void mobiveil_pcie_ep_exit(struct mobiveil_pcie_ep *ep) {
> + struct pci_epc *epc = ep->epc;
> +
> + pci_epc_mem_free_addr(epc, ep->msi_mem_phys, ep->msi_mem,
> + epc->mem->page_size);
> +
> + pci_epc_mem_exit(epc);
> +}
> +
> +int mobiveil_pcie_ep_init(struct mobiveil_pcie_ep *ep) {
> + int ret;
> + void *addr;
> + struct pci_epc *epc;
> + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep);
> + struct device *dev = &pcie->pdev->dev;
> + struct device_node *np = dev->of_node;
> +
> + if (!pcie->csr_axi_slave_base) {
> + dev_err(dev, "csr_base is not populated\n");
> + return -EINVAL;
> + }
> +
> + ret = of_property_read_u32(np, "num-ob-windows", &ep->num_ob_windows);
> + if (ret < 0) {
> + dev_err(dev, "Unable to read *num-ob-windows* property\n");
> + return ret;
> + }
> +
> + if (ep->num_ob_windows > MAX_IATU_OUT) {
> + dev_err(dev, "Invalid *num-ob-windows*\n");
> + return -EINVAL;
> + }
> + ep->ob_window_map = devm_kcalloc(dev,
> + BITS_TO_LONGS(ep->num_ob_windows),
> + sizeof(long),
> + GFP_KERNEL);
> + if (!ep->ob_window_map)
> + return -ENOMEM;
> +
> + addr = devm_kcalloc(dev, ep->num_ob_windows, sizeof(phys_addr_t),
> + GFP_KERNEL);
> + if (!addr)
> + return -ENOMEM;
> + ep->outbound_addr = addr;
> +
> + mobiveil_pcie_enable_bridge_pio(pcie);
> + mobiveil_pcie_enable_engine_apio(pcie);
> + mobiveil_pcie_enable_engine_ppio(pcie);
> + mobiveil_pcie_enable_msi_ep(pcie);
> +
> + epc = devm_pci_epc_create(dev, &epc_ops);
> + if (IS_ERR(epc)) {
> + dev_err(dev, "Failed to create epc device\n");
> + return PTR_ERR(epc);
> + }
> +
> + ep->epc = epc;
> + epc_set_drvdata(epc, ep);
> +
> + ep->msi_cap = mobiveil_pcie_ep_find_capability(pcie,
> + PCI_CAP_ID_MSI);
> +
> + ep->msix_cap = mobiveil_pcie_ep_find_capability(pcie,
> +
> + PCI_CAP_ID_MSIX);
> +
> + if (ep->ops->ep_init)
> + ep->ops->ep_init(ep);
> +
> + epc->max_functions = ep->pf_num;
> +
> + ret = __pci_epc_mem_init(epc, ep->phys_base, ep->addr_size,
> + ep->page_size);
> + if (ret < 0) {
> + dev_err(dev, "Failed to initialize address space\n");
> + return ret;
> + }
> +
> + ep->msi_mem = pci_epc_mem_alloc_addr(epc, &ep->msi_mem_phys,
> + epc->mem->page_size);
> + if (!ep->msi_mem) {
> + dev_err(dev, "Failed to reserve memory for MSI/MSI-X\n");
> + return -ENOMEM;
> + }
> +
> + return 0;
> +}
> diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil.c
> b/drivers/pci/controller/mobiveil/pcie-mobiveil.c
> index 49d471b..d1fdfed 100644
> --- a/drivers/pci/controller/mobiveil/pcie-mobiveil.c
> +++ b/drivers/pci/controller/mobiveil/pcie-mobiveil.c
> @@ -210,6 +210,57 @@ void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, u64 cpu_addr,
> pcie->ob_wins_configured++;
> }
>
> +int program_ob_windows_ep(struct mobiveil_pcie *pcie, int win_num, int type,
> + u64 phys, u64 bus_addr, u8 func, u64 size) {
> + u32 val;
> + u32 size_h, size_l;
> +
> + if (size & (size - 1))
> + size = 1 << (1 + ilog2(size));
> +
> + size_h = upper_32_bits(~(size - 1));
> + size_l = lower_32_bits(~(size - 1));
> +
> + val = csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num));
> + val &= ~(WIN_TYPE_MASK << WIN_TYPE_SHIFT |
> + WIN_SIZE_MASK << WIN_SIZE_SHIFT);
> + val |= 1 << WIN_ENABLE_SHIFT | type << WIN_TYPE_SHIFT |
> + (size_l & (WIN_SIZE_MASK << WIN_SIZE_SHIFT));
> + csr_writel(pcie, val, PAB_AXI_AMAP_CTRL(win_num));
> +
> + csr_writel(pcie, func, PAB_AXI_AMAP_PCI_HDR_PARAM(win_num));
> + csr_writel(pcie, lower_32_bits(phys), PAB_AXI_AMAP_AXI_WIN(win_num));
> + csr_writel(pcie, upper_32_bits(phys),
> + PAB_EXT_AXI_AMAP_AXI_WIN(win_num));
> + csr_writel(pcie, lower_32_bits(bus_addr),
> + PAB_AXI_AMAP_PEX_WIN_L(win_num));
> + csr_writel(pcie, upper_32_bits(bus_addr),
> + PAB_AXI_AMAP_PEX_WIN_H(win_num));
> + csr_writel(pcie, size_h, PAB_EXT_AXI_AMAP_SIZE(win_num));
> +
> + return 0;
> +}
> +
> +void program_ib_windows_ep(struct mobiveil_pcie *pcie, u8 func_no,
> + int bar, u64 phys) {
> + csr_writel(pcie, upper_32_bits(phys),
> + PAB_EXT_PEX_BAR_AMAP(func_no, bar));
> + csr_writel(pcie, lower_32_bits(phys) | PEX_BAR_AMAP_EN,
> + PAB_PEX_BAR_AMAP(func_no, bar)); }
> +
> +void mobiveil_pcie_disable_ib_win_ep(struct mobiveil_pcie *pcie,
> + u8 func_no, u8 bar) {
> + u32 val;
> +
> + val = csr_readl(pcie, PAB_PEX_BAR_AMAP(func_no, bar));
> + val &= ~(1 << 0);
> + csr_writel(pcie, val, PAB_PEX_BAR_AMAP(func_no, bar)); }
> +
> int mobiveil_bringup_link(struct mobiveil_pcie *pcie) {
> int retries;
> @@ -227,20 +278,57 @@ int mobiveil_bringup_link(struct mobiveil_pcie *pcie)
> return -ETIMEDOUT;
> }
>
> -void mobiveil_pcie_disable_ib_win(struct mobiveil_pcie *pci, int
> win_num)
> +void mobiveil_pcie_disable_ib_win(struct mobiveil_pcie *pcie, int
> +win_num)
> {
> u32 val;
>
> - val = csr_readl(pci, PAB_PEX_AMAP_CTRL(win_num));
> + val = csr_readl(pcie, PAB_PEX_AMAP_CTRL(win_num));
> val &= ~(1 << AMAP_CTRL_EN_SHIFT);
> - csr_writel(pci, val, PAB_PEX_AMAP_CTRL(win_num));
> + csr_writel(pcie, val, PAB_PEX_AMAP_CTRL(win_num));
> }
>
> -void mobiveil_pcie_disable_ob_win(struct mobiveil_pcie *pci, int
> win_num)
> +void mobiveil_pcie_disable_ob_win(struct mobiveil_pcie *pcie, int
> +win_num)
> {
> u32 val;
>
> - val = csr_readl(pci, PAB_AXI_AMAP_CTRL(win_num));
> + val = csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num));
> val &= ~(1 << WIN_ENABLE_SHIFT);
> - csr_writel(pci, val, PAB_AXI_AMAP_CTRL(win_num));
> + csr_writel(pcie, val, PAB_AXI_AMAP_CTRL(win_num)); }
> +
> +void mobiveil_pcie_enable_bridge_pio(struct mobiveil_pcie *pcie) {
> + u32 val;
> +
> + val = csr_readl(pcie, PAB_CTRL);
> + val |= 1 << AMBA_PIO_ENABLE_SHIFT;
> + val |= 1 << PEX_PIO_ENABLE_SHIFT;
> + csr_writel(pcie, val, PAB_CTRL); }
> +
> +void mobiveil_pcie_enable_engine_apio(struct mobiveil_pcie *pcie) {
> + u32 val;
> +
> + val = csr_readl(pcie, PAB_AXI_PIO_CTRL);
> + val |= APIO_EN_MASK;
> + csr_writel(pcie, val, PAB_AXI_PIO_CTRL); }
> +
> +void mobiveil_pcie_enable_engine_ppio(struct mobiveil_pcie *pcie) {
> + u32 val;
> +
> + val = csr_readl(pcie, PAB_PEX_PIO_CTRL);
> + val |= 1 << PIO_ENABLE_SHIFT;
> + csr_writel(pcie, val, PAB_PEX_PIO_CTRL); }
> +
> +void mobiveil_pcie_enable_msi_ep(struct mobiveil_pcie *pcie) {
> + u32 val;
> +
> + val = csr_readl(pcie, PAB_INTP_AMBA_MISC_ENB);
> + val |= 1 << 0;
> + csr_writel(pcie, val, PAB_INTP_AMBA_MISC_ENB);
> }
> diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil.h
> b/drivers/pci/controller/mobiveil/pcie-mobiveil.h
> index f0e2e4a..275c68f 100644
> --- a/drivers/pci/controller/mobiveil/pcie-mobiveil.h
> +++ b/drivers/pci/controller/mobiveil/pcie-mobiveil.h
> @@ -15,6 +15,10 @@
> #include <linux/msi.h>
> #include "../../pci.h"
>
> +#include <linux/pci-epc.h>
> +#include <linux/pci-epf.h>
> +
> +#define MAX_IATU_OUT 256
> /* register offsets and bit positions */
>
> /*
> @@ -40,6 +44,9 @@
> #define PAGE_SEL_MASK 0x3f
> #define PAGE_LO_MASK 0x3ff
> #define PAGE_SEL_OFFSET_SHIFT 10
> +#define FUNC_SEL_SHIFT 19
> +#define FUNC_SEL_MASK 0x1ff
> +#define MSI_SW_CTRL_EN (1 << 29)
>
> #define PAB_ACTIVITY_STAT 0x81c
>
> @@ -100,6 +107,19 @@
> #define PAB_PEX_AMAP_PEX_WIN_L(win) PAB_REG_ADDR(0x4ba8, win)
> #define PAB_PEX_AMAP_PEX_WIN_H(win) PAB_REG_ADDR(0x4bac, win)
>
> +/* PPIO WINs EP mode */
> +#define PAB_PEX_BAR_AMAP(func, bar) (0x1ba0 + 0x20 * func + 4 * bar)
> +#define PAB_EXT_PEX_BAR_AMAP(func, bar) (0x84a0 + 0x20 * func + 4 * bar)
> +#define PEX_BAR_AMAP_EN (1 << 0)
> +
> +#define PAB_AXI_AMAP_PCI_HDR_PARAM(idx) (0x5ba0 + 0x04 * idx)
> +#define PAB_MSIX_TABLE_PBA_ACCESS 0xD000
> +
> +#define GPEX_BAR_ENABLE 0x4D4
> +#define GPEX_BAR_SIZE_LDW 0x4D8
> +#define GPEX_BAR_SIZE_UDW 0x4DC
> +#define GPEX_BAR_SELECT 0x4E0
> +
> /* starting offset of INTX bits in status register */
> #define PAB_INTX_START 5
>
> @@ -137,6 +157,7 @@
> ((off >> PAGE_SEL_OFFSET_SHIFT) & PAGE_SEL_MASK)
>
> struct mobiveil_pcie;
> +struct mobiveil_pcie_ep;
>
> struct mobiveil_msi { /* MSI information */
> struct mutex lock; /* protect bitmap variable */
> @@ -169,6 +190,29 @@ struct mobiveil_pab_ops {
> int (*host_init)(struct mobiveil_pcie *pcie); };
>
> +struct mobiveil_pcie_ep_ops {
> + void (*ep_init)(struct mobiveil_pcie_ep *ep);
> + int (*raise_irq)(struct mobiveil_pcie_ep *ep, u8 func_no,
> + enum pci_epc_irq_type type, u16 interrupt_num);
> +};
> +
> +struct mobiveil_pcie_ep {
> + struct pci_epc *epc;
> + struct mobiveil_pcie_ep_ops *ops;
> + phys_addr_t phys_base;
> + size_t addr_size;
> + size_t page_size;
> + phys_addr_t *outbound_addr;
> + unsigned long *ob_window_map;
> + u32 num_ob_windows;
> + void __iomem *msi_mem;
> + phys_addr_t msi_mem_phys;
> + u8 msi_cap; /* MSI capability offset */
> + u8 msix_cap; /* MSI-X capability offset */
> + u8 bar_num;
> + u32 pf_num;
> +};
> +
> struct mobiveil_pcie {
> struct platform_device *pdev;
> struct list_head *resources;
> @@ -181,7 +225,10 @@ struct mobiveil_pcie {
> u32 ib_wins_configured; /* configured inbound windows */
> const struct mobiveil_pab_ops *ops;
> struct root_port rp;
> + struct mobiveil_pcie_ep ep;
> };
> +#define to_mobiveil_pcie_from_ep(endpoint) \
> + container_of((endpoint), struct
> +mobiveil_pcie, ep)
>
> int mobiveil_pcie_host_probe(struct mobiveil_pcie *pcie); int
> mobiveil_host_init(struct mobiveil_pcie *pcie, bool reinit); @@ -226,4
> +273,21 @@ static inline void csr_writeb(struct mobiveil_pcie *pcie, u32 val, u32 off)
> csr_write(pcie, val, off, 0x1);
> }
>
> +void program_ib_windows_ep(struct mobiveil_pcie *pcie, u8 func_no,
> + int bar, u64 phys); int
> +program_ob_windows_ep(struct mobiveil_pcie *pcie, int win_num, int type,
> + u64 phys, u64 bus_addr, u8 func, u64 size);
> +void mobiveil_pcie_disable_ib_win_ep(struct mobiveil_pcie *pci,
> + u8 func_no, u8 bar); int
> +mobiveil_pcie_ep_init(struct mobiveil_pcie_ep *ep); int
> +mobiveil_pcie_ep_raise_legacy_irq(struct mobiveil_pcie_ep *ep, u8
> +func_no); int mobiveil_pcie_ep_raise_msi_irq(struct mobiveil_pcie_ep *ep, u8 func_no,
> + u8 interrupt_num); int
> +mobiveil_pcie_ep_raise_msix_irq(struct mobiveil_pcie_ep *ep, u8 func_no,
> + u16 interrupt_num); void
> +mobiveil_pcie_ep_reset_bar(struct mobiveil_pcie *pci, enum pci_barno
> +bar); void mobiveil_pcie_enable_bridge_pio(struct mobiveil_pcie
> +*pci); void mobiveil_pcie_enable_engine_apio(struct mobiveil_pcie
> +*pci); void mobiveil_pcie_enable_engine_ppio(struct mobiveil_pcie
> +*pci); void mobiveil_pcie_enable_msi_ep(struct mobiveil_pcie *pci);
> #endif /* _PCIE_MOBIVEIL_H */
> --
> 1.7.1
>