Merge branch 'pci/controller/dwc-imx6'

- Move IMX6SX_GPR12_PCIE_TEST_POWERDOWN handling into the core reset
  functions (Richard Zhu)

- Add pci_host_common_parse_ports() for use by any native driver to parse
  Root Port properties (currently only reset GPIOs) (Sherry Sun)

- Assert PERST# before enabling regulators to ensure that even if power is
  enabled, endpoint stays inactive until REFCLK is stable (Sherry Sun)

- Parse reset properties in Root Port nodes (falling back to host bridge)
  to help support Key E connectors and the pwrctrl framework (Sherry Sun)

- Configure i.MX95 REF_USE_PAD before PHY reset (Richard Zhu)

- Assert i.MX95 ref_clk_en after reference clock stabilizes (Richard Zhu)

- Integrate new pwrctrl API for DTs with Root Port-level power supplies
  (Sherry Sun)

* pci/controller/dwc-imx6:
  PCI: imx6: Integrate new pwrctrl API
  PCI: imx6: Assert ref_clk_en after reference clock stabilizes on i.MX95
  PCI: imx6: Configure REF_USE_PAD before PHY reset for i.MX95
  PCI: imx6: Parse 'reset-gpios' in Root Port nodes
  PCI: imx6: Assert PERST# before enabling regulators
  PCI: host-generic: Add common helpers for parsing Root Port properties
  dt-bindings: PCI: fsl,imx6q-pcie: Add reset GPIO in Root Port node
  PCI: imx6: Fix IMX6SX_GPR12_PCIE_TEST_POWERDOWN handling
This commit is contained in:
Bjorn Helgaas 2026-06-23 17:32:06 -05:00
commit 878f37d6de
7 changed files with 416 additions and 41 deletions

View File

@ -66,16 +66,34 @@ properties:
- const: dma
reset-gpio:
deprecated: true
description: Should specify the GPIO for controlling the PCI bus device
reset signal. It's not polarity aware and defaults to active-low reset
sequence (L=reset state, H=operation state) (optional required).
This property is deprecated, instead of referencing this property from the
host bridge node, use the reset-gpios property from the root port node.
reset-gpio-active-high:
deprecated: true
description: If present then the reset sequence using the GPIO
specified in the "reset-gpio" property is reversed (H=reset state,
L=operation state) (optional required).
This property is deprecated along with the reset-gpio property above, use
the reset-gpios property from the root port node.
type: boolean
pcie@0:
description:
Describe the i.MX6 PCIe Root Port.
type: object
$ref: /schemas/pci/pci-pci-bridge.yaml#
properties:
reg:
maxItems: 1
unevaluatedProperties: false
required:
- compatible
- reg
@ -236,6 +254,7 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/clock/imx6qdl-clock.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
pcie: pcie@1ffc000 {
@ -262,5 +281,18 @@ examples:
<&clks IMX6QDL_CLK_LVDS1_GATE>,
<&clks IMX6QDL_CLK_PCIE_REF_125M>;
clock-names = "pcie", "pcie_bus", "pcie_phy";
pcie_port0: pcie@0 {
compatible = "pciclass,0604";
device_type = "pci";
reg = <0x0 0x0 0x0 0x0 0x0>;
bus-range = <0x01 0xff>;
#address-cells = <3>;
#size-cells = <2>;
ranges;
reset-gpios = <&gpio7 12 GPIO_ACTIVE_LOW>;
};
};
...

View File

@ -114,6 +114,7 @@ config PCI_IMX6_HOST
depends on PCI_MSI
select PCIE_DW_HOST
select PCI_IMX6
select PCI_PWRCTRL_GENERIC
help
Enables support for the PCIe controller in the i.MX SoCs to
work in Root Complex mode. The PCI controller on i.MX is based

View File

@ -20,6 +20,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/pci.h>
#include <linux/pci-pwrctrl.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
@ -34,6 +35,7 @@
#include <linux/pm_runtime.h>
#include "../../pci.h"
#include "../pci-host-common.h"
#include "pcie-designware.h"
#define IMX8MQ_GPR_PCIE_REF_USE_PAD BIT(9)
@ -137,6 +139,7 @@ struct imx_pcie_drvdata {
const u32 mode_off[IMX_PCIE_MAX_INSTANCES];
const u32 mode_mask[IMX_PCIE_MAX_INSTANCES];
const struct pci_epc_features *epc_features;
int (*select_ref_clk_src)(struct imx_pcie *pcie);
int (*init_phy)(struct imx_pcie *pcie);
int (*enable_ref_clk)(struct imx_pcie *pcie, bool enable);
int (*core_reset)(struct imx_pcie *pcie, bool assert);
@ -152,7 +155,6 @@ struct imx_lut_data {
struct imx_pcie {
struct dw_pcie *pci;
struct gpio_desc *reset_gpiod;
struct clk_bulk_data *clks;
int num_clks;
bool supports_clkreq;
@ -168,6 +170,8 @@ struct imx_pcie {
u32 tx_swing_full;
u32 tx_swing_low;
struct regulator *vpcie;
struct regulator *vpcie_aux;
bool vpcie_aux_enabled;
struct regulator *vph;
void __iomem *phy_base;
@ -247,10 +251,26 @@ static unsigned int imx_pcie_grp_offset(const struct imx_pcie *imx_pcie)
return imx_pcie->controller_id == 1 ? IOMUXC_GPR16 : IOMUXC_GPR14;
}
static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie)
static int imx95_pcie_select_ref_clk_src(struct imx_pcie *imx_pcie)
{
bool ext = imx_pcie->enable_ext_refclk;
/*
* Regarding the Signal Descriptions of i.MX95 PCIe PHY, ref_use_pad is
* used to select reference clock connected to a pair of pads.
*
* Any change in this input must be followed by phy_reset assertion.
*/
regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_GEN_CTRL,
IMX95_PCIE_REF_USE_PAD,
ext ? IMX95_PCIE_REF_USE_PAD : 0);
return 0;
}
static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie)
{
/*
* ERR051624: The Controller Without Vaux Cannot Exit L23 Ready
* Through Beacon or PERST# De-assertion
@ -269,13 +289,6 @@ static int imx95_pcie_init_phy(struct imx_pcie *imx_pcie)
IMX95_PCIE_PHY_CR_PARA_SEL,
IMX95_PCIE_PHY_CR_PARA_SEL);
regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_PHY_GEN_CTRL,
IMX95_PCIE_REF_USE_PAD,
ext ? IMX95_PCIE_REF_USE_PAD : 0);
regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0,
IMX95_PCIE_REF_CLKEN,
ext ? 0 : IMX95_PCIE_REF_CLKEN);
return 0;
}
@ -665,14 +678,6 @@ static int imx_pcie_attach_pd(struct device *dev)
return 0;
}
static int imx6sx_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable)
{
regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12,
IMX6SX_GPR12_PCIE_TEST_POWERDOWN,
enable ? 0 : IMX6SX_GPR12_PCIE_TEST_POWERDOWN);
return 0;
}
static int imx6q_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable)
{
if (enable) {
@ -732,7 +737,29 @@ static void imx95_pcie_clkreq_override(struct imx_pcie *imx_pcie, bool enable)
static int imx95_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable)
{
bool ext = imx_pcie->enable_ext_refclk;
imx95_pcie_clkreq_override(imx_pcie, enable);
/*
* The ref_clk_en signal must remain de-asserted until the
* reference clock is running at appropriate frequency, at which
* point this bit can be asserted. For lower power states where
* the reference clock to the PHY is disabled, it may also be
* de-asserted.
* +------------------- -+--------+----------------+
* | External clock mode | Enable | PCIE_REF_CLKEN |
* +---------------------+--------+----------------+
* | TRUE | X | 1b'0 |
* +---------------------+--------+----------------+
* | FALSE | TRUE | 1b'1 |
* +---------------------+--------+----------------+
* | FALSE | FALSE | 1b'0 |
* +---------------------+--------+----------------+
*/
regmap_update_bits(imx_pcie->iomuxc_gpr, IMX95_PCIE_SS_RW_REG_0,
IMX95_PCIE_REF_CLKEN,
ext || !enable ? 0 : IMX95_PCIE_REF_CLKEN);
return 0;
}
@ -786,6 +813,9 @@ static int imx6sx_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert)
if (assert)
regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12,
IMX6SX_GPR12_PCIE_TEST_POWERDOWN);
else
regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR12,
IMX6SX_GPR12_PCIE_TEST_POWERDOWN);
/* Force PCIe PHY reset */
regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR5, IMX6SX_GPR5_PCIE_BTNRST_RESET,
@ -1222,16 +1252,70 @@ static void imx_pcie_disable_device(struct pci_host_bridge *bridge,
imx_pcie_remove_lut(imx_pcie, pci_dev_id(pdev));
}
static int imx_pcie_parse_legacy_binding(struct imx_pcie *pcie)
{
struct device *dev = pcie->pci->dev;
struct pci_host_bridge *bridge = pcie->pci->pp.bridge;
struct pci_host_port *port;
struct pci_host_perst *perst;
struct gpio_desc *reset;
reset = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS);
if (IS_ERR(reset))
return PTR_ERR(reset);
if (!reset)
return 0;
port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
if (!port)
return -ENOMEM;
perst = devm_kzalloc(dev, sizeof(*perst), GFP_KERNEL);
if (!perst)
return -ENOMEM;
INIT_LIST_HEAD(&port->perst);
perst->desc = reset;
INIT_LIST_HEAD(&perst->list);
list_add_tail(&perst->list, &port->perst);
INIT_LIST_HEAD(&port->list);
list_add_tail(&port->list, &bridge->ports);
return devm_add_action_or_reset(dev, pci_host_common_delete_ports,
&bridge->ports);
}
static void imx_pcie_vpcie_aux_disable(void *data)
{
struct regulator *vpcie_aux = data;
regulator_disable(vpcie_aux);
}
static void imx_pcie_assert_perst(struct imx_pcie *imx_pcie, bool assert)
{
struct dw_pcie *pci = imx_pcie->pci;
struct pci_host_bridge *bridge = pci->pp.bridge;
struct pci_host_perst *perst;
struct pci_host_port *port;
if (!bridge || list_empty(&bridge->ports))
return;
if (assert) {
gpiod_set_value_cansleep(imx_pcie->reset_gpiod, 1);
} else {
if (imx_pcie->reset_gpiod) {
msleep(PCIE_T_PVPERL_MS);
gpiod_set_value_cansleep(imx_pcie->reset_gpiod, 0);
msleep(PCIE_RESET_CONFIG_WAIT_MS);
list_for_each_entry(port, &bridge->ports, list) {
list_for_each_entry(perst, &port->perst, list)
gpiod_direction_output(perst->desc, 1);
}
} else {
mdelay(PCIE_T_PVPERL_MS);
list_for_each_entry(port, &bridge->ports, list) {
list_for_each_entry(perst, &port->perst, list)
gpiod_direction_output(perst->desc, 0);
}
mdelay(PCIE_RESET_CONFIG_WAIT_MS);
}
}
@ -1240,8 +1324,47 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp)
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
struct device *dev = pci->dev;
struct imx_pcie *imx_pcie = to_imx_pcie(pci);
struct pci_host_bridge *bridge = pp->bridge;
int ret;
if (bridge && list_empty(&bridge->ports)) {
/* Parse Root Port nodes if present */
ret = pci_host_common_parse_ports(dev, bridge);
if (ret) {
if (ret != -ENODEV) {
dev_err(dev, "Failed to parse Root Port nodes: %d\n", ret);
return ret;
}
/*
* Fall back to legacy binding for DT backwards
* compatibility
*/
ret = imx_pcie_parse_legacy_binding(imx_pcie);
if (ret)
return ret;
}
}
imx_pcie_assert_perst(imx_pcie, true);
/* Keep 3.3Vaux supply enabled for entire PCIe controller lifecycle */
if (imx_pcie->vpcie_aux && !imx_pcie->vpcie_aux_enabled) {
ret = regulator_enable(imx_pcie->vpcie_aux);
if (ret) {
dev_err(dev, "failed to enable vpcie_aux regulator: %d\n",
ret);
return ret;
}
imx_pcie->vpcie_aux_enabled = true;
ret = devm_add_action_or_reset(dev, imx_pcie_vpcie_aux_disable,
imx_pcie->vpcie_aux);
if (ret)
return ret;
}
/* Legacy regulator handling for DT backward compatibility. */
if (imx_pcie->vpcie) {
ret = regulator_enable(imx_pcie->vpcie);
if (ret) {
@ -1251,25 +1374,39 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp)
}
}
ret = pci_pwrctrl_create_devices(dev);
if (ret) {
dev_err(dev, "failed to create pwrctrl devices\n");
goto err_reg_disable;
}
ret = pci_pwrctrl_power_on_devices(dev);
if (ret) {
dev_err(dev, "failed to power on pwrctrl devices\n");
goto err_pwrctrl_destroy;
}
ret = imx_pcie_clk_enable(imx_pcie);
if (ret) {
dev_err(dev, "unable to enable pcie clocks: %d\n", ret);
goto err_pwrctrl_power_off;
}
if (pp->bridge && imx_check_flag(imx_pcie, IMX_PCIE_FLAG_HAS_LUT)) {
pp->bridge->enable_device = imx_pcie_enable_device;
pp->bridge->disable_device = imx_pcie_disable_device;
}
if (imx_pcie->drvdata->select_ref_clk_src)
imx_pcie->drvdata->select_ref_clk_src(imx_pcie);
imx_pcie_assert_core_reset(imx_pcie);
imx_pcie_assert_perst(imx_pcie, true);
if (imx_pcie->drvdata->init_phy)
imx_pcie->drvdata->init_phy(imx_pcie);
imx_pcie_configure_type(imx_pcie);
ret = imx_pcie_clk_enable(imx_pcie);
if (ret) {
dev_err(dev, "unable to enable pcie clocks: %d\n", ret);
goto err_reg_disable;
}
if (imx_pcie->phy) {
ret = phy_init(imx_pcie->phy);
if (ret) {
@ -1314,6 +1451,11 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp)
phy_exit(imx_pcie->phy);
err_clk_disable:
imx_pcie_clk_disable(imx_pcie);
err_pwrctrl_power_off:
pci_pwrctrl_power_off_devices(dev);
err_pwrctrl_destroy:
if (ret != -EPROBE_DEFER)
pci_pwrctrl_destroy_devices(dev);
err_reg_disable:
if (imx_pcie->vpcie)
regulator_disable(imx_pcie->vpcie);
@ -1332,6 +1474,7 @@ static void imx_pcie_host_exit(struct dw_pcie_rp *pp)
}
imx_pcie_clk_disable(imx_pcie);
pci_pwrctrl_power_off_devices(pci->dev);
if (imx_pcie->vpcie)
regulator_disable(imx_pcie->vpcie);
}
@ -1678,13 +1821,6 @@ static int imx_pcie_probe(struct platform_device *pdev)
return PTR_ERR(imx_pcie->phy_base);
}
/* Fetch GPIOs */
imx_pcie->reset_gpiod = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(imx_pcie->reset_gpiod))
return dev_err_probe(dev, PTR_ERR(imx_pcie->reset_gpiod),
"unable to get reset gpio\n");
gpiod_set_consumer_name(imx_pcie->reset_gpiod, "PCIe reset");
/* Fetch clocks */
imx_pcie->num_clks = devm_clk_bulk_get_all(dev, &imx_pcie->clks);
if (imx_pcie->num_clks < 0)
@ -1782,9 +1918,13 @@ static int imx_pcie_probe(struct platform_device *pdev)
of_property_read_u32(node, "fsl,max-link-speed", &pci->max_link_speed);
imx_pcie->supports_clkreq = of_property_read_bool(node, "supports-clkreq");
ret = devm_regulator_get_enable_optional(&pdev->dev, "vpcie3v3aux");
if (ret < 0 && ret != -ENODEV)
return dev_err_probe(dev, ret, "failed to enable Vaux supply\n");
imx_pcie->vpcie_aux = devm_regulator_get_optional(&pdev->dev,
"vpcie3v3aux");
if (IS_ERR(imx_pcie->vpcie_aux)) {
if (PTR_ERR(imx_pcie->vpcie_aux) != -ENODEV)
return PTR_ERR(imx_pcie->vpcie_aux);
imx_pcie->vpcie_aux = NULL;
}
imx_pcie->vpcie = devm_regulator_get_optional(&pdev->dev, "vpcie");
if (IS_ERR(imx_pcie->vpcie)) {
@ -1846,6 +1986,8 @@ static void imx_pcie_shutdown(struct platform_device *pdev)
/* bring down link, so bootloader gets clean state in case of reboot */
imx_pcie_assert_core_reset(imx_pcie);
imx_pcie_assert_perst(imx_pcie, true);
pci_pwrctrl_power_off_devices(&pdev->dev);
pci_pwrctrl_destroy_devices(&pdev->dev);
}
static const struct imx_pcie_drvdata drvdata[] = {
@ -1877,7 +2019,6 @@ static const struct imx_pcie_drvdata drvdata[] = {
.mode_off[0] = IOMUXC_GPR12,
.mode_mask[0] = IMX6Q_GPR12_DEVICE_TYPE,
.init_phy = imx6sx_pcie_init_phy,
.enable_ref_clk = imx6sx_pcie_enable_ref_clk,
.core_reset = imx6sx_pcie_core_reset,
.ops = &imx_pcie_host_ops,
},
@ -1967,6 +2108,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
.mode_mask[0] = IMX95_PCIE_DEVICE_TYPE,
.core_reset = imx95_pcie_core_reset,
.init_phy = imx95_pcie_init_phy,
.select_ref_clk_src = imx95_pcie_select_ref_clk_src,
.wait_pll_lock = imx95_pcie_wait_for_phy_pll_lock,
.enable_ref_clk = imx95_pcie_enable_ref_clk,
.clr_clkreq_override = imx95_pcie_clr_clkreq_override,
@ -2022,6 +2164,7 @@ static const struct imx_pcie_drvdata drvdata[] = {
.ltssm_mask = IMX95_PCIE_LTSSM_EN,
.mode_off[0] = IMX95_PE0_GEN_CTRL_1,
.mode_mask[0] = IMX95_PCIE_DEVICE_TYPE,
.select_ref_clk_src = imx95_pcie_select_ref_clk_src,
.init_phy = imx95_pcie_init_phy,
.core_reset = imx95_pcie_core_reset,
.wait_pll_lock = imx95_pcie_wait_for_phy_pll_lock,

View File

@ -9,6 +9,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_pci.h>
@ -17,6 +18,174 @@
#include "pci-host-common.h"
/**
* pci_host_common_delete_ports - Cleanup function for port list
* @data: Pointer to the port list head
*/
void pci_host_common_delete_ports(void *data)
{
struct list_head *ports = data;
struct pci_host_perst *perst, *tmp_perst;
struct pci_host_port *port, *tmp_port;
list_for_each_entry_safe(port, tmp_port, ports, list) {
list_for_each_entry_safe(perst, tmp_perst, &port->perst, list)
list_del(&perst->list);
list_del(&port->list);
}
}
EXPORT_SYMBOL_GPL(pci_host_common_delete_ports);
/**
* pci_host_common_parse_perst - Parse PERST# from all nodes, depth first
* @dev: Device pointer
* @port: PCI host port
* @np: Device tree node to start parsing from
*
* Recursively parse PERST# GPIO from all PCIe bridge nodes starting from
* @np in a depth-first manner.
*
* Return: 0 on success, negative error code on failure.
*/
static int pci_host_common_parse_perst(struct device *dev,
struct pci_host_port *port,
struct device_node *np)
{
struct pci_host_perst *perst;
struct gpio_desc *reset;
int ret;
if (!of_property_present(np, "reset-gpios"))
goto parse_child_node;
reset = devm_fwnode_gpiod_get(dev, of_fwnode_handle(np), "reset",
GPIOD_ASIS, "PERST#");
if (IS_ERR(reset)) {
/*
* FIXME: GPIOLIB currently supports exclusive GPIO access only.
* Non exclusive access is broken. But shared PERST# requires
* non-exclusive access. So once GPIOLIB properly supports it,
* implement it here.
*/
if (PTR_ERR(reset) == -EBUSY)
dev_err(dev, "Shared PERST# is not supported\n");
return PTR_ERR(reset);
}
perst = devm_kzalloc(dev, sizeof(*perst), GFP_KERNEL);
if (!perst)
return -ENOMEM;
INIT_LIST_HEAD(&perst->list);
perst->desc = reset;
list_add_tail(&perst->list, &port->perst);
parse_child_node:
for_each_available_child_of_node_scoped(np, child) {
ret = pci_host_common_parse_perst(dev, port, child);
if (ret)
return ret;
}
return 0;
}
/**
* pci_host_common_parse_port - Parse a single Root Port node
* @dev: Device pointer
* @bridge: PCI host bridge
* @node: Device tree node of the Root Port
*
* Parse Root Port properties from the device tree. Currently it only
* handles the PERST# GPIO (including PERST# GPIOs from all PCIe bridge
* nodes under this Root Port), which is optional.
*
* NOTE: This helper fetches resources (like PERST# GPIO) optionally. If a
* controller driver has a hard dependency on certain resources (PHY,
* clocks, regulators, etc.), those resources MUST be modeled correctly in
* the DT binding and validated in DTS. This helper cannot enforce such
* dependencies and the driver may fail to operate if required resources
* are missing.
*
* Return: 0 on success, -ENODEV if PERST# found in RC node (legacy binding
* should be used), Other negative error codes on failure.
*/
static int pci_host_common_parse_port(struct device *dev,
struct pci_host_bridge *bridge,
struct device_node *node)
{
struct pci_host_port *port;
int ret;
port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
if (!port)
return -ENOMEM;
INIT_LIST_HEAD(&port->perst);
ret = pci_host_common_parse_perst(dev, port, node);
if (ret)
return ret;
/*
* 1. PERST# found in RP or its child nodes - list is not empty,
* continue
*
* 2. PERST# not found in RP/children, but found in RC node -
* return -ENODEV to fallback legacy binding
*
* 3. PERST# not found anywhere - list is empty, continue (optional
* PERST#)
*/
if (list_empty(&port->perst)) {
if (of_property_present(dev->of_node, "reset-gpios") ||
of_property_present(dev->of_node, "reset-gpio"))
return -ENODEV;
}
INIT_LIST_HEAD(&port->list);
list_add_tail(&port->list, &bridge->ports);
return 0;
}
/**
* pci_host_common_parse_ports - Parse Root Port nodes from device tree
* @dev: Device pointer
* @bridge: PCI host bridge
*
* Iterate through child nodes of the host bridge and parse Root Port
* properties (currently only reset GPIOs).
*
* Return: 0 on success, -ENODEV if no ports found or PERST# found in RC
* node (legacy binding should be used), Other negative error codes on
* failure.
*/
int pci_host_common_parse_ports(struct device *dev, struct pci_host_bridge *bridge)
{
int ret = -ENODEV;
for_each_available_child_of_node_scoped(dev->of_node, of_port) {
if (!of_node_is_type(of_port, "pci"))
continue;
ret = pci_host_common_parse_port(dev, bridge, of_port);
if (ret)
goto err_cleanup;
}
if (ret)
return ret;
return devm_add_action_or_reset(dev, pci_host_common_delete_ports,
&bridge->ports);
err_cleanup:
pci_host_common_delete_ports(&bridge->ports);
return ret;
}
EXPORT_SYMBOL_GPL(pci_host_common_parse_ports);
static void gen_pci_unmap_cfg(void *ptr)
{
pci_ecam_free((struct pci_config_window *)ptr);

View File

@ -12,6 +12,34 @@
struct pci_ecam_ops;
/**
* struct pci_host_perst - PERST# GPIO descriptor
* @list: List node for linking multiple PERST# GPIOs
* @desc: GPIO descriptor for PERST# signal
*
* This structure holds a single PERST# GPIO descriptor.
*/
struct pci_host_perst {
struct list_head list;
struct gpio_desc *desc;
};
/**
* struct pci_host_port - Generic Root Port properties
* @list: List node for linking multiple ports
* @perst: List of PERST# GPIO descriptors for this port and its children
*
* This structure contains common properties that can be parsed from
* Root Port device tree nodes.
*/
struct pci_host_port {
struct list_head list;
struct list_head perst;
};
void pci_host_common_delete_ports(void *data);
int pci_host_common_parse_ports(struct device *dev,
struct pci_host_bridge *bridge);
int pci_host_common_probe(struct platform_device *pdev);
int pci_host_common_init(struct platform_device *pdev,
struct pci_host_bridge *bridge,

View File

@ -660,6 +660,7 @@ static void pci_init_host_bridge(struct pci_host_bridge *bridge)
{
INIT_LIST_HEAD(&bridge->windows);
INIT_LIST_HEAD(&bridge->dma_ranges);
INIT_LIST_HEAD(&bridge->ports);
/*
* We assume we can manage these PCIe features. Some systems may

View File

@ -635,6 +635,7 @@ struct pci_host_bridge {
int domain_nr;
struct list_head windows; /* resource_entry */
struct list_head dma_ranges; /* dma ranges resource list */
struct list_head ports; /* Root Port list (pci_host_port) */
#ifdef CONFIG_PCI_IDE
u16 nr_ide_streams; /* Max streams possibly active in @ide_stream_ida */
struct ida ide_stream_ida;