root/drivers/pci/controller/pci-host-common.c
// 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");