mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
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:
commit
15b0d8e9db
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user