diff --git a/Documentation/devicetree/bindings/pci/xilinx-versal-cpm.yaml b/Documentation/devicetree/bindings/pci/xilinx-versal-cpm.yaml index 9823456addea..a28a88dc8e88 100644 --- a/Documentation/devicetree/bindings/pci/xilinx-versal-cpm.yaml +++ b/Documentation/devicetree/bindings/pci/xilinx-versal-cpm.yaml @@ -41,6 +41,10 @@ properties: description: Maps a Requester ID to an MSI controller and associated MSI sideband data. + resets: + description: Reset for the PCIe controller. + maxItems: 1 + ranges: maxItems: 2 @@ -61,6 +65,17 @@ properties: interrupt-controller: true +patternProperties: + '^pcie@[0-2],0$': + type: object + $ref: /schemas/pci/pci-pci-bridge.yaml# + + properties: + reg: + maxItems: 1 + + unevaluatedProperties: false + required: - reg - reg-names @@ -76,6 +91,9 @@ unevaluatedProperties: false examples: - | + #include + #include + versal { #address-cells = <2>; #size-cells = <2>; @@ -99,6 +117,16 @@ examples: reg = <0x0 0xfca10000 0x0 0x1000>, <0x6 0x00000000 0x0 0x10000000>; reg-names = "cpm_slcr", "cfg"; + resets = <&versal_reset VERSAL_RST_PCIE_CORE0>; + + pcie@0,0 { + device_type = "pci"; + reg = <0x0 0x0 0x0 0x0 0x0>; + reset-gpios = <&gpio1 38 GPIO_ACTIVE_LOW>; + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; pcie_intc_0: interrupt-controller { #address-cells = <0>; #interrupt-cells = <1>; @@ -127,6 +155,16 @@ examples: <0x06 0x00000000 0x00 0x1000000>, <0x00 0xfce20000 0x00 0x1000000>; reg-names = "cpm_slcr", "cfg", "cpm_csr"; + resets = <&versal_reset VERSAL_RST_PCIE_CORE0>; + + pcie@0,0 { + device_type = "pci"; + reg = <0x0 0x0 0x0 0x0 0x0>; + reset-gpios = <&gpio1 38 GPIO_ACTIVE_LOW>; + #address-cells = <3>; + #size-cells = <2>; + ranges; + }; pcie_intc_1: interrupt-controller { #address-cells = <0>; diff --git a/drivers/pci/controller/pcie-xilinx-cpm.c b/drivers/pci/controller/pcie-xilinx-cpm.c index d38f27e20761..1fdd7f920590 100644 --- a/drivers/pci/controller/pcie-xilinx-cpm.c +++ b/drivers/pci/controller/pcie-xilinx-cpm.c @@ -6,6 +6,8 @@ */ #include +#include +#include #include #include #include @@ -16,6 +18,7 @@ #include #include #include +#include #include "../pci.h" #include "pcie-xilinx-common.h" @@ -113,6 +116,8 @@ struct xilinx_cpm_variant { * @irq: Error interrupt number * @lock: lock protecting shared register access * @variant: CPM version check pointer + * @perst_gpio: GPIO descriptor for PERST# signal handling + * @rstc: Pointer to the PCIe controller reset */ struct xilinx_cpm_pcie { struct device *dev; @@ -125,6 +130,8 @@ struct xilinx_cpm_pcie { int irq; raw_spinlock_t lock; const struct xilinx_cpm_variant *variant; + struct gpio_desc *perst_gpio; + struct reset_control *rstc; }; static u32 pcie_read(struct xilinx_cpm_pcie *port, u32 reg) @@ -389,7 +396,7 @@ static int xilinx_cpm_pcie_init_irq_domain(struct xilinx_cpm_pcie *port) struct device_node *pcie_intc_node; /* Setup INTx */ - pcie_intc_node = of_get_next_child(node, NULL); + pcie_intc_node = of_get_child_by_name(node, "interrupt-controller"); if (!pcie_intc_node) { dev_err(dev, "No PCIe Intc node found\n"); return -EINVAL; @@ -469,6 +476,22 @@ static int xilinx_cpm_setup_irq(struct xilinx_cpm_pcie *port) return 0; } +/** + * xilinx_cpm_pcie_reset - Reset the PCIe controller and deassert PERST# + * @port: PCIe port information + * + * Reset the PCIe controller and then release the PERST# signal so that the + * link can train once the bridge is enabled. + */ +static void xilinx_cpm_pcie_reset(struct xilinx_cpm_pcie *port) +{ + reset_control_assert(port->rstc); + udelay(PCIE_T_PERST_US); + reset_control_deassert(port->rstc); + gpiod_set_value_cansleep(port->perst_gpio, 0); + msleep(PCIE_RESET_CONFIG_WAIT_MS); +} + /** * xilinx_cpm_pcie_init_port - Initialize hardware * @port: PCIe port information @@ -480,6 +503,9 @@ static void xilinx_cpm_pcie_init_port(struct xilinx_cpm_pcie *port) if (variant->version == CPM5NC_HOST) return; + if (port->perst_gpio || port->rstc) + xilinx_cpm_pcie_reset(port); + if (cpm_pcie_link_up(port)) dev_info(port->dev, "PCIe Link is UP\n"); else @@ -512,6 +538,42 @@ static void xilinx_cpm_pcie_init_port(struct xilinx_cpm_pcie *port) XILINX_CPM_PCIE_REG_RPSC); } +/** + * xilinx_cpm_pcie_parse_port - Parse the PCIe Root Port child node + * @port: PCIe port information + * + * Read the PERST# GPIO from the Root Port child node. + * + * Return: '0' on success and error value on failure + */ +static int xilinx_cpm_pcie_parse_port(struct xilinx_cpm_pcie *port) +{ + struct device *dev = port->dev; + + /* + * This platform currently supports only one Root Port, so the loop + * will execute only once. + * TODO: Enhance the driver to handle multiple Root Ports in the future. + */ + for_each_child_of_node_with_prefix(dev->of_node, pcie_port_node, "pcie") { + port->perst_gpio = devm_fwnode_gpiod_get(dev, + of_fwnode_handle(pcie_port_node), + "reset", GPIOD_OUT_HIGH, + NULL); + if (IS_ERR(port->perst_gpio)) { + if (PTR_ERR(port->perst_gpio) == -ENOENT) { + port->perst_gpio = NULL; + return 0; + } + return dev_err_probe(dev, PTR_ERR(port->perst_gpio), + "Failed to request reset GPIO\n"); + } + return 0; + } + + return 0; +} + /** * xilinx_cpm_pcie_parse_dt - Parse Device tree * @port: PCIe port information @@ -525,6 +587,19 @@ static int xilinx_cpm_pcie_parse_dt(struct xilinx_cpm_pcie *port, struct device *dev = port->dev; struct platform_device *pdev = to_platform_device(dev); struct resource *res; + int ret; + + /* CPM5NC does not support PERST# handling yet */ + if (port->variant->version != CPM5NC_HOST) { + port->rstc = devm_reset_control_get_optional_exclusive(dev, NULL); + if (IS_ERR(port->rstc)) + return dev_err_probe(dev, PTR_ERR(port->rstc), + "Failed to request reset\n"); + + ret = xilinx_cpm_pcie_parse_port(port); + if (ret) + return ret; + } port->cpm_base = devm_platform_ioremap_resource_byname(pdev, "cpm_slcr"); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 0ede46012339..77efa4c5c960 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -40,6 +40,15 @@ struct pcie_tlp_log; */ #define PCIE_T_PERST_CLK_US 100 +/* + * PERST# active time. + * + * See the "Power Sequencing and Reset Signal Timings" table of the PCI Express + * Card Electromechanical Specification, Revision 6.0, Section 2.11.2, Symbol + * "T_PERST". + */ +#define PCIE_T_PERST_US 100 + /* * PCIe r6.0, sec 5.3.3.2.1 * Recommends 1ms to 10ms timeout to check L2 ready.