diff options
author | Dong Aisheng <aisheng.dong@nxp.com> | 2021-11-30 14:59:55 +0800 |
---|---|---|
committer | Dong Aisheng <aisheng.dong@nxp.com> | 2021-11-30 14:59:55 +0800 |
commit | 23f57891e4b576454d08b9bb07d9989639974901 (patch) | |
tree | add47196a9ae260d5e0c058372e3a0c5f7b9ba72 | |
parent | 841bf366158fe120f871c8e919251deabf8c4f05 (diff) | |
parent | ee6792c9d17cb07d45ed292b915cf977842458ed (diff) |
Merge remote-tracking branch 'origin/pcie/mobiveil' into pcie/next
* origin/pcie/mobiveil: (10 commits)
PCI: mobiveil: Complete initialization of host even if no PCIe link
PCI: mobiveil: Add link up condition check
PCI: mobiveil: Add workaround for unsupported request error
PCI: mobiveil: Add PCIe Gen4 EP driver for NXP Layerscape SoCs
dt-bindings: Add DT binding for PCIE GEN4 EP of the layerscape
...
-rw-r--r-- | Documentation/devicetree/bindings/pci/layerscape-pcie-gen4.txt | 28 | ||||
-rw-r--r-- | MAINTAINERS | 3 | ||||
-rw-r--r-- | drivers/pci/controller/mobiveil/Kconfig | 19 | ||||
-rw-r--r-- | drivers/pci/controller/mobiveil/Makefile | 2 | ||||
-rw-r--r-- | drivers/pci/controller/mobiveil/pcie-layerscape-gen4-ep.c | 169 | ||||
-rw-r--r-- | drivers/pci/controller/mobiveil/pcie-layerscape-gen4.c | 56 | ||||
-rw-r--r-- | drivers/pci/controller/mobiveil/pcie-mobiveil-ep.c | 572 | ||||
-rw-r--r-- | drivers/pci/controller/mobiveil/pcie-mobiveil-host.c | 33 | ||||
-rw-r--r-- | drivers/pci/controller/mobiveil/pcie-mobiveil.c | 101 | ||||
-rw-r--r-- | drivers/pci/controller/mobiveil/pcie-mobiveil.h | 79 |
10 files changed, 1046 insertions, 16 deletions
diff --git a/Documentation/devicetree/bindings/pci/layerscape-pcie-gen4.txt b/Documentation/devicetree/bindings/pci/layerscape-pcie-gen4.txt index b40fb5d15d3d..414a86c9c6af 100644 --- a/Documentation/devicetree/bindings/pci/layerscape-pcie-gen4.txt +++ b/Documentation/devicetree/bindings/pci/layerscape-pcie-gen4.txt @@ -3,6 +3,8 @@ NXP Layerscape PCIe Gen4 controller This PCIe controller is based on the Mobiveil PCIe IP and thus inherits all the common properties defined in mobiveil-pcie.txt. +HOST MODE +========= Required properties: - compatible: should contain the platform identifier such as: "fsl,lx2160a-pcie" @@ -23,7 +25,20 @@ Required properties: - msi-parent : See the generic MSI binding described in Documentation/devicetree/bindings/interrupt-controller/msi.txt. -Example: +DEVICE MODE +========= +Required properties: +- compatible: should contain the platform identifier such as: + "fsl,lx2160a-pcie-ep" +- reg: base addresses and lengths of the PCIe controller register blocks. + "regs": PCIe controller registers. + "addr_space" EP device CPU address. +- apio-wins: number of requested apio outbound windows. + +Optional Property: +- max-functions: Maximum number of functions that can be configured (default 1). + +RC Example: pcie@3400000 { compatible = "fsl,lx2160a-pcie"; @@ -50,3 +65,14 @@ Example: <0000 0 0 3 &gic 0 0 GIC_SPI 111 IRQ_TYPE_LEVEL_HIGH>, <0000 0 0 4 &gic 0 0 GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>; }; + +EP Example: + + pcie_ep@3400000 { + compatible = "fsl,lx2160a-pcie-ep"; + reg = <0x00 0x03400000 0x0 0x00100000 + 0x80 0x00000000 0x8 0x00000000>; + reg-names = "regs", "addr_space"; + apio-wins = <8>; + status = "disabled"; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 3b79fd441dde..42406e8f8ae9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14380,6 +14380,7 @@ F: include/uapi/linux/switchtec_ioctl.h PCI DRIVER FOR MOBIVEIL PCIE IP M: Karthikeyan Mitran <m.karthikeyan@mobiveil.co.in> M: Hou Zhiqiang <Zhiqiang.Hou@nxp.com> +M: Xiaowei Bao <xiaowei.bao@nxp.com> L: linux-pci@vger.kernel.org S: Supported F: Documentation/devicetree/bindings/pci/mobiveil-pcie.txt @@ -14402,11 +14403,13 @@ F: drivers/pci/controller/pci-tegra.c PCI DRIVER FOR NXP LAYERSCAPE GEN4 CONTROLLER M: Hou Zhiqiang <Zhiqiang.Hou@nxp.com> +M: Xiaowei Bao <xiaowei.bao@nxp.com> L: linux-pci@vger.kernel.org L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: Documentation/devicetree/bindings/pci/layerscape-pcie-gen4.txt F: drivers/pci/controller/mobiveil/pcie-layerscape-gen4.c +F: drivers/pci/controller/mobiveil/pcie-layerscape-gen4-ep.c PCI DRIVER FOR RENESAS R-CAR M: Marek Vasut <marek.vasut+renesas@gmail.com> diff --git a/drivers/pci/controller/mobiveil/Kconfig b/drivers/pci/controller/mobiveil/Kconfig index e4643fb94e78..490d73692f85 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 @@ -23,11 +28,21 @@ config PCIE_MOBIVEIL_PLAT for address translation and it is a PCIe Gen4 IP. config PCIE_LAYERSCAPE_GEN4 - bool "Freescale Layerscape PCIe Gen4 controller" + bool "Freescale Layerscape PCIe Gen4 controller in RC mode" depends on ARCH_LAYERSCAPE || COMPILE_TEST depends on PCI_MSI_IRQ_DOMAIN select PCIE_MOBIVEIL_HOST help - Say Y here if you want PCIe Gen4 controller support on + Say Y here if you want PCIe Gen4 controller as host support on + Layerscape SoCs. + +config PCIE_LAYERSCAPE_GEN4_EP + bool "Freescale Layerscape 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 as endpoint support on Layerscape SoCs. endmenu diff --git a/drivers/pci/controller/mobiveil/Makefile b/drivers/pci/controller/mobiveil/Makefile index 99d879de32d6..6f548566dde8 100644 --- a/drivers/pci/controller/mobiveil/Makefile +++ b/drivers/pci/controller/mobiveil/Makefile @@ -1,5 +1,7 @@ # 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_PCIE_LAYERSCAPE_GEN4) += pcie-layerscape-gen4.o +obj-$(CONFIG_PCIE_LAYERSCAPE_GEN4_EP) += pcie-layerscape-gen4-ep.o diff --git a/drivers/pci/controller/mobiveil/pcie-layerscape-gen4-ep.c b/drivers/pci/controller/mobiveil/pcie-layerscape-gen4-ep.c new file mode 100644 index 000000000000..976c4a3f8efc --- /dev/null +++ b/drivers/pci/controller/mobiveil/pcie-layerscape-gen4-ep.c @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PCIe controller EP driver for Freescale Layerscape SoCs + * + * Copyright 2019 NXP + * + * Author: Xiaowei Bao <xiaowei.bao@nxp.com> + */ + +#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" + +#define PCIE_LX2_BAR_NUM 4 + +#define to_ls_pcie_g4_ep(x) dev_get_drvdata((x)->dev) + +struct ls_pcie_g4_ep { + struct mobiveil_pcie *mv_pci; +}; + +static const struct of_device_id ls_pcie_g4_ep_of_match[] = { + { .compatible = "fsl,lx2160a-pcie-ep",}, + { }, +}; + +static const struct pci_epc_features ls_pcie_g4_epc_features = { + .linkup_notifier = false, + .msi_capable = true, + .msix_capable = true, + .reserved_bar = (1 << BAR_4) | (1 << BAR_5), +}; + +static const struct pci_epc_features* +ls_pcie_g4_ep_get_features(struct mobiveil_pcie_ep *ep) +{ + return &ls_pcie_g4_epc_features; +} + +static void ls_pcie_g4_ep_init(struct mobiveil_pcie_ep *ep) +{ + struct mobiveil_pcie *mv_pci = to_mobiveil_pcie_from_ep(ep); + int win_idx; + u8 bar; + u32 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 = mobiveil_csr_readl(mv_pci, CFG_UNCORRECTABLE_ERROR_SEVERITY); + val |= 1 << UNSUPPORTED_REQUEST_ERROR_SHIFT; + mobiveil_csr_writel(mv_pci, val, CFG_UNCORRECTABLE_ERROR_SEVERITY); + + ep->bar_num = PCIE_LX2_BAR_NUM; + + for (bar = BAR_0; bar < ep->epc->max_functions * ep->bar_num; bar++) + mobiveil_pcie_ep_reset_bar(mv_pci, bar); + + for (win_idx = 0; win_idx < ep->apio_wins; win_idx++) + mobiveil_pcie_disable_ob_win(mv_pci, win_idx); +} + +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 const struct mobiveil_pcie_ep_ops pcie_ep_ops = { + .ep_init = ls_pcie_g4_ep_init, + .raise_irq = ls_pcie_g4_ep_raise_irq, + .get_features = ls_pcie_g4_ep_get_features, +}; + +static int __init ls_pcie_gen4_add_pcie_ep(struct ls_pcie_g4_ep *ls_ep, + struct platform_device *pdev) +{ + struct mobiveil_pcie *mv_pci = ls_ep->mv_pci; + struct device *dev = &pdev->dev; + struct mobiveil_pcie_ep *ep; + struct resource *res; + int ret; + + 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 = mobiveil_pcie_ep_init(ep); + if (ret) { + dev_err(dev, "failed to initialize layerscape 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_ep; + struct resource *res; + int ret; + + ls_ep = devm_kzalloc(dev, sizeof(*ls_ep), GFP_KERNEL); + if (!ls_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_ep->mv_pci = mv_pci; + + platform_set_drvdata(pdev, ls_ep); + + ret = ls_pcie_gen4_add_pcie_ep(ls_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); diff --git a/drivers/pci/controller/mobiveil/pcie-layerscape-gen4.c b/drivers/pci/controller/mobiveil/pcie-layerscape-gen4.c index 306950272fd6..d36864481557 100644 --- a/drivers/pci/controller/mobiveil/pcie-layerscape-gen4.c +++ b/drivers/pci/controller/mobiveil/pcie-layerscape-gen4.c @@ -22,8 +22,12 @@ #include "pcie-mobiveil.h" +#define REV_1_0 0x10 + /* LUT and PF control registers */ #define PCIE_LUT_OFF 0x80000 +#define PCIE_LUT_GCR 0x28 +#define PCIE_LUT_GCR_RRE BIT(0) #define PCIE_PF_OFF 0xc0000 #define PCIE_PF_INT_STAT 0x18 #define PF_INT_STAT_PABRST BIT(31) @@ -40,6 +44,7 @@ struct ls_pcie_g4 { struct mobiveil_pcie pci; struct delayed_work dwork; int irq; + u8 rev; }; static inline u32 ls_pcie_g4_pf_readl(struct ls_pcie_g4 *pcie, u32 off) @@ -53,6 +58,30 @@ static inline void ls_pcie_g4_pf_writel(struct ls_pcie_g4 *pcie, iowrite32(val, pcie->pci.csr_axi_slave_base + PCIE_PF_OFF + off); } +static void workaround_A011451(struct ls_pcie_g4 *pcie) +{ + struct mobiveil_pcie *mv_pci = &pcie->pci; + u32 val; + + /* Set ACK latency timeout */ + val = mobiveil_csr_readl(mv_pci, GPEX_ACK_REPLAY_TO); + val &= ~(ACK_LAT_TO_VAL_MASK << ACK_LAT_TO_VAL_SHIFT); + val |= (4 << ACK_LAT_TO_VAL_SHIFT); + mobiveil_csr_writel(mv_pci, val, GPEX_ACK_REPLAY_TO); +} + +static int ls_pcie_g4_host_init(struct mobiveil_pcie *pci) +{ + struct ls_pcie_g4 *pcie = to_ls_pcie_g4(pci); + + pcie->rev = mobiveil_csr_readb(pci, PCI_REVISION_ID); + + if (pcie->rev == REV_1_0) + workaround_A011451(pcie); + + return 0; +} + static int ls_pcie_g4_link_up(struct mobiveil_pcie *pci) { struct ls_pcie_g4 *pcie = to_ls_pcie_g4(pci); @@ -190,12 +219,39 @@ static void ls_pcie_g4_reset(struct work_struct *work) ls_pcie_g4_enable_interrupt(pcie); } +static int ls_pcie_g4_read_other_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + struct mobiveil_pcie *pci = bus->sysdata; + struct ls_pcie_g4 *pcie = to_ls_pcie_g4(pci); + u32 reg; + int ret; + + if (pcie->rev == REV_1_0 && where == PCI_VENDOR_ID) { + reg = ls_pcie_g4_lut_readl(pcie, PCIE_LUT_GCR); + reg &= ~PCIE_LUT_GCR_RRE; + ls_pcie_g4_lut_writel(pcie, PCIE_LUT_GCR, reg); + } + + ret = pci_generic_config_read(bus, devfn, where, size, val); + + if (pcie->rev == REV_1_0 && where == PCI_VENDOR_ID) { + reg = ls_pcie_g4_lut_readl(pcie, PCIE_LUT_GCR); + reg |= PCIE_LUT_GCR_RRE; + ls_pcie_g4_lut_writel(pcie, PCIE_LUT_GCR, reg); + } + + return ret; +} + static struct mobiveil_rp_ops ls_pcie_g4_rp_ops = { .interrupt_init = ls_pcie_g4_interrupt_init, + .read_other_conf = ls_pcie_g4_read_other_conf, }; static const struct mobiveil_pab_ops ls_pcie_g4_pab_ops = { .link_up = ls_pcie_g4_link_up, + .host_init = ls_pcie_g4_host_init, }; static int __init ls_pcie_g4_probe(struct platform_device *pdev) 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 000000000000..7d2cee508e18 --- /dev/null +++ b/drivers/pci/controller/mobiveil/pcie-mobiveil-ep.c @@ -0,0 +1,572 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Mobiveil PCIe Endpoint controller driver + * + * Copyright 2019 NXP + * Author: Xiaowei Bao <xiaowei.bao@nxp.com> + */ + +#include <linux/of.h> +#include <linux/pci-epc.h> +#include <linux/pci-epf.h> +#include <linux/platform_device.h> +#include "pcie-mobiveil.h" + +static void mobiveil_pcie_ep_func_select(struct mobiveil_pcie *pcie, u8 func_no) +{ + u32 func_num; + + /* + * select to access the config space of func_no by setting func_no + * to FUNC_SEL_SHIFT bit of PAB_CTRL register. + */ + func_num = mobiveil_csr_readl(pcie, PAB_CTRL); + func_num &= ~(FUNC_SEL_MASK << FUNC_SEL_SHIFT); + func_num |= (func_no & FUNC_SEL_MASK) << FUNC_SEL_SHIFT; + mobiveil_csr_writel(pcie, func_num, PAB_CTRL); +} + +static void mobiveil_pcie_ep_func_deselect(struct mobiveil_pcie *pcie) +{ + u32 func_num; + + /* + * clear the FUNC_SEL_SHIFT bits when access other registers except + * config space register. + */ + func_num = mobiveil_csr_readl(pcie, PAB_CTRL); + func_num &= ~(FUNC_SEL_MASK << FUNC_SEL_SHIFT); + mobiveil_csr_writel(pcie, func_num, PAB_CTRL); +} + +static void __mobiveil_pcie_ep_reset_bar(struct mobiveil_pcie *pcie, u8 bar) +{ + mobiveil_csr_writel(pcie, bar, GPEX_BAR_SELECT); + mobiveil_csr_writel(pcie, 0, GPEX_BAR_SIZE_LDW); + mobiveil_csr_writel(pcie, 0, GPEX_BAR_SIZE_UDW); +} + +void mobiveil_pcie_ep_reset_bar(struct mobiveil_pcie *pcie, u8 bar) +{ + __mobiveil_pcie_ep_reset_bar(pcie, bar); +} + +static u8 __mobiveil_pcie_ep_find_next_cap(struct mobiveil_pcie *pcie, + u8 func_no, u8 cap_ptr, u8 cap) +{ + u8 cap_id, next_cap_ptr; + u16 reg; + + if (!cap_ptr) + return 0; + + mobiveil_pcie_ep_func_select(pcie, func_no); + + reg = mobiveil_csr_readw(pcie, cap_ptr); + cap_id = (reg & 0x00ff); + + mobiveil_pcie_ep_func_deselect(pcie); + + if (cap_id > PCI_CAP_ID_MAX) + return 0; + + if (cap_id == cap) + return cap_ptr; + + next_cap_ptr = (reg & 0xff00) >> 8; + return __mobiveil_pcie_ep_find_next_cap(pcie, func_no, + next_cap_ptr, cap); +} + +static u8 mobiveil_pcie_ep_find_capability(struct mobiveil_pcie_ep *ep, + u8 func_no, u8 cap) +{ + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep); + u8 next_cap_ptr; + u16 reg; + + mobiveil_pcie_ep_func_select(pcie, func_no); + + reg = mobiveil_csr_readw(pcie, PCI_CAPABILITY_LIST); + next_cap_ptr = (reg & 0x00ff); + + mobiveil_pcie_ep_func_deselect(pcie); + + return __mobiveil_pcie_ep_find_next_cap(pcie, func_no, + 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); + + mobiveil_pcie_ep_func_select(pcie, func_no); + + mobiveil_csr_writew(pcie, hdr->vendorid, PCI_VENDOR_ID); + mobiveil_csr_writew(pcie, hdr->deviceid, PCI_DEVICE_ID); + mobiveil_csr_writeb(pcie, hdr->revid, PCI_REVISION_ID); + mobiveil_csr_writeb(pcie, hdr->progif_code, PCI_CLASS_PROG); + mobiveil_csr_writew(pcie, hdr->subclass_code | hdr->baseclass_code << 8, + PCI_CLASS_DEVICE); + mobiveil_csr_writeb(pcie, hdr->cache_line_size, PCI_CACHE_LINE_SIZE); + mobiveil_csr_writew(pcie, hdr->subsys_vendor_id, PCI_SUBSYSTEM_VENDOR_ID); + mobiveil_csr_writew(pcie, hdr->subsys_id, PCI_SUBSYSTEM_ID); + mobiveil_csr_writeb(pcie, hdr->interrupt_pin, PCI_INTERRUPT_PIN); + + mobiveil_pcie_ep_func_deselect(pcie); + + return 0; +} + +static void mobiveil_pcie_ep_inbound_win(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); +} + +static int mobiveil_pcie_ep_outbound_win(struct mobiveil_pcie_ep *ep, + phys_addr_t phys_addr, + u64 pci_addr, u8 func_no, + size_t size) +{ + u32 free_win; + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep); + + free_win = find_first_zero_bit(ep->apio_wins_map, ep->apio_wins); + if (free_win >= ep->apio_wins) { + dev_err(&pcie->pdev->dev, "No free outbound window\n"); + return -EINVAL; + } + + program_ob_windows_ep(pcie, func_no, free_win, phys_addr, + pci_addr, MEM_WINDOW_TYPE, size); + + set_bit(free_win, ep->apio_wins_map); + ep->apio_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) +{ + 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) { + mobiveil_pcie_ep_inbound_win(ep, func_no, bar, + epf_bar->phys_addr); + + mobiveil_csr_writel(pcie, func_no * ep->bar_num + bar, + GPEX_BAR_SELECT); + mobiveil_csr_writel(pcie, lower_32_bits(~(size - 1)), + GPEX_BAR_SIZE_LDW); + mobiveil_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->apio_wins; index++) { + if (ep->apio_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->apio_wins_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_win(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; + u8 msi_cap; + + msi_cap = mobiveil_pcie_ep_find_capability(ep, func_no, + PCI_CAP_ID_MSI); + if (!msi_cap) + return -EINVAL; + + mobiveil_pcie_ep_func_select(pcie, func_no); + + reg = msi_cap + PCI_MSI_FLAGS; + val = mobiveil_csr_readw(pcie, reg); + + mobiveil_pcie_ep_func_deselect(pcie); + + 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; + u8 msi_cap; + + msi_cap = mobiveil_pcie_ep_find_capability(ep, func_no, + PCI_CAP_ID_MSI); + if (!msi_cap) + return -EINVAL; + + mobiveil_pcie_ep_func_select(pcie, func_no); + + reg = msi_cap + PCI_MSI_FLAGS; + val = mobiveil_csr_readw(pcie, reg); + val &= ~PCI_MSI_FLAGS_QMASK; + val |= (interrupts << 1) & PCI_MSI_FLAGS_QMASK; + mobiveil_csr_writew(pcie, val, reg); + + mobiveil_pcie_ep_func_deselect(pcie); + + 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; + u8 msix_cap; + + msix_cap = mobiveil_pcie_ep_find_capability(ep, func_no, + PCI_CAP_ID_MSIX); + if (!msix_cap) + return -EINVAL; + + mobiveil_pcie_ep_func_select(pcie, func_no); + + reg = msix_cap + PCI_MSIX_FLAGS; + val = mobiveil_csr_readw(pcie, reg); + + mobiveil_pcie_ep_func_deselect(pcie); + + 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, enum pci_barno bir, + u32 offset) +{ + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc); + struct mobiveil_pcie *pcie = to_mobiveil_pcie_from_ep(ep); + u32 val, reg; + u8 msix_cap; + + msix_cap = mobiveil_pcie_ep_find_capability(ep, func_no, + PCI_CAP_ID_MSIX); + if (!msix_cap) + return -EINVAL; + + mobiveil_pcie_ep_func_select(pcie, func_no); + + reg = msix_cap + PCI_MSIX_FLAGS; + val = mobiveil_csr_readw(pcie, reg); + val &= ~PCI_MSIX_FLAGS_QSIZE; + val |= interrupts; + mobiveil_csr_writew(pcie, val, reg); + + mobiveil_pcie_ep_func_deselect(pcie); + + 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_features* +mobiveil_pcie_ep_get_features(struct pci_epc *epc, u8 func_no) +{ + struct mobiveil_pcie_ep *ep = epc_get_drvdata(epc); + + if (!ep->ops->get_features) + return NULL; + + return ep->ops->get_features(ep); +} + +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, + .get_features = mobiveil_pcie_ep_get_features, +}; + +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; + bool has_upper; + int ret; + u8 msi_cap; + + msi_cap = mobiveil_pcie_ep_find_capability(ep, func_no, + PCI_CAP_ID_MSI); + if (!msi_cap) + return -EINVAL; + + mobiveil_pcie_ep_func_select(pcie, func_no); + + reg = msi_cap + PCI_MSI_FLAGS; + msg_ctrl = mobiveil_csr_readw(pcie, reg); + has_upper = !!(msg_ctrl & PCI_MSI_FLAGS_64BIT); + reg = msi_cap + PCI_MSI_ADDRESS_LO; + msg_addr_lower = mobiveil_csr_readl(pcie, reg); + if (has_upper) { + reg = msi_cap + PCI_MSI_ADDRESS_HI; + msg_addr_upper = mobiveil_csr_readl(pcie, reg); + reg = msi_cap + PCI_MSI_DATA_64; + msg_data = mobiveil_csr_readw(pcie, reg); + } else { + msg_addr_upper = 0; + reg = msi_cap + PCI_MSI_DATA_32; + msg_data = mobiveil_csr_readw(pcie, reg); + } + msg_addr = ((u64) msg_addr_upper) << 32 | msg_addr_lower; + + mobiveil_pcie_ep_func_deselect(pcie); + + ret = mobiveil_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys, + msg_addr, epc->mem->window.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; + u8 msix_cap; + int ret; + + msix_cap = mobiveil_pcie_ep_find_capability(ep, func_no, + PCI_CAP_ID_MSIX); + if (!msix_cap) + return -EINVAL; + + mobiveil_pcie_ep_func_deselect(pcie); + + msg_addr_lower = mobiveil_csr_readl(pcie, PAB_MSIX_TABLE_PBA_ACCESS + + PCI_MSIX_ENTRY_LOWER_ADDR + + (interrupt_num - 1) * + PCI_MSIX_ENTRY_SIZE); + msg_addr_upper = mobiveil_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 = mobiveil_csr_readl(pcie, PAB_MSIX_TABLE_PBA_ACCESS + + PCI_MSIX_ENTRY_DATA + + (interrupt_num - 1) * + PCI_MSIX_ENTRY_SIZE); + + ret = mobiveil_pcie_ep_map_addr(epc, func_no, ep->msi_mem_phys, + msg_addr, epc->mem->window.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->window.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, "apio-wins", &ep->apio_wins); + if (ret < 0) { + dev_err(dev, "Unable to read apio-wins property\n"); + return ret; + } + + if (ep->apio_wins > MAX_IATU_OUT) { + dev_err(dev, "Invalid apio-wins\n"); + return -EINVAL; + } + ep->apio_wins_map = devm_kcalloc(dev, + BITS_TO_LONGS(ep->apio_wins), + sizeof(long), + GFP_KERNEL); + if (!ep->apio_wins_map) + return -ENOMEM; + + addr = devm_kcalloc(dev, ep->apio_wins, sizeof(phys_addr_t), + GFP_KERNEL); + if (!addr) + return -ENOMEM; + + ep->apio_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); + + ret = of_property_read_u8(np, "max-functions", &epc->max_functions); + if (ret < 0) + epc->max_functions = 1; + + if (ep->ops->ep_init) + ep->ops->ep_init(ep); + + 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->window.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-host.c b/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c index f3547aa60140..1e00a3223a20 100644 --- a/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c +++ b/drivers/pci/controller/mobiveil/pcie-mobiveil-host.c @@ -29,6 +29,12 @@ static bool mobiveil_pcie_valid_device(struct pci_bus *bus, unsigned int devfn) { + struct mobiveil_pcie *pcie = bus->sysdata; + + /* If there is no link, then there is no device */ + if (!pci_is_root_bus(bus) && !mobiveil_pcie_link_up(pcie)) + return false; + /* Only one device down on each root port */ if (pci_is_root_bus(bus) && (devfn > 0)) return false; @@ -61,6 +67,12 @@ static void __iomem *mobiveil_pcie_map_bus(struct pci_bus *bus, if (pci_is_root_bus(bus)) return pcie->csr_axi_slave_base + where; + /* Make sure the Master Enable bit not cleared */ + value = mobiveil_csr_readl(pcie, PCI_COMMAND); + if (!(value & PCI_COMMAND_MASTER)) + mobiveil_csr_writel(pcie, value | PCI_COMMAND_MASTER, + PCI_COMMAND); + /* * EP config access (in Config/APIO space) * Program PEX Address base (31..16 bits) with appropriate value @@ -76,9 +88,20 @@ static void __iomem *mobiveil_pcie_map_bus(struct pci_bus *bus, return rp->config_axi_slave_base + where; } +static int mobiveil_pcie_config_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + struct mobiveil_pcie *pcie = bus->sysdata; + struct mobiveil_root_port *rp = &pcie->rp; + + if (!pci_is_root_bus(bus) && rp->ops->read_other_conf) + return rp->ops->read_other_conf(bus, devfn, where, size, val); + + return pci_generic_config_read(bus, devfn, where, size, val); +} static struct pci_ops mobiveil_pcie_ops = { .map_bus = mobiveil_pcie_map_bus, - .read = pci_generic_config_read, + .read = mobiveil_pcie_config_read, .write = pci_generic_config_write, }; @@ -298,6 +321,10 @@ int mobiveil_host_init(struct mobiveil_pcie *pcie, bool reinit) value |= (PCI_CLASS_BRIDGE_PCI << 16); mobiveil_csr_writel(pcie, value, PAB_INTP_AXI_PIO_CLASS); + /* Platform specific host init */ + if (pcie->ops->host_init) + return pcie->ops->host_init(pcie); + return 0; } @@ -584,10 +611,8 @@ int mobiveil_pcie_host_probe(struct mobiveil_pcie *pcie) bridge->ops = &mobiveil_pcie_ops; ret = mobiveil_bringup_link(pcie); - if (ret) { + if (ret) dev_info(dev, "link bring-up failed\n"); - return ret; - } return pci_host_probe(bridge); } diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil.c b/drivers/pci/controller/mobiveil/pcie-mobiveil.c index 62ecbaeb0a60..1570464360ed 100644 --- a/drivers/pci/controller/mobiveil/pcie-mobiveil.c +++ b/drivers/pci/controller/mobiveil/pcie-mobiveil.c @@ -170,18 +170,12 @@ void program_ib_windows(struct mobiveil_pcie *pcie, int win_num, /* * routine to program the outbound windows */ -void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, - u64 cpu_addr, u64 pci_addr, u32 type, u64 size) +void __program_ob_windows(struct mobiveil_pcie *pcie, u8 func_no, int win_num, + u64 cpu_addr, u64 pci_addr, u32 type, u64 size) { u32 value; u64 size64 = ~(size - 1); - if (win_num >= pcie->apio_wins) { - dev_err(&pcie->pdev->dev, - "ERROR: max outbound windows reached !\n"); - return; - } - /* * program Enable Bit to 1, Type Bit to (00) base 2, AXI Window Size Bit * to 4 KB in PAB_AXI_AMAP_CTRL register @@ -195,6 +189,7 @@ void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, mobiveil_csr_writel(pcie, upper_32_bits(size64), PAB_EXT_AXI_AMAP_SIZE(win_num)); + mobiveil_csr_writel(pcie, func_no, PAB_AXI_AMAP_PCI_HDR_PARAM(win_num)); /* * program AXI window base with appropriate value in * PAB_AXI_AMAP_AXI_WIN0 register @@ -209,10 +204,98 @@ void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, PAB_AXI_AMAP_PEX_WIN_L(win_num)); mobiveil_csr_writel(pcie, upper_32_bits(pci_addr), PAB_AXI_AMAP_PEX_WIN_H(win_num)); +} + +void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, u64 cpu_addr, + u64 pci_addr, u32 type, u64 size) +{ + if (win_num >= pcie->apio_wins) { + dev_err(&pcie->pdev->dev, + "ERROR: max outbound windows reached !\n"); + return; + } + + __program_ob_windows(pcie, 0, win_num, cpu_addr, + pci_addr, type, size); pcie->ob_wins_configured++; } +void program_ob_windows_ep(struct mobiveil_pcie *pcie, u8 func_no, int win_num, + u64 cpu_addr, u64 pci_addr, u32 type, u64 size) +{ + if (size & (size - 1)) + size = 1 << (1 + ilog2(size)); + + __program_ob_windows(pcie, func_no, win_num, cpu_addr, + pci_addr, type, size); +} + +void program_ib_windows_ep(struct mobiveil_pcie *pcie, u8 func_no, + int bar, u64 phys) +{ + mobiveil_csr_writel(pcie, upper_32_bits(phys), + PAB_EXT_PEX_BAR_AMAP(func_no, bar)); + mobiveil_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 = mobiveil_csr_readl(pcie, PAB_PEX_BAR_AMAP(func_no, bar)); + val &= ~(1 << 0); + mobiveil_csr_writel(pcie, val, PAB_PEX_BAR_AMAP(func_no, bar)); +} + +void mobiveil_pcie_disable_ob_win(struct mobiveil_pcie *pcie, int win_num) +{ + u32 val; + + val = mobiveil_csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num)); + val &= ~(1 << WIN_ENABLE_SHIFT); + mobiveil_csr_writel(pcie, val, PAB_AXI_AMAP_CTRL(win_num)); +} + +void mobiveil_pcie_enable_bridge_pio(struct mobiveil_pcie *pcie) +{ + u32 val; + + val = mobiveil_csr_readl(pcie, PAB_CTRL); + val |= 1 << AMBA_PIO_ENABLE_SHIFT; + val |= 1 << PEX_PIO_ENABLE_SHIFT; + mobiveil_csr_writel(pcie, val, PAB_CTRL); +} + +void mobiveil_pcie_enable_engine_apio(struct mobiveil_pcie *pcie) +{ + u32 val; + + val = mobiveil_csr_readl(pcie, PAB_AXI_PIO_CTRL); + val |= APIO_EN_MASK; + mobiveil_csr_writel(pcie, val, PAB_AXI_PIO_CTRL); +} + +void mobiveil_pcie_enable_engine_ppio(struct mobiveil_pcie *pcie) +{ + u32 val; + + val = mobiveil_csr_readl(pcie, PAB_PEX_PIO_CTRL); + val |= 1 << PIO_ENABLE_SHIFT; + mobiveil_csr_writel(pcie, val, PAB_PEX_PIO_CTRL); +} + +void mobiveil_pcie_enable_msi_ep(struct mobiveil_pcie *pcie) +{ + u32 val; + + val = mobiveil_csr_readl(pcie, PAB_INTP_AMBA_MISC_ENB); + val |= PAB_INTP_PAMR; + mobiveil_csr_writel(pcie, val, PAB_INTP_AMBA_MISC_ENB); +} + int mobiveil_bringup_link(struct mobiveil_pcie *pcie) { int retries; @@ -225,7 +308,7 @@ int mobiveil_bringup_link(struct mobiveil_pcie *pcie) usleep_range(LINK_WAIT_MIN, LINK_WAIT_MAX); } - dev_err(&pcie->pdev->dev, "link never came up\n"); + dev_info(&pcie->pdev->dev, "link never came up\n"); return -ETIMEDOUT; } diff --git a/drivers/pci/controller/mobiveil/pcie-mobiveil.h b/drivers/pci/controller/mobiveil/pcie-mobiveil.h index 6082b8afbc31..7b0cf21fb152 100644 --- a/drivers/pci/controller/mobiveil/pcie-mobiveil.h +++ b/drivers/pci/controller/mobiveil/pcie-mobiveil.h @@ -15,8 +15,12 @@ #include <linux/pci.h> #include <linux/irq.h> #include <linux/msi.h> +#include <linux/pci-epc.h> +#include <linux/pci-epf.h> + #include "../../pci.h" +#define MAX_IATU_OUT 256 /* register offsets and bit positions */ /* @@ -42,6 +46,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 BIT(29) #define PAB_ACTIVITY_STAT 0x81c @@ -52,6 +59,7 @@ #define PIO_ENABLE_SHIFT 0 #define PAB_INTP_AMBA_MISC_ENB 0x0b0c +#define PAB_INTP_PAMR BIT(0) #define PAB_INTP_AMBA_MISC_STAT 0x0b1c #define PAB_INTP_RESET BIT(1) #define PAB_INTP_MSI BIT(3) @@ -72,6 +80,8 @@ #define WIN_TYPE_MASK 0x3 #define WIN_SIZE_MASK 0xfffffc00 +#define PAB_AXI_AMAP_PCI_HDR_PARAM(win) PAB_EXT_REG_ADDR(0x5ba0, win) + #define PAB_EXT_AXI_AMAP_SIZE(win) PAB_EXT_REG_ADDR(0xbaf0, win) #define PAB_EXT_AXI_AMAP_AXI_WIN(win) PAB_EXT_REG_ADDR(0x80a0, win) @@ -86,6 +96,10 @@ #define PAB_AXI_AMAP_PEX_WIN_H(win) PAB_REG_ADDR(0x0bac, win) #define PAB_INTP_AXI_PIO_CLASS 0x474 +#define GPEX_ACK_REPLAY_TO 0x438 +#define ACK_LAT_TO_VAL_MASK 0x1fff +#define ACK_LAT_TO_VAL_SHIFT 0 + #define PAB_PEX_AMAP_CTRL(win) PAB_REG_ADDR(0x4ba0, win) #define AMAP_CTRL_EN_SHIFT 0 #define AMAP_CTRL_TYPE_SHIFT 1 @@ -97,6 +111,22 @@ #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 BIT(0) + +#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 + +#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 @@ -143,9 +173,12 @@ struct mobiveil_msi { /* MSI information */ }; struct mobiveil_pcie; +struct mobiveil_pcie_ep; struct mobiveil_rp_ops { int (*interrupt_init)(struct mobiveil_pcie *pcie); + int (*read_other_conf)(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val); }; struct mobiveil_root_port { @@ -161,6 +194,29 @@ struct mobiveil_root_port { struct mobiveil_pab_ops { int (*link_up)(struct mobiveil_pcie *pcie); + 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); + const struct pci_epc_features* (*get_features) + (struct mobiveil_pcie_ep *ep); +}; + +struct mobiveil_pcie_ep { + struct pci_epc *epc; + const struct mobiveil_pcie_ep_ops *ops; + phys_addr_t phys_base; + size_t addr_size; + size_t page_size; + phys_addr_t *apio_addr; + unsigned long *apio_wins_map; + u32 apio_wins; + void __iomem *msi_mem; + phys_addr_t msi_mem_phys; + u8 bar_num; }; struct mobiveil_pcie { @@ -174,8 +230,12 @@ struct mobiveil_pcie { int ib_wins_configured; /* configured inbound windows */ const struct mobiveil_pab_ops *ops; struct mobiveil_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); bool mobiveil_pcie_link_up(struct mobiveil_pcie *pcie); @@ -222,4 +282,23 @@ static inline void mobiveil_csr_writeb(struct mobiveil_pcie *pcie, u8 val, mobiveil_csr_write(pcie, val, off, 0x1); } +void program_ib_windows_ep(struct mobiveil_pcie *pcie, u8 func_no, + int bar, u64 phys); +void program_ob_windows_ep(struct mobiveil_pcie *pcie, u8 func_num, int win_num, + u64 cpu_addr, u64 pci_addr, u32 type, u64 size); +void mobiveil_pcie_disable_ib_win_ep(struct mobiveil_pcie *pci, + u8 func_no, u8 bar); +void mobiveil_pcie_disable_ob_win(struct mobiveil_pcie *pcie, int win_num); +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, u8 bar); +u8 mobiveil_pcie_ep_get_bar_num(struct mobiveil_pcie_ep *ep, u8 func_no); +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 */ |