Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.4.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (C) 2024 Linaro Ltd.
  4 */
  5
  6#include <linux/device.h>
  7#include <linux/export.h>
  8#include <linux/kernel.h>
  9#include <linux/pci.h>
 10#include <linux/pci-pwrctrl.h>
 11#include <linux/property.h>
 12#include <linux/slab.h>
 13
 14static int pci_pwrctrl_notify(struct notifier_block *nb, unsigned long action,
 15			      void *data)
 16{
 17	struct pci_pwrctrl *pwrctrl = container_of(nb, struct pci_pwrctrl, nb);
 18	struct device *dev = data;
 19
 20	if (dev_fwnode(dev) != dev_fwnode(pwrctrl->dev))
 21		return NOTIFY_DONE;
 22
 23	switch (action) {
 24	case BUS_NOTIFY_ADD_DEVICE:
 25		/*
 26		 * We will have two struct device objects bound to two different
 27		 * drivers on different buses but consuming the same DT node. We
 28		 * must not bind the pins twice in this case but only once for
 29		 * the first device to be added.
 30		 *
 31		 * If we got here then the PCI device is the second after the
 32		 * power control platform device. Mark its OF node as reused.
 33		 */
 34		dev->of_node_reused = true;
 35		break;
 36	}
 37
 38	return NOTIFY_DONE;
 39}
 40
 41static void rescan_work_func(struct work_struct *work)
 42{
 43	struct pci_pwrctrl *pwrctrl = container_of(work,
 44						   struct pci_pwrctrl, work);
 45
 46	pci_lock_rescan_remove();
 47	pci_rescan_bus(to_pci_dev(pwrctrl->dev->parent)->bus);
 48	pci_unlock_rescan_remove();
 49}
 50
 51/**
 52 * pci_pwrctrl_init() - Initialize the PCI power control context struct
 53 *
 54 * @pwrctrl: PCI power control data
 55 * @dev: Parent device
 56 */
 57void pci_pwrctrl_init(struct pci_pwrctrl *pwrctrl, struct device *dev)
 58{
 59	pwrctrl->dev = dev;
 60	INIT_WORK(&pwrctrl->work, rescan_work_func);
 61}
 62EXPORT_SYMBOL_GPL(pci_pwrctrl_init);
 63
 64/**
 65 * pci_pwrctrl_device_set_ready() - Notify the pwrctrl subsystem that the PCI
 66 * device is powered-up and ready to be detected.
 67 *
 68 * @pwrctrl: PCI power control data.
 69 *
 70 * Returns:
 71 * 0 on success, negative error number on error.
 72 *
 73 * Note:
 74 * This function returning 0 doesn't mean the device was detected. It means,
 75 * that the bus rescan was successfully started. The device will get bound to
 76 * its PCI driver asynchronously.
 77 */
 78int pci_pwrctrl_device_set_ready(struct pci_pwrctrl *pwrctrl)
 79{
 80	int ret;
 81
 82	if (!pwrctrl->dev)
 83		return -ENODEV;
 84
 85	pwrctrl->nb.notifier_call = pci_pwrctrl_notify;
 86	ret = bus_register_notifier(&pci_bus_type, &pwrctrl->nb);
 87	if (ret)
 88		return ret;
 89
 90	schedule_work(&pwrctrl->work);
 91
 92	return 0;
 93}
 94EXPORT_SYMBOL_GPL(pci_pwrctrl_device_set_ready);
 95
 96/**
 97 * pci_pwrctrl_device_unset_ready() - Notify the pwrctrl subsystem that the PCI
 98 * device is about to be powered-down.
 99 *
100 * @pwrctrl: PCI power control data.
101 */
102void pci_pwrctrl_device_unset_ready(struct pci_pwrctrl *pwrctrl)
103{
104	/*
105	 * We don't have to delete the link here. Typically, this function
106	 * is only called when the power control device is being detached. If
107	 * it is being detached then the child PCI device must have already
108	 * been unbound too or the device core wouldn't let us unbind.
109	 */
110	bus_unregister_notifier(&pci_bus_type, &pwrctrl->nb);
111}
112EXPORT_SYMBOL_GPL(pci_pwrctrl_device_unset_ready);
113
114static void devm_pci_pwrctrl_device_unset_ready(void *data)
115{
116	struct pci_pwrctrl *pwrctrl = data;
117
118	pci_pwrctrl_device_unset_ready(pwrctrl);
119}
120
121/**
122 * devm_pci_pwrctrl_device_set_ready - Managed variant of
123 * pci_pwrctrl_device_set_ready().
124 *
125 * @dev: Device managing this pwrctrl provider.
126 * @pwrctrl: PCI power control data.
127 *
128 * Returns:
129 * 0 on success, negative error number on error.
130 */
131int devm_pci_pwrctrl_device_set_ready(struct device *dev,
132				      struct pci_pwrctrl *pwrctrl)
133{
134	int ret;
135
136	ret = pci_pwrctrl_device_set_ready(pwrctrl);
137	if (ret)
138		return ret;
139
140	return devm_add_action_or_reset(dev,
141					devm_pci_pwrctrl_device_unset_ready,
142					pwrctrl);
143}
144EXPORT_SYMBOL_GPL(devm_pci_pwrctrl_device_set_ready);
145
146MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@linaro.org>");
147MODULE_DESCRIPTION("PCI Device Power Control core driver");
148MODULE_LICENSE("GPL");