diff --git a/Documentation/devicetree/bindings/pci/phytium,pe2201-pcie-ep.yaml b/Documentation/devicetree/bindings/pci/phytium,pe2201-pcie-ep.yaml new file mode 100644 index 0000000000000..49ec3e7ae7767 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/phytium,pe2201-pcie-ep.yaml @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/phytium,pe2201-pcie-ep.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Phytium PCIe endpoint controller + +maintainers: + - Li Tongfeng + +allOf: + - $ref: "pci-ep.yaml#" + +properties: + compatible: + const: phytium,pe2201-pcie-ep + + reg: + maxItems: 2 + + reg-names: + items: + - const: reg + - const: mem + +required: + - compatible + - reg + - reg-names + +examples: + - | + ep0: ep@0x31000000 { + compatible = "phytium,pe2201-pcie-ep"; + interrupts = <0x00000000 0x0000000a 0x00000004 0x00000000 0x0000000b 0x00000004>; + reg = <0x00000000 0x31000000 0x00000000 0x00010000 0x00000011 0x00000000 0x00000001 0x00000000 0x00000000 0x31100000 0x00000000 0x00001000>; + reg-names = "reg", "mem", "hpb"; + max-outbound-regions = <0x00000003>; + max-functions = [02]; + status = "disabled"; + }; diff --git a/MAINTAINERS b/MAINTAINERS index c7ebccca27be8..bd3fe775d56c0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17547,6 +17547,7 @@ F: Documentation/devicetree/bindings/gpio/phytium,gpio.yaml F: Documentation/devicetree/bindings/usb/phytium,usb2-2.0.yaml F: Documentation/devicetree/bindings/usb/phytium,usb2.yaml F: Documentation/devicetree/bindings/usb/phytium.role-sw.yaml +F: Documentation/devicetree/bindings/pci/phytium,pe2201-pcie-ep.yaml F: drivers/usb/phytium/* F: drivers/usb/phytium/phytium_usb_v2* F: drivers/usb/typec/role-switch-phytium.c diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index 398259dd942f2..e0ce86d257ec3 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -217,6 +217,7 @@ config PCIE_MT7621 config PCIE_PHYTIUM_EP tristate "Phytium PCIe endpoint controller" + depends on ARCH_PHYTIUM depends on OF depends on PCI_ENDPOINT help diff --git a/drivers/pci/controller/pcie-phytium-ep.c b/drivers/pci/controller/pcie-phytium-ep.c index 7a9d6a4ab39cd..a00d8cd9db0d9 100644 --- a/drivers/pci/controller/pcie-phytium-ep.c +++ b/drivers/pci/controller/pcie-phytium-ep.c @@ -19,17 +19,23 @@ #include "pcie-phytium-ep.h" #include "pcie-phytium-register.h" -#define PHYTIUM_PCIE_RP_DRIVER_VERSION "1.1.1" +#define PHYTIUM_PCIE_EP_DRIVER_VERSION "1.1.1" #define PHYTIUM_PCIE_EP_IRQ_PCI_ADDR_NONE 0x0 #define PHYTIUM_PCIE_EP_IRQ_PCI_ADDR_LEGACY 0x1 +#define PHYTIUM_PCIE_EP_ADDR_LO_MASK 0xffffffff +#define PHYTIUM_PCIE_EP_DMA_CONTROL_VALUE 0x18017e1 +#define PHYTIUM_PCIE_EP_PCIE_INTERFACE_ID 0x0 +#define PHYTIUM_PCIE_EP_AXI_MASTER_INTERFACE_ID 0x4 +#define PHYTIUM_PCIE_EP_DMA_SHARE_ACCESS 0x3 + static int phytium_pcie_ep_write_header(struct pci_epc *epc, unsigned char fn, u8 vfn, struct pci_epf_header *hdr) { struct phytium_pcie_ep *priv = epc_get_drvdata(epc); u16 tmp = 0; - + fn++; phytium_pcie_writew(priv, fn, PHYTIUM_PCI_VENDOR_ID, hdr->vendorid); phytium_pcie_writew(priv, fn, PHYTIUM_PCI_DEVICE_ID, hdr->deviceid); phytium_pcie_writeb(priv, fn, PHYTIUM_PCI_REVISION_ID, hdr->revid); @@ -53,71 +59,75 @@ static int phytium_pcie_ep_write_header(struct pci_epc *epc, unsigned char fn, u } static int phytium_pcie_ep_set_bar(struct pci_epc *epc, u8 fn, u8 vfn, - struct pci_epf_bar *epf_bar) + struct pci_epf_bar *epf_bar) { struct phytium_pcie_ep *priv = epc_get_drvdata(epc); - u64 sz = 0, sz_mask, atr_size; + u64 base, sz_mask, atr_size, sz = 0; int flags = epf_bar->flags; - u32 setting, src_addr0, src_addr1, trsl_addr0, trsl_addr1, trsl_param; enum pci_barno barno = epf_bar->barno; struct pci_epc_mem *mem = epc->mem; + u32 setting, src_addr0, trsl_param; + fn++; if ((flags & PCI_BASE_ADDRESS_MEM_TYPE_64) && (barno & 1)) { dev_err(&epc->dev, "bar %d do not support mem64\n", barno); return -EINVAL; } - if (barno & 1) { - dev_err(&epc->dev, "not support bar 1/3/5\n"); - return -EINVAL; - } - dev_dbg(epc->dev.parent, "set bar%d mapping address 0x%pa size 0x%lx\n", + if (barno == 0 || barno == 3 || barno == 5) + return 0; + + dev_dbg(&epc->dev, "set bar%d mapping address 0x%pa size 0x%zx\n", barno, &(epf_bar->phys_addr), epf_bar->size); if ((flags & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) { setting = BAR_IO_TYPE; - sz = max_t(size_t, epf_bar->size, BAR_IO_MIN_APERTURE); - sz = 1 << fls64(sz - 1); - sz_mask = ~(sz - 1); + sz_mask = ~(epf_bar->size / 2 - 1); setting |= sz_mask; trsl_param = TRSL_ID_IO; } else { setting = BAR_MEM_TYPE; - sz = max_t(size_t, epf_bar->size, BAR_MEM_MIN_APERTURE); - sz = 1 << fls64(sz - 1); - sz_mask = ~(sz - 1); - setting |= lower_32_bits(sz_mask); if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) setting |= BAR_MEM_64BIT; if (flags & PCI_BASE_ADDRESS_MEM_PREFETCH) setting |= BAR_MEM_PREFETCHABLE; - + sz_mask = ~(epf_bar->size / 2 - 1); + setting |= lower_32_bits(sz_mask); trsl_param = TRSL_ID_MASTER; } + sz = max_t(size_t, epf_bar->size / 2, BAR_MEM_MIN_APERTURE); + sz = 1 << fls64(sz - 1); + sz = ALIGN(sz, mem->window.page_size); + atr_size = fls64(sz - 1) - 1; + + base = 0xE00 + fn * 0x40 + (barno - 1) * 0x8; + phytium_hpb_writel(priv, base, upper_32_bits(epf_bar->phys_addr)); + phytium_hpb_writel(priv, (base + 0x4), (lower_32_bits(epf_bar->phys_addr) | (atr_size))); + phytium_pcie_writel(priv, fn, PHYTIUM_PCI_BAR(barno), setting); if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) - phytium_pcie_writel(priv, fn, PHYTIUM_PCI_BAR(barno + 1), + phytium_pcie_writel(priv, fn, PHYTIUM_PCI_BAR((barno + 1)), upper_32_bits(sz_mask)); - dev_dbg(epc->dev.parent, "set bar%d mapping address 0x%pa size 0x%llx 0x%x\n", - barno, &(epf_bar->phys_addr), sz, lower_32_bits(epf_bar->phys_addr)); - sz = ALIGN(sz, mem->window.page_size); - atr_size = fls64(sz - 1) - 1; + + if (barno == 2) { + phytium_pcie_writel(priv, fn, PHYTIUM_PCI_BAR((barno + 1)), 0); + return 0; + } src_addr0 = ATR_IMPL | ((atr_size & ATR_SIZE_MASK) << ATR_SIZE_SHIFT); - src_addr1 = 0; - trsl_addr0 = (lower_32_bits(epf_bar->phys_addr) & TRSL_ADDR_32_12_MASK); - trsl_addr1 = upper_32_bits(epf_bar->phys_addr); phytium_pcie_writel(priv, fn, PHYTIUM_PCI_WIN0_SRC_ADDR0(barno), src_addr0); phytium_pcie_writel(priv, fn, PHYTIUM_PCI_WIN0_SRC_ADDR1(barno), - src_addr1); + 0x0); + phytium_pcie_writel(priv, fn, PHYTIUM_PCI_WIN0_TRSL_ADDR0(barno), - trsl_addr0); + (barno == 1) ? lower_32_bits(epf_bar->phys_addr) : 0); phytium_pcie_writel(priv, fn, PHYTIUM_PCI_WIN0_TRSL_ADDR1(barno), - trsl_addr1); + (barno == 1) ? upper_32_bits(epf_bar->phys_addr) : barno << 10); + phytium_pcie_writel(priv, fn, PHYTIUM_PCI_WIN0_TRSL_PARAM(barno), trsl_param); @@ -130,7 +140,7 @@ static void phytium_pcie_ep_clear_bar(struct pci_epc *epc, u8 fn, u8 vfn, struct phytium_pcie_ep *priv = epc_get_drvdata(epc); int flags = epf_bar->flags; enum pci_barno barno = epf_bar->barno; - + fn++; phytium_pcie_writel(priv, fn, PHYTIUM_PCI_BAR(barno), 0); if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) phytium_pcie_writel(priv, fn, PHYTIUM_PCI_BAR(barno + 1), 0); @@ -151,7 +161,7 @@ static int phytium_pcie_ep_map_addr(struct pci_epc *epc, u8 fn, u8 vfn, u64 sz = 0; u32 r; struct pci_epc_mem *mem = epc->mem; - + fn++; r = find_first_zero_bit(&priv->ob_region_map, sizeof(priv->ob_region_map) * BITS_PER_LONG); if (r >= priv->max_regions) { @@ -192,7 +202,7 @@ static void phytium_pcie_ep_unmap_addr(struct pci_epc *epc, u8 fn, u8 vfn, { struct phytium_pcie_ep *priv = epc_get_drvdata(epc); u32 r; - + fn++; for (r = 0; r < priv->max_regions; r++) if (priv->ob_addr[r] == addr) break; @@ -216,7 +226,7 @@ static int phytium_pcie_ep_set_msi(struct pci_epc *epc, u8 fn, u8 vfn, u8 mmc) { struct phytium_pcie_ep *priv = epc_get_drvdata(epc); u16 flags = 0; - + fn++; flags = (mmc & MSI_NUM_MASK) << MSI_NUM_SHIFT; flags &= ~MSI_MASK_SUPPORT; phytium_pcie_writew(priv, fn, PHYTIUM_PCI_INTERRUPT_PIN, flags); @@ -229,7 +239,7 @@ static int phytium_pcie_ep_get_msi(struct pci_epc *epc, u8 fn, u8 vfn) struct phytium_pcie_ep *priv = epc_get_drvdata(epc); u16 flags, mme; u32 cap = PHYTIUM_PCI_CF_MSI_BASE; - + fn++; flags = phytium_pcie_readw(priv, fn, cap + PCI_MSI_FLAGS); if (!(flags & PCI_MSI_FLAGS_ENABLE)) return -EINVAL; @@ -247,7 +257,7 @@ static int phytium_pcie_ep_send_msi_irq(struct phytium_pcie_ep *priv, u8 fn, u8 msi_count; u64 pci_addr, pci_addr_mask = IRQ_MAPPING_SIZE - 1; u32 src_addr0, src_addr1, trsl_addr0, trsl_addr1, trsl_param, atr_size; - + fn++; flags = phytium_pcie_readw(priv, fn, cap + PCI_MSI_FLAGS); if (!(flags & PCI_MSI_FLAGS_ENABLE)) return -EINVAL; @@ -302,7 +312,7 @@ static int phytium_pcie_ep_raise_irq(struct pci_epc *epc, u8 fn, u8 vfn, u16 interrupt_num) { struct phytium_pcie_ep *priv = epc_get_drvdata(epc); - + fn++; switch (type) { case PCI_EPC_IRQ_MSI: return phytium_pcie_ep_send_msi_irq(priv, fn, interrupt_num); @@ -326,6 +336,68 @@ static int phytium_pcie_ep_start(struct pci_epc *epc) return 0; } +static int phytium_pcie_ep_start_dma(struct pci_epc *epc, u8 func_no, u64 cpu_addr, u64 pci_addr, + size_t size, u8 mode) +{ + u32 value; + struct phytium_pcie_ep *priv = epc_get_drvdata(epc); + + func_no++; + dev_dbg(&epc->dev, "%s func_no %d cpu_addr %llu pci_addr %llu size %zu mode %d\n", + __func__, func_no, cpu_addr, pci_addr, size, mode); + + u64 base = 0xE00 + func_no * 0x40; + + phytium_hpb_writel(priv, base, upper_32_bits(cpu_addr)); + phytium_hpb_writel(priv, (base + 0x4), + (lower_32_bits(cpu_addr) & (~0x3f)) | (fls64(size - 1))); + cpu_addr = 0x40000000000 | (cpu_addr & 0x3f); + + phytium_pcie_writel(priv, func_no, DMA_SHARE_ACCESS(mode), + PHYTIUM_PCIE_EP_DMA_SHARE_ACCESS); + if (mode == DMA_READ_ENGINE) { + phytium_pcie_writel(priv, func_no, DMA_SRCPARAM(mode), + PHYTIUM_PCIE_EP_PCIE_INTERFACE_ID); + phytium_pcie_writel(priv, func_no, DMA_DSTPARAM(mode), + PHYTIUM_PCIE_EP_AXI_MASTER_INTERFACE_ID); + value = pci_addr & PHYTIUM_PCIE_EP_ADDR_LO_MASK; + phytium_pcie_writel(priv, func_no, DMA_SRCADDR_LO(mode), value); + value = (pci_addr >> 32); + phytium_pcie_writel(priv, func_no, DMA_SRCADDR_UP(mode), value); + + value = cpu_addr & PHYTIUM_PCIE_EP_ADDR_LO_MASK; + phytium_pcie_writel(priv, func_no, DMA_DESTADDR_LO(mode), value); + value = ((cpu_addr >> 32) | 0x400); + phytium_pcie_writel(priv, func_no, DMA_DESTADDR_UP(mode), value); + } else { + phytium_pcie_writel(priv, func_no, DMA_SRCPARAM(mode), + PHYTIUM_PCIE_EP_AXI_MASTER_INTERFACE_ID); + phytium_pcie_writel(priv, func_no, DMA_DSTPARAM(mode), + PHYTIUM_PCIE_EP_PCIE_INTERFACE_ID); + value = cpu_addr & PHYTIUM_PCIE_EP_ADDR_LO_MASK; + phytium_pcie_writel(priv, func_no, DMA_SRCADDR_LO(mode), value); + value = ((cpu_addr >> 32) | 0x400); + phytium_pcie_writel(priv, func_no, DMA_SRCADDR_UP(mode), value); + + value = pci_addr & PHYTIUM_PCIE_EP_ADDR_LO_MASK; + phytium_pcie_writel(priv, func_no, DMA_DESTADDR_LO(mode), value); + value = (pci_addr >> 32); + phytium_pcie_writel(priv, func_no, DMA_DESTADDR_UP(mode), value); + } + phytium_pcie_writel(priv, func_no, DMA_LENGTH(mode), size); + phytium_pcie_writel(priv, func_no, DMA_CONTROL(mode), PHYTIUM_PCIE_EP_DMA_CONTROL_VALUE); + + return 0; +} + +static int phytium_pcie_ep_dma_status(struct pci_epc *epc, u8 func_no, u8 mode) +{ + struct phytium_pcie_ep *priv = epc_get_drvdata(epc); + + func_no++; + return phytium_pcie_readl(priv, func_no, DMA_STATUS(mode)); +} + static const struct pci_epc_ops phytium_pcie_epc_ops = { .write_header = phytium_pcie_ep_write_header, .set_bar = phytium_pcie_ep_set_bar, @@ -336,10 +408,10 @@ static const struct pci_epc_ops phytium_pcie_epc_ops = { .get_msi = phytium_pcie_ep_get_msi, .raise_irq = phytium_pcie_ep_raise_irq, .start = phytium_pcie_ep_start, + .start_dma = phytium_pcie_ep_start_dma, + .dma_status = phytium_pcie_ep_dma_status, }; - - static int phytium_pcie_ep_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -348,11 +420,33 @@ static int phytium_pcie_ep_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; struct pci_epc *epc; int ret = 0, value; + const char *compatible; + u32 hpb_c0_pref_base_limit; + u32 hpb_c0_pref_base_limit_up32; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; + compatible = of_get_property(np, "compatible", NULL); + if (!compatible) { + dev_err(dev, "Compatible property not found\n"); + return -EINVAL; + } + + if (strcmp(compatible, "phytium,pd2008-pcie-ep") == 0) { + hpb_c0_pref_base_limit = PHYTIUM_PD2008_HPB_C0_PREF_BASE_LIMIT; + hpb_c0_pref_base_limit_up32 = + PHYTIUM_PD2008_HPB_C0_PREF_BASE_LIMIT_UP32; + } else if (strcmp(compatible, "phytium,pe2201-pcie-ep") == 0) { + hpb_c0_pref_base_limit = PHYTIUM_PE2201_HPB_C0_PREF_BASE_LIMIT; + hpb_c0_pref_base_limit_up32 = + PHYTIUM_PE2201_HPB_C0_PREF_BASE_LIMIT_UP32; + } else { + dev_err(dev, "Unsupported chip model\n"); + return -ENODEV; + } + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "reg"); priv->reg_base = devm_ioremap_resource(dev, res); if (IS_ERR(priv->reg_base)) { @@ -397,6 +491,8 @@ static int phytium_pcie_ep_probe(struct platform_device *pdev) priv->epc = epc; epc_set_drvdata(epc, priv); + priv->pdev = pdev; + if (of_property_read_u8(np, "max-functions", &epc->max_functions) < 0) epc->max_functions = 1; dev_info(dev, "%s epc->max_functions %d\n", __func__, epc->max_functions); @@ -424,13 +520,13 @@ static int phytium_pcie_ep_probe(struct platform_device *pdev) & C0_PREF_BASE_MASK) << C0_PREF_BASE_SHIFT; value |= (((lower_32_bits(priv->mem_res->end) >> C0_PREF_VALUE_SHIFT) & C0_PREF_LIMIT_MASK) << C0_PREF_LIMIT_SHIFT); - phytium_hpb_writel(priv, PHYTIUM_HPB_C0_PREF_BASE_LIMIT, value); + phytium_hpb_writel(priv, hpb_c0_pref_base_limit, value); value = ((upper_32_bits(priv->mem_res->start) >> C0_PREF_UP32_VALUE_SHIFT) & C0_PREF_BASE_UP32_MASK) << C0_PREF_BASE_UP32_SHIFT; value |= (((upper_32_bits(priv->mem_res->end) >> C0_PREF_UP32_VALUE_SHIFT) & C0_PREF_LIMIT_UP32_MASK) << C0_PREF_LIMIT_UP32_SHIFT); - phytium_hpb_writel(priv, PHYTIUM_HPB_C0_PREF_BASE_LIMIT_UP32, value); + phytium_hpb_writel(priv, hpb_c0_pref_base_limit_up32, value); dev_dbg(dev, "exit %s successful\n", __func__); return 0; @@ -453,6 +549,7 @@ static int phytium_pcie_ep_remove(struct platform_device *pdev) static const struct of_device_id phytium_pcie_ep_of_match[] = { { .compatible = "phytium,pd2008-pcie-ep" }, + { .compatible = "phytium,pe2201-pcie-ep" }, { }, }; @@ -468,6 +565,6 @@ MODULE_DEVICE_TABLE(of, phytium_pcie_ep_of_match); module_platform_driver(phytium_pcie_ep_driver); MODULE_LICENSE("GPL"); -MODULE_VERSION(PHYTIUM_PCIE_RP_DRIVER_VERSION); +MODULE_VERSION(PHYTIUM_PCIE_EP_DRIVER_VERSION); MODULE_AUTHOR("Yang Xun "); MODULE_DESCRIPTION("Phytium PCIe Controller Endpoint driver"); diff --git a/drivers/pci/controller/pcie-phytium-ep.h b/drivers/pci/controller/pcie-phytium-ep.h index 1c38181fc19d9..bb5f67b57c086 100644 --- a/drivers/pci/controller/pcie-phytium-ep.h +++ b/drivers/pci/controller/pcie-phytium-ep.h @@ -23,6 +23,8 @@ struct phytium_pcie_ep { unsigned long irq_pci_addr; u8 irq_pci_fn; struct pci_epc *epc; + + struct platform_device *pdev; }; static inline void diff --git a/drivers/pci/controller/pcie-phytium-register.h b/drivers/pci/controller/pcie-phytium-register.h index 458df0014504e..5f98a93e4b8ab 100644 --- a/drivers/pci/controller/pcie-phytium-register.h +++ b/drivers/pci/controller/pcie-phytium-register.h @@ -63,13 +63,16 @@ #define PHYTIUM_PCI_CF_MSI_BASE 0x10e0 #define PHYTIUM_PCI_CF_MSI_CONTROL 0x10e2 -#define PHYTIUM_HPB_C0_PREF_BASE_LIMIT 0xa30 +#define PHYTIUM_PD2008_HPB_C0_PREF_BASE_LIMIT 0xa30 +#define PHYTIUM_PE2201_HPB_C0_PREF_BASE_LIMIT 0xa40 #define C0_PREF_LIMIT_MASK 0xfff #define C0_PREF_LIMIT_SHIFT 20 #define C0_PREF_BASE_MASK 0xfff #define C0_PREF_BASE_SHIFT 4 #define C0_PREF_VALUE_SHIFT 20 #define PHYTIUM_HPB_C0_PREF_BASE_LIMIT_UP32 0xa34 +#define PHYTIUM_PD2008_HPB_C0_PREF_BASE_LIMIT_UP32 0xa34 +#define PHYTIUM_PE2201_HPB_C0_PREF_BASE_LIMIT_UP32 0xa44 #define C0_PREF_LIMIT_UP32_MASK 0xff #define C0_PREF_LIMIT_UP32_SHIFT 8 #define C0_PREF_BASE_UP32_MASK 0xff @@ -77,4 +80,19 @@ #define C0_PREF_UP32_VALUE_SHIFT 0 #endif +#define DMA_READ_ENGINE 0 +#define DMA_WRITE_ENGINE 1 +#define DMA_ENGINE0_BASE 0x400 + +#define DMA_SHARE_ACCESS(engnum) (DMA_ENGINE0_BASE + 0x40 * engnum + 0x28) // 0x468 +#define DMA_SRCPARAM(engnum) (DMA_ENGINE0_BASE + 0x40 * engnum + 0x0) // 0x440 +#define DMA_DSTPARAM(engnum) (DMA_ENGINE0_BASE + 0x40 * engnum + 0x4) // 0x444 +#define DMA_SRCADDR_LO(engnum) (DMA_ENGINE0_BASE + 0x40 * engnum + 0x8) // 0x448 +#define DMA_SRCADDR_UP(engnum) (DMA_ENGINE0_BASE + 0x40 * engnum + 0xc) // 0x44C +#define DMA_DESTADDR_LO(engnum) (DMA_ENGINE0_BASE + 0x40 * engnum + 0x10) // 0x450 +#define DMA_DESTADDR_UP(engnum) (DMA_ENGINE0_BASE + 0x40 * engnum + 0x14) // 0x454 +#define DMA_LENGTH(engnum) (DMA_ENGINE0_BASE + 0x40 * engnum + 0x18) // 0x458 +#define DMA_CONTROL(engnum) (DMA_ENGINE0_BASE + 0x40 * engnum + 0x1c) // 0x45C +#define DMA_STATUS(engnum) (DMA_ENGINE0_BASE + 0x40 * engnum + 0x20) // 0x460 + diff --git a/drivers/pci/endpoint/pci-ep-cfs.c b/drivers/pci/endpoint/pci-ep-cfs.c index 5b64203f100fc..6d43a640206b3 100644 --- a/drivers/pci/endpoint/pci-ep-cfs.c +++ b/drivers/pci/endpoint/pci-ep-cfs.c @@ -31,6 +31,10 @@ struct pci_epc_group { struct config_group group; struct pci_epc *epc; bool start; + +#ifdef CONFIG_ARCH_PHYTIUM + unsigned long function_num_map; +#endif }; static inline struct pci_epf_group *to_pci_epf_group(struct config_item *item) @@ -204,8 +208,41 @@ static ssize_t pci_epc_start_show(struct config_item *item, char *page) CONFIGFS_ATTR(pci_epc_, start); +#ifdef CONFIG_ARCH_PHYTIUM +static ssize_t pci_epc_function_num_map_store(struct config_item *item, const char *page, + size_t len) +{ + int ret; + unsigned long function_num_map; + struct pci_epc *epc; + struct pci_epc_group *epc_group = to_pci_epc_group(item); + + epc = epc_group->epc; + + ret = kstrtoul(page, 10, &function_num_map); + if (ret) + return ret; + + epc_group->function_num_map = function_num_map; + epc->function_num_map = function_num_map; + + return len; +} + +static ssize_t pci_epc_function_num_map_show(struct config_item *item, char *page) +{ + return sprintf(page, "%ld\n", + to_pci_epc_group(item)->function_num_map); +} + +CONFIGFS_ATTR(pci_epc_, function_num_map); +#endif + static struct configfs_attribute *pci_epc_attrs[] = { &pci_epc_attr_start, +#ifdef CONFIG_ARCH_PHYTIUM + &pci_epc_attr_function_num_map, +#endif NULL, }; diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 3a82c6a613a3c..75bd16507e2cc 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -15,6 +15,9 @@ #include static struct class *pci_epc_class; +#ifdef CONFIG_ARCH_PHYTIUM +static DEFINE_SPINLOCK(epc_dma_lock); +#endif static void devm_pci_epc_release(struct device *dev, void *res) { @@ -206,6 +209,54 @@ int pci_epc_start(struct pci_epc *epc) } EXPORT_SYMBOL_GPL(pci_epc_start); +/** + * pci_epc_start_dma() - start dma with phytium-d2000 ep + * @cpu_addr: ep local mem addr + * @pci_addr: pci doamin addr,which means rc mem addr + * @size: transfer total data size bytes + * @mode: which direct the dma work,EP_TO_RC/RC_TO_EP + */ +#ifdef CONFIG_ARCH_PHYTIUM +int pci_epc_start_dma(struct pci_epc *epc, u8 func_no, u64 cpu_addr, u64 pci_addr, + size_t size, u8 mode) +{ + int ret; + unsigned long flags; + + if (IS_ERR(epc)) + return -EINVAL; + + if (!epc->ops->start_dma) + return 0; + + spin_lock_irqsave(&epc_dma_lock, flags); + ret = epc->ops->start_dma(epc, func_no, cpu_addr, pci_addr, size, mode); + spin_unlock_irqrestore(&epc_dma_lock, flags); + + return ret; +} +EXPORT_SYMBOL_GPL(pci_epc_start_dma); + +int pci_epc_dma_status(struct pci_epc *epc, u8 func_no, u8 mode) +{ + int ret; + unsigned long flags; + + if (IS_ERR(epc)) + return -EINVAL; + + if (!epc->ops->dma_status) + return 0; + + spin_lock_irqsave(&epc_dma_lock, flags); + ret = epc->ops->dma_status(epc, func_no, mode); + spin_unlock_irqrestore(&epc_dma_lock, flags); + + return ret; +} +EXPORT_SYMBOL_GPL(pci_epc_dma_status); +#endif + /** * pci_epc_raise_irq() - interrupt the host system * @epc: the EPC device which has to interrupt the host diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index 4b105878aa044..1f3890ded6ad4 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -287,7 +287,7 @@ void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events) slot_name(ctrl)); #ifdef CONFIG_ARCH_PHYTIUM if (present && link_active) - phytium_clear_ctrl_prot(ctrl->pcie->port, PHYTIUM_PCIE_HOTPLUG); + phytium_pcie_ctrl_smc_op(ctrl->pcie->port, PHYTIUM_PCIE_HOTPLUG); #endif ctrl->request_result = pciehp_enable_slot(ctrl); break; diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index b0bccc4d0da28..4aa22caec02c1 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -26,6 +26,9 @@ #include "../pci.h" #include "pciehp.h" +#ifdef CONFIG_ARCH_PHYTIUM +#include +#endif static const struct dmi_system_id inband_presence_disabled_dmi_table[] = { /* @@ -241,6 +244,18 @@ int pciehp_check_link_active(struct controller *ctrl) if (ret == PCIBIOS_DEVICE_NOT_FOUND || PCI_POSSIBLE_ERROR(lnk_status)) return -ENODEV; +#ifdef CONFIG_ARCH_PHYTIUM + if (is_ps24080()) { + /* PS24080 sometimes reports link up even when it's down */ + static u16 lnksta; + + lnksta = phytium_pcie_ctrl_smc_op(pdev, PHYTIUM_PCIE_GET_LNKSTA); + if (lnksta >= 0) + lnk_status = (lnk_status & ~PCI_EXP_LNKSTA_DLLLA) | + (lnksta & PCI_EXP_LNKSTA_DLLLA); + } +#endif + ret = !!(lnk_status & PCI_EXP_LNKSTA_DLLLA); ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index cd18c4b955882..d0f2f8f0363cd 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -48,6 +48,9 @@ EXPORT_SYMBOL_GPL(pci_power_names); int isa_dma_bridge_buggy; EXPORT_SYMBOL(isa_dma_bridge_buggy); #endif +#ifdef CONFIG_ARCH_PHYTIUM +#include +#endif int pci_pci_problems; EXPORT_SYMBOL(pci_pci_problems); @@ -5340,7 +5343,7 @@ void pci_reset_secondary_bus(struct pci_dev *dev) pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl); #ifdef CONFIG_ARCH_PHYTIUM - phytium_clear_ctrl_prot(dev, PHYTIUM_PCIE_HOTRESET); + phytium_pcie_ctrl_smc_op(dev, PHYTIUM_PCIE_HOTRESET); #endif } @@ -5349,6 +5352,55 @@ void __weak pcibios_reset_secondary_bus(struct pci_dev *dev) pci_reset_secondary_bus(dev); } +#ifdef CONFIG_ARCH_PHYTIUM +/** + * phytium_pci_bridge_secondary_bus_reset - Reset the secondary bus + * on Phytium bridges. + * @dev: Bridge device + * + * Workaround: Perform a PCIe secondary-bus reset again when + * the link is degraded on the Phytium platform. + */ +static int phytium_pci_bridge_secondary_bus_reset(struct pci_dev *dev) +{ + u16 lnksta, reset_cnt = 0; + u16 cur_speed, cur_width; + u16 next_speed, next_width; + int ret; + + pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta); + cur_speed = lnksta & PCI_EXP_LNKSTA_CLS; + cur_width = (lnksta & PCI_EXP_LNKSTA_NLW) >> + PCI_EXP_LNKSTA_NLW_SHIFT; + +retry: + reset_cnt++; + pcibios_reset_secondary_bus(dev); + ret = pci_bridge_wait_for_secondary_bus(dev, "bus reset"); + + pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta); + next_speed = lnksta & PCI_EXP_LNKSTA_CLS; + next_width = (lnksta & PCI_EXP_LNKSTA_NLW) >> + PCI_EXP_LNKSTA_NLW_SHIFT; + + /* if link degraded, allow one more retry */ + if ((next_speed < cur_speed) || (next_width < cur_width)) { + if (reset_cnt >= 2) { + pci_err(dev, "phytium: link degraded - pre Gen%u/x%u post Gen%u/x%u\n", + cur_speed, cur_width, next_speed, next_width); + goto out; + } + + pci_info(dev, "phytium: link degraded - pre Gen%u/x%u post Gen%u/x%u, reset again\n", + cur_speed, cur_width, next_speed, next_width); + goto retry; + } + +out: + return ret; +} +#endif + /** * pci_bridge_secondary_bus_reset - Reset the secondary bus on a PCI bridge. * @dev: Bridge device @@ -5358,6 +5410,19 @@ void __weak pcibios_reset_secondary_bus(struct pci_dev *dev) */ int pci_bridge_secondary_bus_reset(struct pci_dev *dev) { +#ifdef CONFIG_ARCH_PHYTIUM + if (is_pd2308()) { + int ret = 0; + + pci_save_state(dev); + pcibios_reset_secondary_bus(dev); + ret = pci_bridge_wait_for_secondary_bus(dev, "bus reset"); + pci_restore_state(dev); + return ret; + } else if (is_ps24080()) { + return phytium_pci_bridge_secondary_bus_reset(dev); + } +#endif pcibios_reset_secondary_bus(dev); return pci_bridge_wait_for_secondary_bus(dev, "bus reset"); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 8033f9056a801..c3d912cb71c01 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -877,40 +877,72 @@ static inline pci_power_t mid_pci_get_power_state(struct pci_dev *pdev) #define PHYTIUM_PCIE_HOTRESET 0 #define PHYTIUM_PCIE_HOTPLUG 1 -#define PHYTIUM_PCI_VENDOR_ID 0x1DB7 +#define PHYTIUM_PCIE_GET_LNKSTA 2 #define PHYTIUM_PCI_CTRL_ID 0x0100 -#define PHYTIUM_PCIE_CLEAR_CTRL_PROT_SMC_FUNC_ID 0xC2000020 - -static inline void phytium_clear_ctrl_prot(struct pci_dev *pdev, int op) -{ - int socket; - u8 bus = pdev->bus->number; - u8 device = PCI_SLOT(pdev->devfn); - u8 function = PCI_FUNC(pdev->devfn); - u16 vendor_id = pdev->vendor; - u16 device_id = pdev->device; - struct arm_smccc_res res; - u32 arg; - - if (vendor_id != PHYTIUM_PCI_VENDOR_ID || - device_id != PHYTIUM_PCI_CTRL_ID || - pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) - return; - - socket = dev_to_node(&pdev->dev); - if (socket < 0) { - pci_err(pdev, "Cannot find socket, stop clean pcie protection\n"); - return; - } - - arg = (socket << 16) | (bus << 8) | (device << 3) | function; - arm_smccc_smc(PHYTIUM_PCIE_CLEAR_CTRL_PROT_SMC_FUNC_ID, arg, op, 0, 0, 0, 0, 0, &res); - if (res.a0 != 0) - pci_err(pdev, "Error: Firmware call PCIE protection clear Failed: %d, sbdf: 0x%x\n", - (int)res.a0, arg); - else - pci_info(pdev, "%s : Clear pcie protection successfully\n", - op ? "HotPlug" : "HotReset"); +#define PHYTIUM_PCIE_CTRL_SMC_FUNC_ID 0xC2000020 + +#define PHYTIUM_PCIE_LINKUP 3 +#define PHYTIUM_PCIE_LINKDOWN 0 + +static inline int phytium_pcie_ctrl_smc_op(struct pci_dev *pdev, int op) +{ + int socket; + u8 bus = pdev->bus->number; + u8 device = PCI_SLOT(pdev->devfn); + u8 function = PCI_FUNC(pdev->devfn); + u16 vendor_id = pdev->vendor; + u16 device_id = pdev->device; + struct arm_smccc_res res; + const char *op_str; + u16 link_status = 0; + u32 arg; + + switch (op) { + case PHYTIUM_PCIE_HOTRESET: + op_str = "hotreset"; + break; + case PHYTIUM_PCIE_HOTPLUG: + op_str = "hotplug"; + break; + case PHYTIUM_PCIE_GET_LNKSTA: + op_str = "get-link-status"; + break; + default: + op_str = "unknown"; + break; + } + + if (vendor_id != PCI_VENDOR_ID_PHYTIUM || + device_id != PHYTIUM_PCI_CTRL_ID || + pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) + return -ENOENT; + + socket = dev_to_node(&pdev->dev); + if (socket < 0) { + pci_err(pdev, "no socket found, stop calling SMC 0x%x (%s)\n", + PHYTIUM_PCIE_CTRL_SMC_FUNC_ID, op_str); + return -ENOENT; + } + + arg = (socket << 16) | (bus << 8) | (device << 3) | function; + arm_smccc_smc(PHYTIUM_PCIE_CTRL_SMC_FUNC_ID, arg, op, 0, 0, 0, 0, 0, &res); + if (res.a0 != 0) { + pci_err(pdev, "Error: %s call SMC 0x%x failed (%d), sbdf: 0x%x\n", + op_str, PHYTIUM_PCIE_CTRL_SMC_FUNC_ID, + (int)res.a0, arg); + return -ENOENT; + } + pci_info(pdev, "%s: call SMC completed successfully\n", op_str); + + /* Update link_status bit if op == get-link-status */ + if (op == PHYTIUM_PCIE_GET_LNKSTA) { + if (res.a1 == PHYTIUM_PCIE_LINKUP) + link_status |= PCI_EXP_LNKSTA_DLLLA; + else if (res.a1 == PHYTIUM_PCIE_LINKDOWN) + link_status &= ~PCI_EXP_LNKSTA_DLLLA; + } + + return link_status; } #endif diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index d07c1d9ed0620..094e6bb3ddc6c 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -580,6 +580,9 @@ static void pci_setup_bridge_io(struct pci_dev *bridge) u8 io_base_lo, io_limit_lo; u16 l; u32 io_upper16; +#ifdef CONFIG_ARCH_PHYTIUM + u32 io_base_limit = 0; +#endif io_mask = PCI_IO_RANGE_MASK; if (bridge->io_window_1k) @@ -608,6 +611,14 @@ static void pci_setup_bridge_io(struct pci_dev *bridge) pci_write_config_word(bridge, PCI_IO_BASE, l); /* Update upper 16 bits of I/O base/limit */ pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, io_upper16); + +#ifdef CONFIG_ARCH_PHYTIUM + if (bridge->dev.parent) { + if (!fwnode_property_read_u32(dev_fwnode(bridge->dev.parent), + "io-upper", &io_base_limit)) + pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, io_base_limit); + } +#endif } static void pci_setup_bridge_mmio(struct pci_dev *bridge) diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index 5cb6940310729..d700332362950 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -86,6 +86,13 @@ struct pci_epc_ops { u32 *msi_addr_offset); int (*start)(struct pci_epc *epc); void (*stop)(struct pci_epc *epc); + +#ifdef CONFIG_ARCH_PHYTIUM + int (*start_dma)(struct pci_epc *epc, u8 func_no, u64 cpu_addr, u64 pci_addr, + size_t size, u8 mode); + int (*dma_status)(struct pci_epc *epc, u8 func_no, u8 mode); +#endif + const struct pci_epc_features* (*get_features)(struct pci_epc *epc, u8 func_no, u8 vfunc_no); struct module *owner; @@ -251,4 +258,11 @@ void __iomem *pci_epc_mem_alloc_addr(struct pci_epc *epc, phys_addr_t *phys_addr, size_t size); void pci_epc_mem_free_addr(struct pci_epc *epc, phys_addr_t phys_addr, void __iomem *virt_addr, size_t size); + +#ifdef CONFIG_ARCH_PHYTIUM +int pci_epc_start_dma(struct pci_epc *epc, u8 func_no, u64 cpu_addr, + u64 pci_addr, size_t size, u8 mode); +int pci_epc_dma_status(struct pci_epc *epc, u8 func_no, u8 mode); +#endif + #endif /* __LINUX_PCI_EPC_H */