| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (C) 2024 Linaro Ltd.
- */
- #define dev_fmt(fmt) "pwrctrl: " fmt
- #include <linux/device.h>
- #include <linux/export.h>
- #include <linux/kernel.h>
- #include <linux/of.h>
- #include <linux/of_graph.h>
- #include <linux/of_platform.h>
- #include <linux/pci.h>
- #include <linux/pci-pwrctrl.h>
- #include <linux/platform_device.h>
- #include <linux/property.h>
- #include <linux/slab.h>
- #include "../pci.h"
- static int pci_pwrctrl_notify(struct notifier_block *nb, unsigned long action,
- void *data)
- {
- struct pci_pwrctrl *pwrctrl = container_of(nb, struct pci_pwrctrl, nb);
- struct device *dev = data;
- if (dev_fwnode(dev) != dev_fwnode(pwrctrl->dev))
- return NOTIFY_DONE;
- switch (action) {
- case BUS_NOTIFY_ADD_DEVICE:
- /*
- * We will have two struct device objects bound to two different
- * drivers on different buses but consuming the same DT node. We
- * must not bind the pins twice in this case but only once for
- * the first device to be added.
- *
- * If we got here then the PCI device is the second after the
- * power control platform device. Mark its OF node as reused.
- */
- dev->of_node_reused = true;
- break;
- }
- return NOTIFY_DONE;
- }
- /**
- * pci_pwrctrl_init() - Initialize the PCI power control context struct
- *
- * @pwrctrl: PCI power control data
- * @dev: Parent device
- */
- void pci_pwrctrl_init(struct pci_pwrctrl *pwrctrl, struct device *dev)
- {
- pwrctrl->dev = dev;
- dev_set_drvdata(dev, pwrctrl);
- }
- EXPORT_SYMBOL_GPL(pci_pwrctrl_init);
- /**
- * pci_pwrctrl_device_set_ready() - Notify the pwrctrl subsystem that the PCI
- * device is powered-up and ready to be detected.
- *
- * @pwrctrl: PCI power control data.
- *
- * Returns:
- * 0 on success, negative error number on error.
- *
- * Note:
- * This function returning 0 doesn't mean the device was detected. It means,
- * that the bus rescan was successfully started. The device will get bound to
- * its PCI driver asynchronously.
- */
- int pci_pwrctrl_device_set_ready(struct pci_pwrctrl *pwrctrl)
- {
- int ret;
- if (!pwrctrl->dev)
- return -ENODEV;
- pwrctrl->nb.notifier_call = pci_pwrctrl_notify;
- ret = bus_register_notifier(&pci_bus_type, &pwrctrl->nb);
- if (ret)
- return ret;
- return 0;
- }
- EXPORT_SYMBOL_GPL(pci_pwrctrl_device_set_ready);
- /**
- * pci_pwrctrl_device_unset_ready() - Notify the pwrctrl subsystem that the PCI
- * device is about to be powered-down.
- *
- * @pwrctrl: PCI power control data.
- */
- void pci_pwrctrl_device_unset_ready(struct pci_pwrctrl *pwrctrl)
- {
- /*
- * We don't have to delete the link here. Typically, this function
- * is only called when the power control device is being detached. If
- * it is being detached then the child PCI device must have already
- * been unbound too or the device core wouldn't let us unbind.
- */
- bus_unregister_notifier(&pci_bus_type, &pwrctrl->nb);
- }
- EXPORT_SYMBOL_GPL(pci_pwrctrl_device_unset_ready);
- static void devm_pci_pwrctrl_device_unset_ready(void *data)
- {
- struct pci_pwrctrl *pwrctrl = data;
- pci_pwrctrl_device_unset_ready(pwrctrl);
- }
- /**
- * devm_pci_pwrctrl_device_set_ready - Managed variant of
- * pci_pwrctrl_device_set_ready().
- *
- * @dev: Device managing this pwrctrl provider.
- * @pwrctrl: PCI power control data.
- *
- * Returns:
- * 0 on success, negative error number on error.
- */
- int devm_pci_pwrctrl_device_set_ready(struct device *dev,
- struct pci_pwrctrl *pwrctrl)
- {
- int ret;
- ret = pci_pwrctrl_device_set_ready(pwrctrl);
- if (ret)
- return ret;
- return devm_add_action_or_reset(dev,
- devm_pci_pwrctrl_device_unset_ready,
- pwrctrl);
- }
- EXPORT_SYMBOL_GPL(devm_pci_pwrctrl_device_set_ready);
- static int __pci_pwrctrl_power_off_device(struct device *dev)
- {
- struct pci_pwrctrl *pwrctrl = dev_get_drvdata(dev);
- if (!pwrctrl)
- return 0;
- return pwrctrl->power_off(pwrctrl);
- }
- static void pci_pwrctrl_power_off_device(struct device_node *np)
- {
- struct platform_device *pdev;
- int ret;
- for_each_available_child_of_node_scoped(np, child)
- pci_pwrctrl_power_off_device(child);
- pdev = of_find_device_by_node(np);
- if (!pdev)
- return;
- if (device_is_bound(&pdev->dev)) {
- ret = __pci_pwrctrl_power_off_device(&pdev->dev);
- if (ret)
- dev_err(&pdev->dev, "Failed to power off device: %d", ret);
- }
- platform_device_put(pdev);
- }
- /**
- * pci_pwrctrl_power_off_devices - Power off pwrctrl devices
- *
- * @parent: PCI host controller device
- *
- * Recursively traverse all pwrctrl devices for the devicetree hierarchy
- * below the specified PCI host controller and power them off in a depth
- * first manner.
- */
- void pci_pwrctrl_power_off_devices(struct device *parent)
- {
- struct device_node *np = parent->of_node;
- for_each_available_child_of_node_scoped(np, child)
- pci_pwrctrl_power_off_device(child);
- }
- EXPORT_SYMBOL_GPL(pci_pwrctrl_power_off_devices);
- static int __pci_pwrctrl_power_on_device(struct device *dev)
- {
- struct pci_pwrctrl *pwrctrl = dev_get_drvdata(dev);
- if (!pwrctrl)
- return 0;
- return pwrctrl->power_on(pwrctrl);
- }
- /*
- * Power on the devices in a depth first manner. Before powering on the device,
- * make sure its driver is bound.
- */
- static int pci_pwrctrl_power_on_device(struct device_node *np)
- {
- struct platform_device *pdev;
- int ret;
- for_each_available_child_of_node_scoped(np, child) {
- ret = pci_pwrctrl_power_on_device(child);
- if (ret)
- return ret;
- }
- pdev = of_find_device_by_node(np);
- if (!pdev)
- return 0;
- if (device_is_bound(&pdev->dev)) {
- ret = __pci_pwrctrl_power_on_device(&pdev->dev);
- } else {
- /* FIXME: Use blocking wait instead of probe deferral */
- dev_dbg(&pdev->dev, "driver is not bound\n");
- ret = -EPROBE_DEFER;
- }
- platform_device_put(pdev);
- return ret;
- }
- /**
- * pci_pwrctrl_power_on_devices - Power on pwrctrl devices
- *
- * @parent: PCI host controller device
- *
- * Recursively traverse all pwrctrl devices for the devicetree hierarchy
- * below the specified PCI host controller and power them on in a depth
- * first manner. On error, all powered on devices will be powered off.
- *
- * Return: 0 on success, -EPROBE_DEFER if any pwrctrl driver is not bound, an
- * appropriate error code otherwise.
- */
- int pci_pwrctrl_power_on_devices(struct device *parent)
- {
- struct device_node *np = parent->of_node;
- struct device_node *child = NULL;
- int ret;
- for_each_available_child_of_node(np, child) {
- ret = pci_pwrctrl_power_on_device(child);
- if (ret)
- goto err_power_off;
- }
- return 0;
- err_power_off:
- for_each_available_child_of_node_scoped(np, tmp) {
- if (tmp == child)
- break;
- pci_pwrctrl_power_off_device(tmp);
- }
- of_node_put(child);
- return ret;
- }
- EXPORT_SYMBOL_GPL(pci_pwrctrl_power_on_devices);
- /*
- * Check whether the pwrctrl device really needs to be created or not. The
- * pwrctrl device will only be created if the node satisfies below requirements:
- *
- * 1. Presence of compatible property with "pci" prefix to match against the
- * pwrctrl driver (AND)
- * 2. At least one of the power supplies defined in the devicetree node of the
- * device (OR) in the remote endpoint parent node to indicate pwrctrl
- * requirement.
- */
- static bool pci_pwrctrl_is_required(struct device_node *np)
- {
- struct device_node *endpoint;
- const char *compat;
- int ret;
- ret = of_property_read_string(np, "compatible", &compat);
- if (ret < 0)
- return false;
- if (!strstarts(compat, "pci"))
- return false;
- if (of_pci_supply_present(np))
- return true;
- if (of_graph_is_present(np)) {
- for_each_endpoint_of_node(np, endpoint) {
- struct device_node *remote __free(device_node) =
- of_graph_get_remote_port_parent(endpoint);
- if (remote) {
- if (of_pci_supply_present(remote)) {
- of_node_put(endpoint);
- return true;
- }
- }
- }
- }
- return false;
- }
- static int pci_pwrctrl_create_device(struct device_node *np,
- struct device *parent)
- {
- struct platform_device *pdev;
- int ret;
- for_each_available_child_of_node_scoped(np, child) {
- ret = pci_pwrctrl_create_device(child, parent);
- if (ret)
- return ret;
- }
- /* Bail out if the platform device is already available for the node */
- pdev = of_find_device_by_node(np);
- if (pdev) {
- platform_device_put(pdev);
- return 0;
- }
- if (!pci_pwrctrl_is_required(np)) {
- dev_dbg(parent, "Skipping OF node: %s\n", np->name);
- return 0;
- }
- /* Now create the pwrctrl device */
- pdev = of_platform_device_create(np, NULL, parent);
- if (!pdev) {
- dev_err(parent, "Failed to create pwrctrl device for node: %s\n", np->name);
- return -EINVAL;
- }
- return 0;
- }
- /**
- * pci_pwrctrl_create_devices - Create pwrctrl devices
- *
- * @parent: PCI host controller device
- *
- * Recursively create pwrctrl devices for the devicetree hierarchy below
- * the specified PCI host controller in a depth first manner. On error, all
- * created devices will be destroyed.
- *
- * Return: 0 on success, negative error number on error.
- */
- int pci_pwrctrl_create_devices(struct device *parent)
- {
- int ret;
- for_each_available_child_of_node_scoped(parent->of_node, child) {
- ret = pci_pwrctrl_create_device(child, parent);
- if (ret) {
- pci_pwrctrl_destroy_devices(parent);
- return ret;
- }
- }
- return 0;
- }
- EXPORT_SYMBOL_GPL(pci_pwrctrl_create_devices);
- static void pci_pwrctrl_destroy_device(struct device_node *np)
- {
- struct platform_device *pdev;
- for_each_available_child_of_node_scoped(np, child)
- pci_pwrctrl_destroy_device(child);
- pdev = of_find_device_by_node(np);
- if (!pdev)
- return;
- of_device_unregister(pdev);
- platform_device_put(pdev);
- of_node_clear_flag(np, OF_POPULATED);
- }
- /**
- * pci_pwrctrl_destroy_devices - Destroy pwrctrl devices
- *
- * @parent: PCI host controller device
- *
- * Recursively destroy pwrctrl devices for the devicetree hierarchy below
- * the specified PCI host controller in a depth first manner.
- */
- void pci_pwrctrl_destroy_devices(struct device *parent)
- {
- struct device_node *np = parent->of_node;
- for_each_available_child_of_node_scoped(np, child)
- pci_pwrctrl_destroy_device(child);
- }
- EXPORT_SYMBOL_GPL(pci_pwrctrl_destroy_devices);
- MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@linaro.org>");
- MODULE_DESCRIPTION("PCI Device Power Control core driver");
- MODULE_LICENSE("GPL");
|