Merge branch 'pci/controller/xilinx-cpm'

- Add DT and driver support for optional PERST# for use in warm reset
  scenarios (Sai Krishna Musham)

* pci/controller/xilinx-cpm:
  PCI: xilinx-cpm: Add support for PCIe RP PERST# signal
  dt-bindings: PCI: xilinx-versal-cpm: Add PERST# and reset support
This commit is contained in:
Bjorn Helgaas 2026-10-21 13:20:39 -05:00
commit 15b0d8e9db
3 changed files with 123 additions and 1 deletions

View File

@ -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 <dt-bindings/gpio/gpio.h>
#include <dt-bindings/reset/xlnx-versal-resets.h>
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>;

View File

@ -6,6 +6,8 @@
*/
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqchip.h>
@ -16,6 +18,7 @@
#include <linux/of_address.h>
#include <linux/of_pci.h>
#include <linux/of_platform.h>
#include <linux/reset.h>
#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");

View File

@ -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 <PME Synchronization>
* Recommends 1ms to 10ms timeout to check L2 ready.