Contributors: 20
Author Tokens Token Proportion Commits Commit Proportion
Sherry Sun 463 41.12% 1 3.12%
Krishna chaitanya chundru 151 13.41% 1 3.12%
Jayachandran C 112 9.95% 3 9.38%
Rob Herring 110 9.77% 7 21.88%
David Daney 96 8.53% 2 6.25%
Marc Zyngier 48 4.26% 4 12.50%
Jan Kiszka 37 3.29% 1 3.12%
Will Deacon 35 3.11% 1 3.12%
Lorenzo Pieralisi 34 3.02% 1 3.12%
Ratheesh Kannoth 14 1.24% 1 3.12%
Mayank Rana 9 0.80% 1 3.12%
Manivannan Sadhasivam 5 0.44% 1 3.12%
Jeff Johnson 4 0.36% 1 3.12%
Linus Torvalds (pre-git) 2 0.18% 1 3.12%
Björn Helgaas 1 0.09% 1 3.12%
Uwe Kleine-König 1 0.09% 1 3.12%
Patrick Mochel 1 0.09% 1 3.12%
Jess 1 0.09% 1 3.12%
Cyrille Pitchen 1 0.09% 1 3.12%
Fuqian Huang 1 0.09% 1 3.12%
Total 1126 32


// SPDX-License-Identifier: GPL-2.0
/*
 * Common library for PCI host controller drivers
 *
 * Copyright (C) 2014 ARM Limited
 *
 * Author: Will Deacon <will.deacon@arm.com>
 */

#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>
#include <linux/pci-ecam.h>
#include <linux/platform_device.h>

#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);

#define PCI_HOST_D3COLD_ALLOWED        BIT(0)
#define PCI_HOST_PME_D3COLD_CAPABLE    BIT(1)


static void gen_pci_unmap_cfg(void *ptr)
{
	pci_ecam_free((struct pci_config_window *)ptr);
}

struct pci_config_window *pci_host_common_ecam_create(struct device *dev,
		struct pci_host_bridge *bridge, const struct pci_ecam_ops *ops)
{
	int err;
	struct resource cfgres;
	struct resource_entry *bus;
	struct pci_config_window *cfg;

	err = of_address_to_resource(dev->of_node, 0, &cfgres);
	if (err) {
		dev_err(dev, "missing or malformed \"reg\" property\n");
		return ERR_PTR(err);
	}

	bus = resource_list_first_type(&bridge->windows, IORESOURCE_BUS);
	if (!bus)
		return ERR_PTR(-ENODEV);

	cfg = pci_ecam_create(dev, &cfgres, bus->res, ops);
	if (IS_ERR(cfg))
		return cfg;

	err = devm_add_action_or_reset(dev, gen_pci_unmap_cfg, cfg);
	if (err)
		return ERR_PTR(err);

	return cfg;
}
EXPORT_SYMBOL_GPL(pci_host_common_ecam_create);

int pci_host_common_init(struct platform_device *pdev,
			 struct pci_host_bridge *bridge,
			 const struct pci_ecam_ops *ops)
{
	struct device *dev = &pdev->dev;
	struct pci_config_window *cfg;

	of_pci_check_probe_only();

	platform_set_drvdata(pdev, bridge);

	/* Parse and map our Configuration Space windows */
	cfg = pci_host_common_ecam_create(dev, bridge, ops);
	if (IS_ERR(cfg))
		return PTR_ERR(cfg);

	/* Do not reassign bus numbers if probe only */
	if (!pci_has_flag(PCI_PROBE_ONLY))
		pci_add_flags(PCI_REASSIGN_ALL_BUS);

	bridge->sysdata = cfg;
	bridge->ops = (struct pci_ops *)&ops->pci_ops;
	bridge->enable_device = ops->enable_device;
	bridge->disable_device = ops->disable_device;
	bridge->msi_domain = true;

	return pci_host_probe(bridge);
}
EXPORT_SYMBOL_GPL(pci_host_common_init);

int pci_host_common_probe(struct platform_device *pdev)
{
	const struct pci_ecam_ops *ops;
	struct pci_host_bridge *bridge;

	ops = of_device_get_match_data(&pdev->dev);
	if (!ops)
		return -ENODEV;

	bridge = devm_pci_alloc_host_bridge(&pdev->dev, 0);
	if (!bridge)
		return -ENOMEM;

	return pci_host_common_init(pdev, bridge, ops);
}
EXPORT_SYMBOL_GPL(pci_host_common_probe);

void pci_host_common_remove(struct platform_device *pdev)
{
	struct pci_host_bridge *bridge = platform_get_drvdata(pdev);

	pci_lock_rescan_remove();
	pci_stop_root_bus(bridge->bus);
	pci_remove_root_bus(bridge->bus);
	pci_unlock_rescan_remove();
}
EXPORT_SYMBOL_GPL(pci_host_common_remove);

static int __pci_host_common_d3cold_possible(struct pci_dev *pdev,
					     void *userdata)
{
	u32 *flags = userdata;

	if (!pdev->dev.driver && !pci_is_enabled(pdev))
		return 0;

	if (pdev->current_state != PCI_D3hot)
		goto exit;

	if (device_may_wakeup(&pdev->dev)) {
		if (!pci_pme_capable(pdev, PCI_D3cold))
			goto exit;
		else
			*flags |= PCI_HOST_PME_D3COLD_CAPABLE;
	}

	return 0;

exit:
	*flags &= ~PCI_HOST_D3COLD_ALLOWED;

	return -EOPNOTSUPP;
}

/**
 * pci_host_common_d3cold_possible - Determine whether the host bridge can
 *				     transition the devices into D3cold.
 *
 * @bridge: PCI host bridge to check
 * @pme_capable: Pointer to update if there is any device capable of generating
 *		 PME from D3cold.
 *
 * Walk downstream PCIe endpoint devices and determine whether the host bridge
 * is permitted to transition the devices into D3cold.
 *
 * Devices under host bridge can enter D3cold only if all active PCIe
 * endpoints are in PCI_D3hot and any wakeup-enabled endpoint is capable of
 * generating PME from D3cold.  Inactive endpoints are ignored.
 *
 * The @pme_capable output allows PCIe controller drivers to apply
 * platform-specific handling to preserve wakeup functionality.
 *
 * Return: %true if the host bridge may enter D3cold, otherwise %false.
 */
bool pci_host_common_d3cold_possible(struct pci_host_bridge *bridge,
				     bool *pme_capable)
{
	u32 flags = PCI_HOST_D3COLD_ALLOWED;

	pci_walk_bus(bridge->bus, __pci_host_common_d3cold_possible, &flags);

	*pme_capable = !!(flags & PCI_HOST_PME_D3COLD_CAPABLE);

	return !!(flags & PCI_HOST_D3COLD_ALLOWED);
}
EXPORT_SYMBOL_GPL(pci_host_common_d3cold_possible);

MODULE_DESCRIPTION("Common library for PCI host controller drivers");
MODULE_LICENSE("GPL v2");