[PATCH V14 02/12] PCI: host-generic: Add common helpers for parsing Root Port properties
Sherry Sun
sherry.sun at nxp.com
Mon May 18 01:42:38 PDT 2026
> Subject: Re: [PATCH V14 02/12] PCI: host-generic: Add common helpers for
> parsing Root Port properties
>
> On Wed, Apr 22, 2026 at 05:35:39PM +0800, Sherry Sun wrote:
> > Introduce generic helper functions to parse Root Port device tree
> > nodes and extract common properties like reset GPIOs. This allows
> > multiple PCI host controller drivers to share the same parsing logic.
> >
> > Define struct pci_host_port to hold common Root Port properties
> > (currently only list of PERST# GPIO descriptors) and add
> > pci_host_common_parse_ports() to parse Root Port nodes from device
> tree.
> >
> > Also add the 'ports' list to struct pci_host_bridge for better
> > maintain parsed Root Port information.
> > ...
>
> > +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;
>
> This doesn't seem right to me. The parser of per-Root Port properties should
> not be responsible for deciding whether legacy methods are valid, i.e.,
> whether a property is in the Root Complex node. I think it's up to the caller
> to decide whether it needs to look elsewhere.
>
> I don't think this even needs to return a "success/failure" value because there
> may be more properties in the future, and not all will be required. This
> function can't tell which properties a specific driver requires and which are
> optional.
>
> The caller can check whether we found what it needs and fall back to a legacy
> method as needed.
Hi Bjorn,
The code here was suggested by Mani, https://lore.kernel.org/all/lnzprzrdwra7pn7d6m3sbj5pvjy64blwpjl6i3lmlnfbyho63b@czpyhpgz5vum/.
I think your suggestion here is reasonable, the per-Root Port parser shouldn't
check the RC-level binding. That's a policy decision that belongs to the caller.
Hi Mani, if you also agree, I'll rework this so that:
1. pci_host_common_parse_port() only parses properties from the Root Port
(and its children) without checking the RC node.
2. The function won't return failure for "property not found" - it will only return
errors for real failures (e.g., -ENOMEM, GPIO acquisition errors).
3. The legacy fallback logic will be moved to the caller, which can inspect the
parsed result and decide whether to fall back to the legacy binding.
>
> > + }
> > +
> > + 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
> > + *
> > + * This function iterates through child nodes of the host bridge and
> > +parses
> > + * Root Port properties (currently only reset GPIOs).
> > + *
> > + * Returns: 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;
> > + }
>
> I think we should export pci_host_common_parse_port() itself and drop this
> so we deal with a single Root Port, and drivers that support multiple RPs
> should include their own loop similar to this. That way the driver can do
> several things at once in each iteration of that loop, e.g., get resources, power
> up, configure, etc.
>
> I see that would require some rework of the devm_add_action_or_reset()
> cleanup.
Thanks for the suggestion, make sense.
I'll export pci_host_common_parse_port() to deal with a single Root Port
and drop pci_host_common_parse_ports().
>
> > + 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);
> > ...
>
> > + * 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;
> > +};
>
> How do we associate an element of this list with something?
>
> Based on the imx6 changes, I guess we don't; we don't even associate the
> pci_host_port with an RP. We just assert/deassert PERST# for every RP at
> once, and we do it for every GPIO associated with each RP.
>
> There's no way to assert PERST# for a single RP. I guess we don't need that?
You are right. In the current design, we assert/deassert PERST# for all GPIOs across
all Root Ports simultaneously - there is no per-RP independent PERST# control.
And I think we don't need it for now, for most platforms, multiple RPs typically share
a single PERST# signal or require synchronized reset sequencing during initialization.
I am not aware of a practical use case where independent per-RP PERST# control is
required at the host controller driver level during probe/remove.
The per-port structure is a natural result of parsing per-RP DT nodes (each Root Port
child node maps to one pci_host_port, with PERST# GPIOs collected from the RP and
its downstream nodes). Even if such a requirement does arise in the future, since
the controller driver now owns the multi-RP loop, the driver that needs per-RP PERST#
control could associate each parsed port with its own per-RP context and operate on
specific ports independently.
For the i.MX case (single Root Port), there's no need for per-RP PERST# control -- we
just assert/deassert all PERST# GPIOs at once.
>
> > +/**
> > + * 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 {
>
> "host_port" is not really a standard term. And despite the comments above
> and below, I don't think the list is restricted to Root Ports because we traverse
> the whole hierarchy below the RP.
Ok. How about struct pci_root_port_info to better reflect its purpose, I can also
highlight in the struct comment that it holds common properties parsed from a
Root Port device tree node and all PCIe bridge nodes under this Root Port (currently
only PERST# GPIOs). Or any other suggestions?
Best Regards
Sherry
>
> > + struct list_head list;
> > + struct list_head perst;
> > +};
>
> > + struct list_head ports; /* Root Port list (pci_host_port) */
More information about the linux-arm-kernel
mailing list