From 5be0a2719fc0549096bfdf4fc998eae6296104e5 Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Sat, 9 May 2026 14:42:16 +0800 Subject: [PATCH 01/10] pcie: phytium: Fix PCIe IO space access restriction issue Some PCI devices have IO space access limit and only supporting 16 bit addresses. We update upper 16 bits of I/O base/limit according to the bridge and the RC root port. Signed-off-by: Zhang Fuxiang Signed-off-by: Xiao Cong Signed-off-by: Wang Yinfeng --- drivers/pci/setup-bus.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index d07c1d9ed0620..195bb8d1388a0 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,19 @@ 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); + } + if (bridge->dev.parent->parent) { + if (!fwnode_property_read_u32(dev_fwnode(bridge->dev.parent->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) From 5d5310c0d841eb9e1b8b60e922063d247e8dd68d Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Sat, 9 May 2026 14:49:44 +0800 Subject: [PATCH 02/10] PCI: phytium: Add hotplug and hotreset workaround patch for phytium SoCs When the PCIe device is unplugged or under hotreset, the PCIe controller's protrction mechanism is triggered, which will make the link inaccessible. This patch disables the protection after the link is up and makes the PCIe hotplug or hotreset process work well. Signed-off-by: Zhang Fuxiang Signed-off-by: Long Shixiang Signed-off-by: Wang Yinfeng --- drivers/pci/pci.h | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 8033f9056a801..b75d8bf7bba3a 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -914,4 +914,46 @@ static inline void phytium_clear_ctrl_prot(struct pci_dev *pdev, int op) } #endif +#ifdef CONFIG_ARCH_PHYTIUM +#include + +#define PHYTIUM_PCIE_HOTRESET 0 +#define PHYTIUM_PCIE_HOTPLUG 1 +#define PHYTIUM_PCI_VENDOR_ID 0x1DB7 +#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"); +} +#endif + #endif /* DRIVERS_PCI_H */ From 6dd7290d4f0301997ae2903d77efc82b38701912 Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Sat, 9 May 2026 14:56:27 +0800 Subject: [PATCH 03/10] PCI: Add ACS quirk for phytium root and switch ports The phytium PCIe root ports and X100 switch do not support ACS at this point. However, the hardware provides isolation and source validation through the SMMU. The stream ID generated by the PCIe ports contain both the bus/device/function number as well as the port ID in its 3 most significant bits. Turn on ACS but disable all the peer-to-peer features. Signed-off-by: Zhang Fuxiang Signed-off-by: Xiao Cong Signed-off-by: Wang Yinfeng --- drivers/pci/quirks.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index cf8dc19954481..4f583c8c679af 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -5391,11 +5391,9 @@ static const struct pci_dev_acs_enabled { { PCI_VENDOR_ID_MUCSE, 0x1061, pci_quirk_mf_endpoint_acs }, { PCI_VENDOR_ID_MUCSE, 0x1c61, pci_quirk_mf_endpoint_acs }, /* Phytium Technology */ -#ifdef CONFIG_ARCH_PHYTIUM - { PCI_VENDOR_ID_PLX, PCI_ANY_ID, pci_quirk_xgene_acs }, - { PCI_VENDOR_ID_CDNS, PCI_ANY_ID, pci_quirk_xgene_acs }, -#endif /* CONFIG_ARCH_PHYTIUM */ - { PCI_VENDOR_ID_PHYTIUM, PCI_ANY_ID, pci_quirk_xgene_acs }, + { 0x10b5, PCI_ANY_ID, pci_quirk_xgene_acs }, + { 0x17cd, PCI_ANY_ID, pci_quirk_xgene_acs }, + { 0x1db7, PCI_ANY_ID, pci_quirk_xgene_acs }, { 0 } }; From 03d1fe6a6074fd516bf649edf39d5a0ee1f689e5 Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Sat, 9 May 2026 14:59:03 +0800 Subject: [PATCH 04/10] pci: phytium: Restore PCIe config space after hotrest In the phytium ps2308 platforms, when host PCIe bridge operate hotreset, then host pcie bridge config register will restore default value. So before hotreset, it need save host PCIe bridge config register. Then after hotreset, it need restore save host PCIe bridge config register. Signed-off-by: Zhang Fuxiang Signed-off-by: Xiao Cong Signed-off-by: Wang Yinfeng --- drivers/pci/pci.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index cd18c4b955882..967d20dd4159a 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); @@ -5358,6 +5361,17 @@ 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; + } +#endif pcibios_reset_secondary_bus(dev); return pci_bridge_wait_for_secondary_bus(dev, "bus reset"); From d0829e06feb913468bc362268e91c44c6ad3cba3 Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Sat, 9 May 2026 15:01:12 +0800 Subject: [PATCH 05/10] pci: phytium: Retry secondary-bus reset when link degrades Some Phytium-based systems can come up with reduced PCIe link speed or lane count after a hotreset. A second reset attempt often restores the original link characteristics. Signed-off-by: Zhang Fuxiang Signed-off-by: Li Tongfeng Signed-off-by: Wang Yinfeng --- drivers/pci/pci.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 967d20dd4159a..740fa8ca91247 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -5352,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 @@ -5370,6 +5419,8 @@ int pci_bridge_secondary_bus_reset(struct pci_dev *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); From 4c506d8d66577283d77769d6afbddfdfdeb95c7d Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Sat, 9 May 2026 15:03:34 +0800 Subject: [PATCH 06/10] pci: phytium: Fix incorrect LNKSTA_DLLLA status on Phytium 24080 Soc On Phytium 24080 platform, reading the PCIe Link Status (LNKSTA) register through the standard PCIe capability interface may return an incorrect value for the Data Link Layer Link Active (DLLLA) bit. This causes the kernel to report wrong link state after a device is hot-unplugged. Signed-off-by: Zhang Fuxiang Signed-off-by: Li Tongfeng --- drivers/pci/hotplug/pciehp_ctrl.c | 2 +- drivers/pci/hotplug/pciehp_hpc.c | 15 ++++++++ drivers/pci/pci.c | 2 +- drivers/pci/pci.h | 60 +++++++++++++++++++++++-------- 4 files changed, 63 insertions(+), 16 deletions(-) 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 740fa8ca91247..d0f2f8f0363cd 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -5343,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 } diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index b75d8bf7bba3a..ffc03439b23a9 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -877,11 +877,14 @@ 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 +#define PHYTIUM_PCIE_CTRL_SMC_FUNC_ID 0xC2000020 -static inline void phytium_clear_ctrl_prot(struct pci_dev *pdev, int op) +#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; @@ -932,27 +935,56 @@ static inline void phytium_clear_ctrl_prot(struct pci_dev *pdev, int op) 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; - if (vendor_id != PHYTIUM_PCI_VENDOR_ID || + 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; + return -ENOENT; socket = dev_to_node(&pdev->dev); if (socket < 0) { - pci_err(pdev, "Cannot find socket, stop clean pcie protection\n"); - return; + 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_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"); + 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 From 2bb27af78089e82fef9e094239a369cb0369a74f Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Sat, 9 May 2026 15:11:54 +0800 Subject: [PATCH 07/10] dt-bindings: add phytium,pe2201-pcie-ep.yaml file Add the device tree binding schema for phytium,pe2201-pcie-ep. Signed-off-by: Zhang Fuxiang Signed-off-by: Li Tongfeng Signed-off-by: Wang Yinfeng --- .../bindings/pci/phytium,pe2201-pcie-ep.yaml | 42 +++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 43 insertions(+) create mode 100644 Documentation/devicetree/bindings/pci/phytium,pe2201-pcie-ep.yaml 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 f2c5a9ee20ad8..6a55b0cfc230f 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 From c821153c165788f3a9ebd75d09558d279b04c0a3 Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Sat, 9 May 2026 15:28:58 +0800 Subject: [PATCH 08/10] pci/pcie_phytium_ep: add EPC support for virtual network devices Add phytium ep driver DMA Controller support and add function_num_map to the configfs of EPC. Signed-off-by: Zhang Fuxiang Signed-off-by: Li Tongfeng Signed-off-by: Wang Yinfeng --- drivers/pci/controller/Kconfig | 1 + drivers/pci/controller/pcie-phytium-ep.c | 179 ++++++++++++++---- drivers/pci/controller/pcie-phytium-ep.h | 2 + .../pci/controller/pcie-phytium-register.h | 20 +- drivers/pci/endpoint/pci-ep-cfs.c | 37 ++++ drivers/pci/endpoint/pci-epc-core.c | 51 +++++ include/linux/pci-epc.h | 14 ++ 7 files changed, 262 insertions(+), 42 deletions(-) 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/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 */ From 807e42140cc86b82def3a833010d4c228cfed3fd Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Sat, 9 May 2026 15:31:29 +0800 Subject: [PATCH 09/10] pci: phytium: Fix middle bridge I/O BAR address error issue On PD2308 platform, it only setup root bridge I/O BAR address, and don't modify other bridge I/O BAR address after root. Signed-off-by: Zhang Fuxiang Signed-off-by: Li Wencheng Signed-off-by: Wang Yinfeng --- drivers/pci/pci.h | 156 +++++++++++++++------------------------- drivers/pci/setup-bus.c | 5 -- 2 files changed, 57 insertions(+), 104 deletions(-) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index ffc03439b23a9..c3d912cb71c01 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -886,105 +886,63 @@ static inline pci_power_t mid_pci_get_power_state(struct pci_dev *pdev) 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; - 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"); -} -#endif - -#ifdef CONFIG_ARCH_PHYTIUM -#include - -#define PHYTIUM_PCIE_HOTRESET 0 -#define PHYTIUM_PCIE_HOTPLUG 1 -#define PHYTIUM_PCI_VENDOR_ID 0x1DB7 -#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; - 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; + 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 195bb8d1388a0..094e6bb3ddc6c 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -618,11 +618,6 @@ static void pci_setup_bridge_io(struct pci_dev *bridge) "io-upper", &io_base_limit)) pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, io_base_limit); } - if (bridge->dev.parent->parent) { - if (!fwnode_property_read_u32(dev_fwnode(bridge->dev.parent->parent), - "io-upper", &io_base_limit)) - pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, io_base_limit); - } #endif } From 4a47671e7f949623eec1b20a842ed9eff4c7f786 Mon Sep 17 00:00:00 2001 From: Zhang Fuxiang Date: Wed, 13 May 2026 13:47:00 +0800 Subject: [PATCH 10/10] Revert "PCI: Add ACS quirk for phytium root and switch ports" This reverts commit 6dd7290d4f0301997ae2903d77efc82b38701912. Signed-off-by: Zhang Fuxiang --- drivers/pci/quirks.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 4f583c8c679af..cf8dc19954481 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -5391,9 +5391,11 @@ static const struct pci_dev_acs_enabled { { PCI_VENDOR_ID_MUCSE, 0x1061, pci_quirk_mf_endpoint_acs }, { PCI_VENDOR_ID_MUCSE, 0x1c61, pci_quirk_mf_endpoint_acs }, /* Phytium Technology */ - { 0x10b5, PCI_ANY_ID, pci_quirk_xgene_acs }, - { 0x17cd, PCI_ANY_ID, pci_quirk_xgene_acs }, - { 0x1db7, PCI_ANY_ID, pci_quirk_xgene_acs }, +#ifdef CONFIG_ARCH_PHYTIUM + { PCI_VENDOR_ID_PLX, PCI_ANY_ID, pci_quirk_xgene_acs }, + { PCI_VENDOR_ID_CDNS, PCI_ANY_ID, pci_quirk_xgene_acs }, +#endif /* CONFIG_ARCH_PHYTIUM */ + { PCI_VENDOR_ID_PHYTIUM, PCI_ANY_ID, pci_quirk_xgene_acs }, { 0 } };