Adopt pwrctrl APIs to create, power on/off, and destroy pwrctrl devices.
In qcom_pcie_host_init(), call pci_pwrctrl_create_devices() to create
devices, then pci_pwrctrl_power_on_devices() to power them on, both after
controller resource initialization. Once successful, deassert PERST# for
all devices.
In qcom_pcie_host_deinit(), call pci_pwrctrl_power_off_devices() after
asserting PERST#. Note that pci_pwrctrl_destroy_devices() is not called
here, as deinit is only invoked during system suspend where device
destruction is unnecessary. If the driver becomes removable in future,
pci_pwrctrl_destroy_devices() should be called in the remove() handler.
Remove the old pwrctrl framework code from the PCI core (including
devlinks) as the new APIs are now the sole consumer of pwrctrl
functionality. And also do not power on the pwrctrl drivers during probe()
as this is now handled by the APIs.
Co-developed-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@oss.qualcomm.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Tested-by: Chen-Yu Tsai <wenst@chromium.org>
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Link: https://patch.msgid.link/20260115-pci-pwrctrl-rework-v5-12-9d26da3ce903@oss.qualcomm.com
void pci_bus_add_device(struct pci_dev *dev)
{
struct device_node *dn = dev->dev.of_node;
- struct platform_device *pdev;
/*
* Can not put in pci_device_add yet because resources
/* Save config space for error recoverability */
pci_save_state(dev);
- /*
- * If the PCI device is associated with a pwrctrl device with a
- * power supply, create a device link between the PCI device and
- * pwrctrl device. This ensures that pwrctrl drivers are probed
- * before PCI client drivers.
- */
- pdev = of_find_device_by_node(dn);
- if (pdev) {
- if (of_pci_supply_present(dn)) {
- if (!device_link_add(&dev->dev, &pdev->dev,
- DL_FLAG_AUTOREMOVE_CONSUMER)) {
- pci_err(dev, "failed to add device link to power control device %s\n",
- pdev->name);
- }
- }
- put_device(&pdev->dev);
- }
-
if (!dn || of_device_is_available(dn))
pci_dev_allow_binding(dev);
#include <linux/of_pci.h>
#include <linux/pci.h>
#include <linux/pci-ecam.h>
+#include <linux/pci-pwrctrl.h>
#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
#include <linux/platform_device.h>
if (ret)
goto err_deinit;
+ ret = pci_pwrctrl_create_devices(pci->dev);
+ if (ret)
+ goto err_disable_phy;
+
+ ret = pci_pwrctrl_power_on_devices(pci->dev);
+ if (ret)
+ goto err_pwrctrl_destroy;
+
if (pcie->cfg->ops->post_init) {
ret = pcie->cfg->ops->post_init(pcie);
if (ret)
- goto err_disable_phy;
+ goto err_pwrctrl_power_off;
}
qcom_ep_reset_deassert(pcie);
err_assert_reset:
qcom_ep_reset_assert(pcie);
+err_pwrctrl_power_off:
+ pci_pwrctrl_power_off_devices(pci->dev);
+err_pwrctrl_destroy:
+ if (ret != -EPROBE_DEFER)
+ pci_pwrctrl_destroy_devices(pci->dev);
err_disable_phy:
qcom_pcie_phy_power_off(pcie);
err_deinit:
struct qcom_pcie *pcie = to_qcom_pcie(pci);
qcom_ep_reset_assert(pcie);
+
+ /*
+ * No need to destroy pwrctrl devices as this function only gets called
+ * during system suspend as of now.
+ */
+ pci_pwrctrl_power_off_devices(pci->dev);
qcom_pcie_phy_power_off(pcie);
pcie->cfg->ops->deinit(pcie);
}
ret = dw_pcie_host_init(pp);
if (ret) {
- dev_err(dev, "cannot initialize host\n");
+ dev_err_probe(dev, ret, "cannot initialize host\n");
goto err_phy_exit;
}
}
EXPORT_SYMBOL(pci_bus_read_dev_vendor_id);
-#if IS_ENABLED(CONFIG_PCI_PWRCTRL)
-static struct platform_device *pci_pwrctrl_create_device(struct pci_bus *bus, int devfn)
-{
- struct pci_host_bridge *host = pci_find_host_bridge(bus);
- struct platform_device *pdev;
- struct device_node *np;
-
- np = of_pci_find_child_device(dev_of_node(&bus->dev), devfn);
- if (!np)
- return NULL;
-
- pdev = of_find_device_by_node(np);
- if (pdev) {
- put_device(&pdev->dev);
- goto err_put_of_node;
- }
-
- /*
- * First check whether the pwrctrl device really needs to be created or
- * not. This is decided based on at least one of the power supplies
- * being defined in the devicetree node of the device.
- */
- if (!of_pci_supply_present(np)) {
- pr_debug("PCI/pwrctrl: Skipping OF node: %s\n", np->name);
- goto err_put_of_node;
- }
-
- /* Now create the pwrctrl device */
- pdev = of_platform_device_create(np, NULL, &host->dev);
- if (!pdev) {
- pr_err("PCI/pwrctrl: Failed to create pwrctrl device for node: %s\n", np->name);
- goto err_put_of_node;
- }
-
- of_node_put(np);
-
- return pdev;
-
-err_put_of_node:
- of_node_put(np);
-
- return NULL;
-}
-#else
-static struct platform_device *pci_pwrctrl_create_device(struct pci_bus *bus, int devfn)
-{
- return NULL;
-}
-#endif
-
/*
* Read the config data for a PCI device, sanity-check it,
* and fill in the dev structure.
struct pci_dev *dev;
u32 l;
- /*
- * Create pwrctrl device (if required) for the PCI device to handle the
- * power state. If the pwrctrl device is created, then skip scanning
- * further as the pwrctrl core will rescan the bus after powering on
- * the device.
- */
- if (pci_pwrctrl_create_device(bus, devfn))
- return NULL;
-
if (!pci_bus_read_dev_vendor_id(bus, devfn, &l, 60*1000))
return NULL;
return NOTIFY_DONE;
}
-static void rescan_work_func(struct work_struct *work)
-{
- struct pci_pwrctrl *pwrctrl = container_of(work,
- struct pci_pwrctrl, work);
-
- pci_lock_rescan_remove();
- pci_rescan_bus(to_pci_host_bridge(pwrctrl->dev->parent)->bus);
- pci_unlock_rescan_remove();
-}
-
/**
* pci_pwrctrl_init() - Initialize the PCI power control context struct
*
void pci_pwrctrl_init(struct pci_pwrctrl *pwrctrl, struct device *dev)
{
pwrctrl->dev = dev;
- INIT_WORK(&pwrctrl->work, rescan_work_func);
dev_set_drvdata(dev, pwrctrl);
}
EXPORT_SYMBOL_GPL(pci_pwrctrl_init);
if (ret)
return ret;
- schedule_work(&pwrctrl->work);
-
return 0;
}
EXPORT_SYMBOL_GPL(pci_pwrctrl_device_set_ready);
*/
void pci_pwrctrl_device_unset_ready(struct pci_pwrctrl *pwrctrl)
{
- cancel_work_sync(&pwrctrl->work);
-
/*
* We don't have to delete the link here. Typically, this function
* is only called when the power control device is being detached. If
return dev_err_probe(dev, PTR_ERR(pwrseq->pwrseq),
"Failed to get the power sequencer\n");
- ret = pwrseq_pwrctrl_power_on(&pwrseq->pwrctrl);
- if (ret)
- return dev_err_probe(dev, ret,
- "Failed to power-on the device\n");
-
ret = devm_add_action_or_reset(dev, devm_pwrseq_pwrctrl_power_off,
pwrseq);
if (ret)
static int tc9563_pwrctrl_probe(struct platform_device *pdev)
{
- struct pci_host_bridge *bridge = to_pci_host_bridge(pdev->dev.parent);
struct device_node *node = pdev->dev.of_node;
- struct pci_bus *bus = bridge->bus;
struct device *dev = &pdev->dev;
enum tc9563_pwrctrl_ports port;
struct tc9563_pwrctrl *tc9563;
goto remove_i2c;
}
- if (bridge->ops->assert_perst) {
- ret = bridge->ops->assert_perst(bus, true);
- if (ret)
- goto remove_i2c;
- }
-
- ret = tc9563_pwrctrl_power_on(&tc9563->pwrctrl);
- if (ret)
- goto remove_i2c;
-
- if (bridge->ops->assert_perst) {
- ret = bridge->ops->assert_perst(bus, false);
- if (ret)
- goto power_off;
- }
-
tc9563->pwrctrl.power_on = tc9563_pwrctrl_power_on;
tc9563->pwrctrl.power_off = tc9563_pwrctrl_power_off;
if (ret)
goto power_off;
- platform_set_drvdata(pdev, tc9563);
-
return 0;
power_off:
static void tc9563_pwrctrl_remove(struct platform_device *pdev)
{
- struct tc9563_pwrctrl *tc9563 = platform_get_drvdata(pdev);
+ struct pci_pwrctrl *pwrctrl = dev_get_drvdata(&pdev->dev);
+ struct tc9563_pwrctrl *tc9563 = container_of(pwrctrl,
+ struct tc9563_pwrctrl, pwrctrl);
tc9563_pwrctrl_power_off(&tc9563->pwrctrl);
i2c_unregister_device(tc9563->client);
"Failed to enable slot clock\n");
}
- slot_pwrctrl_power_on(&slot->pwrctrl);
-
slot->pwrctrl.power_on = slot_pwrctrl_power_on;
slot->pwrctrl.power_off = slot_pwrctrl_power_off;
}
}
-static void pci_pwrctrl_unregister(struct device *dev)
-{
- struct device_node *np;
- struct platform_device *pdev;
-
- np = dev_of_node(dev);
- if (!np)
- return;
-
- pdev = of_find_device_by_node(np);
- if (!pdev)
- return;
-
- of_device_unregister(pdev);
- put_device(&pdev->dev);
-
- of_node_clear_flag(np, OF_POPULATED);
-}
-
static void pci_stop_dev(struct pci_dev *dev)
{
pci_pme_active(dev, false);
pci_ide_destroy(dev);
pcie_aspm_exit_link_state(dev);
pci_bridge_d3_update(dev);
- pci_pwrctrl_unregister(&dev->dev);
pci_free_resources(dev);
put_device(&dev->dev);
}