summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjorn Helgaas <bhelgaas@google.com>2026-02-06 17:09:24 -0600
committerBjorn Helgaas <bhelgaas@google.com>2026-02-06 17:09:24 -0600
commitbf37448d9b7793544904ccf21e5844b6ff4af3c0 (patch)
tree84cd99bdac41ec05c0c9de9e1e53a9ea4e92cf9b
parent65a5ac66cd975d61e674f5633755e68c432888be (diff)
parent9db826206f9b7c3b5449848e79adea4756a1605a (diff)
Merge branch 'pci/pwrctrl'
- Rename pwrseq, tc9563, and slot driver structs, variables, and functions for consistency (Bjorn Helgaas) - Add power_on/off callbacks with generic signature to pwrseq, tc9563, and slot drivers so they can be used by pwrctrl core (Manivannan Sadhasivam) - Add interfaces to create and destroy pwrctrl devices (Krishna Chaitanya Chundru) - Add interfaces to power devices on and off (Manivannan Sadhasivam) - Switch to pwrctrl interfaces to create, destroy, and power on/off devices, calling them from host controller drivers instead of the PCI core (Manivannan Sadhasivam) - Drop qcom .assert_perst() callbacks since this is now done by the controller driver instead of the pwrctrl driver (Manivannan Sadhasivam) - Add PCIe M.2 connector support to the slot pwrctrl driver (Manivannan Sadhasivam) - Create pwrctrl devices for devicetree PCIe M.2 connector nodes (Manivannan Sadhasivam) * pci/pwrctrl: PCI/pwrctrl: Create pwrctrl device if graph port is found PCI/pwrctrl: Add PCIe M.2 connector support PCI: Drop the assert_perst() callback PCI: qcom: Drop the assert_perst() callbacks PCI/pwrctrl: Switch to pwrctrl create, power on/off, destroy APIs PCI/pwrctrl: Add APIs to power on/off pwrctrl devices PCI/pwrctrl: Add APIs to create, destroy pwrctrl devices PCI/pwrctrl: Add 'struct pci_pwrctrl::power_{on/off}' callbacks PCI/pwrctrl: pwrseq: Factor out power on/off code to helpers PCI/pwrctrl: slot: Factor out power on/off code to helpers PCI/pwrctrl: tc9563: Rename private struct and pointers for consistency PCI/pwrctrl: tc9563: Add local variables to reduce repetition PCI/pwrctrl: tc9563: Clean up whitespace PCI/pwrctrl: tc9563: Use put_device() instead of i2c_put_adapter() PCI/pwrctrl: slot: Rename private struct and pointers for consistency PCI/pwrctrl: pwrseq: Rename private struct and pointers for consistency # Conflicts: # drivers/pci/bus.c
-rw-r--r--drivers/pci/bus.c19
-rw-r--r--drivers/pci/controller/dwc/pcie-designware-host.c9
-rw-r--r--drivers/pci/controller/dwc/pcie-designware.h9
-rw-r--r--drivers/pci/controller/dwc/pcie-qcom.c37
-rw-r--r--drivers/pci/of.c1
-rw-r--r--drivers/pci/probe.c59
-rw-r--r--drivers/pci/pwrctrl/Kconfig1
-rw-r--r--drivers/pci/pwrctrl/core.c260
-rw-r--r--drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c84
-rw-r--r--drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c226
-rw-r--r--drivers/pci/pwrctrl/slot.c101
-rw-r--r--drivers/pci/remove.c20
-rw-r--r--include/linux/pci-pwrctrl.h16
-rw-r--r--include/linux/pci.h1
14 files changed, 526 insertions, 317 deletions
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 41e5c45e38b5..51af9e6c541c 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -345,7 +345,6 @@ void __weak pcibios_bus_add_device(struct pci_dev *pdev) { }
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
@@ -363,24 +362,6 @@ void pci_bus_add_device(struct pci_dev *dev)
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);
- }
-
- /*
* Enable runtime PM, which potentially allows the device to
* suspend immediately, only after the PCI state has been
* configured completely.
diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c
index 372207c33a85..4862a3a059c7 100644
--- a/drivers/pci/controller/dwc/pcie-designware-host.c
+++ b/drivers/pci/controller/dwc/pcie-designware-host.c
@@ -857,19 +857,10 @@ static void __iomem *dw_pcie_ecam_conf_map_bus(struct pci_bus *bus, unsigned int
return pci->dbi_base + where;
}
-static int dw_pcie_op_assert_perst(struct pci_bus *bus, bool assert)
-{
- struct dw_pcie_rp *pp = bus->sysdata;
- struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
-
- return dw_pcie_assert_perst(pci, assert);
-}
-
static struct pci_ops dw_pcie_ops = {
.map_bus = dw_pcie_own_conf_map_bus,
.read = pci_generic_config_read,
.write = pci_generic_config_write,
- .assert_perst = dw_pcie_op_assert_perst,
};
static struct pci_ops dw_pcie_ecam_ops = {
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index 31685951a080..da32bb5f936c 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -493,7 +493,6 @@ struct dw_pcie_ops {
enum dw_pcie_ltssm (*get_ltssm)(struct dw_pcie *pcie);
int (*start_link)(struct dw_pcie *pcie);
void (*stop_link)(struct dw_pcie *pcie);
- int (*assert_perst)(struct dw_pcie *pcie, bool assert);
};
struct debugfs_info {
@@ -798,14 +797,6 @@ static inline void dw_pcie_stop_link(struct dw_pcie *pci)
pci->ops->stop_link(pci);
}
-static inline int dw_pcie_assert_perst(struct dw_pcie *pci, bool assert)
-{
- if (pci->ops && pci->ops->assert_perst)
- return pci->ops->assert_perst(pci, assert);
-
- return 0;
-}
-
static inline enum dw_pcie_ltssm dw_pcie_get_ltssm(struct dw_pcie *pci)
{
u32 val;
diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
index 7b92e7a1c0d9..2a47f71d936a 100644
--- a/drivers/pci/controller/dwc/pcie-qcom.c
+++ b/drivers/pci/controller/dwc/pcie-qcom.c
@@ -24,6 +24,7 @@
#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>
@@ -641,18 +642,6 @@ static int qcom_pcie_post_init_1_0_0(struct qcom_pcie *pcie)
return 0;
}
-static int qcom_pcie_assert_perst(struct dw_pcie *pci, bool assert)
-{
- struct qcom_pcie *pcie = to_qcom_pcie(pci);
-
- if (assert)
- qcom_ep_reset_assert(pcie);
- else
- qcom_ep_reset_deassert(pcie);
-
- return 0;
-}
-
static void qcom_pcie_2_3_2_ltssm_enable(struct qcom_pcie *pcie)
{
u32 val;
@@ -1310,10 +1299,18 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
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);
@@ -1328,6 +1325,11 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
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:
@@ -1342,6 +1344,12 @@ static void qcom_pcie_host_deinit(struct dw_pcie_rp *pp)
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);
}
@@ -1494,7 +1502,6 @@ static const struct qcom_pcie_cfg cfg_fw_managed = {
static const struct dw_pcie_ops dw_pcie_ops = {
.link_up = qcom_pcie_link_up,
.start_link = qcom_pcie_start_link,
- .assert_perst = qcom_pcie_assert_perst,
};
static int qcom_pcie_icc_init(struct qcom_pcie *pcie)
@@ -1961,7 +1968,7 @@ static int qcom_pcie_probe(struct platform_device *pdev)
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;
}
diff --git a/drivers/pci/of.c b/drivers/pci/of.c
index 3579265f1198..9bb5f258759b 100644
--- a/drivers/pci/of.c
+++ b/drivers/pci/of.c
@@ -867,6 +867,7 @@ bool of_pci_supply_present(struct device_node *np)
return false;
}
+EXPORT_SYMBOL_GPL(of_pci_supply_present);
#endif /* CONFIG_PCI */
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index c791bca2891f..3868b23e0f94 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -2596,56 +2596,6 @@ bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
}
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.
@@ -2655,15 +2605,6 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)
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;
diff --git a/drivers/pci/pwrctrl/Kconfig b/drivers/pci/pwrctrl/Kconfig
index e0f999f299bb..cd3aa15bad00 100644
--- a/drivers/pci/pwrctrl/Kconfig
+++ b/drivers/pci/pwrctrl/Kconfig
@@ -13,6 +13,7 @@ config PCI_PWRCTRL_PWRSEQ
config PCI_PWRCTRL_SLOT
tristate "PCI Power Control driver for PCI slots"
+ select POWER_SEQUENCING
select PCI_PWRCTRL
help
Say Y here to enable the PCI Power Control driver to control the power
diff --git a/drivers/pci/pwrctrl/core.c b/drivers/pci/pwrctrl/core.c
index 6bdbfed584d6..6f7dea6746e0 100644
--- a/drivers/pci/pwrctrl/core.c
+++ b/drivers/pci/pwrctrl/core.c
@@ -3,14 +3,22 @@
* 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)
{
@@ -38,16 +46,6 @@ static int pci_pwrctrl_notify(struct notifier_block *nb, unsigned long action,
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
*
@@ -57,7 +55,7 @@ static void rescan_work_func(struct work_struct *work)
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);
@@ -87,8 +85,6 @@ int pci_pwrctrl_device_set_ready(struct pci_pwrctrl *pwrctrl)
if (ret)
return ret;
- schedule_work(&pwrctrl->work);
-
return 0;
}
EXPORT_SYMBOL_GPL(pci_pwrctrl_device_set_ready);
@@ -101,8 +97,6 @@ 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
@@ -145,6 +139,242 @@ int devm_pci_pwrctrl_device_set_ready(struct device *dev,
}
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);
+
+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;
+ }
+
+ /*
+ * Sanity check to make sure that the node has the compatible property
+ * to allow driver binding.
+ */
+ if (!of_property_present(np, "compatible"))
+ return 0;
+
+ /*
+ * Check whether the pwrctrl device really needs to be created or not.
+ * This is decided based on at least one of the power supplies defined
+ * in the devicetree node of the device or the graph property.
+ */
+ if (!of_pci_supply_present(np) && !of_graph_is_present(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");
diff --git a/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c b/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c
index 4e664e7b8dd2..0d0377283c37 100644
--- a/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c
+++ b/drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c
@@ -13,12 +13,12 @@
#include <linux/slab.h>
#include <linux/types.h>
-struct pci_pwrctrl_pwrseq_data {
- struct pci_pwrctrl ctx;
+struct pwrseq_pwrctrl {
+ struct pci_pwrctrl pwrctrl;
struct pwrseq_desc *pwrseq;
};
-struct pci_pwrctrl_pwrseq_pdata {
+struct pwrseq_pwrctrl_pdata {
const char *target;
/*
* Called before doing anything else to perform device-specific
@@ -27,7 +27,7 @@ struct pci_pwrctrl_pwrseq_pdata {
int (*validate_device)(struct device *dev);
};
-static int pci_pwrctrl_pwrseq_qcm_wcn_validate_device(struct device *dev)
+static int pwrseq_pwrctrl_qcm_wcn_validate_device(struct device *dev)
{
/*
* Old device trees for some platforms already define wifi nodes for
@@ -47,22 +47,38 @@ static int pci_pwrctrl_pwrseq_qcm_wcn_validate_device(struct device *dev)
return 0;
}
-static const struct pci_pwrctrl_pwrseq_pdata pci_pwrctrl_pwrseq_qcom_wcn_pdata = {
+static const struct pwrseq_pwrctrl_pdata pwrseq_pwrctrl_qcom_wcn_pdata = {
.target = "wlan",
- .validate_device = pci_pwrctrl_pwrseq_qcm_wcn_validate_device,
+ .validate_device = pwrseq_pwrctrl_qcm_wcn_validate_device,
};
-static void devm_pci_pwrctrl_pwrseq_power_off(void *data)
+static int pwrseq_pwrctrl_power_on(struct pci_pwrctrl *pwrctrl)
{
- struct pwrseq_desc *pwrseq = data;
+ struct pwrseq_pwrctrl *pwrseq = container_of(pwrctrl,
+ struct pwrseq_pwrctrl, pwrctrl);
- pwrseq_power_off(pwrseq);
+ return pwrseq_power_on(pwrseq->pwrseq);
}
-static int pci_pwrctrl_pwrseq_probe(struct platform_device *pdev)
+static int pwrseq_pwrctrl_power_off(struct pci_pwrctrl *pwrctrl)
{
- const struct pci_pwrctrl_pwrseq_pdata *pdata;
- struct pci_pwrctrl_pwrseq_data *data;
+ struct pwrseq_pwrctrl *pwrseq = container_of(pwrctrl,
+ struct pwrseq_pwrctrl, pwrctrl);
+
+ return pwrseq_power_off(pwrseq->pwrseq);
+}
+
+static void devm_pwrseq_pwrctrl_power_off(void *data)
+{
+ struct pwrseq_pwrctrl *pwrseq = data;
+
+ pwrseq_pwrctrl_power_off(&pwrseq->pwrctrl);
+}
+
+static int pwrseq_pwrctrl_probe(struct platform_device *pdev)
+{
+ const struct pwrseq_pwrctrl_pdata *pdata;
+ struct pwrseq_pwrctrl *pwrseq;
struct device *dev = &pdev->dev;
int ret;
@@ -76,28 +92,26 @@ static int pci_pwrctrl_pwrseq_probe(struct platform_device *pdev)
return ret;
}
- data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
- if (!data)
+ pwrseq = devm_kzalloc(dev, sizeof(*pwrseq), GFP_KERNEL);
+ if (!pwrseq)
return -ENOMEM;
- data->pwrseq = devm_pwrseq_get(dev, pdata->target);
- if (IS_ERR(data->pwrseq))
- return dev_err_probe(dev, PTR_ERR(data->pwrseq),
+ pwrseq->pwrseq = devm_pwrseq_get(dev, pdata->target);
+ if (IS_ERR(pwrseq->pwrseq))
+ return dev_err_probe(dev, PTR_ERR(pwrseq->pwrseq),
"Failed to get the power sequencer\n");
- ret = pwrseq_power_on(data->pwrseq);
- if (ret)
- return dev_err_probe(dev, ret,
- "Failed to power-on the device\n");
-
- ret = devm_add_action_or_reset(dev, devm_pci_pwrctrl_pwrseq_power_off,
- data->pwrseq);
+ ret = devm_add_action_or_reset(dev, devm_pwrseq_pwrctrl_power_off,
+ pwrseq);
if (ret)
return ret;
- pci_pwrctrl_init(&data->ctx, dev);
+ pwrseq->pwrctrl.power_on = pwrseq_pwrctrl_power_on;
+ pwrseq->pwrctrl.power_off = pwrseq_pwrctrl_power_off;
+
+ pci_pwrctrl_init(&pwrseq->pwrctrl, dev);
- ret = devm_pci_pwrctrl_device_set_ready(dev, &data->ctx);
+ ret = devm_pci_pwrctrl_device_set_ready(dev, &pwrseq->pwrctrl);
if (ret)
return dev_err_probe(dev, ret,
"Failed to register the pwrctrl wrapper\n");
@@ -105,34 +119,34 @@ static int pci_pwrctrl_pwrseq_probe(struct platform_device *pdev)
return 0;
}
-static const struct of_device_id pci_pwrctrl_pwrseq_of_match[] = {
+static const struct of_device_id pwrseq_pwrctrl_of_match[] = {
{
/* ATH11K in QCA6390 package. */
.compatible = "pci17cb,1101",
- .data = &pci_pwrctrl_pwrseq_qcom_wcn_pdata,
+ .data = &pwrseq_pwrctrl_qcom_wcn_pdata,
},
{
/* ATH11K in WCN6855 package. */
.compatible = "pci17cb,1103",
- .data = &pci_pwrctrl_pwrseq_qcom_wcn_pdata,
+ .data = &pwrseq_pwrctrl_qcom_wcn_pdata,
},
{
/* ATH12K in WCN7850 package. */
.compatible = "pci17cb,1107",
- .data = &pci_pwrctrl_pwrseq_qcom_wcn_pdata,
+ .data = &pwrseq_pwrctrl_qcom_wcn_pdata,
},
{ }
};
-MODULE_DEVICE_TABLE(of, pci_pwrctrl_pwrseq_of_match);
+MODULE_DEVICE_TABLE(of, pwrseq_pwrctrl_of_match);
-static struct platform_driver pci_pwrctrl_pwrseq_driver = {
+static struct platform_driver pwrseq_pwrctrl_driver = {
.driver = {
.name = "pci-pwrctrl-pwrseq",
- .of_match_table = pci_pwrctrl_pwrseq_of_match,
+ .of_match_table = pwrseq_pwrctrl_of_match,
},
- .probe = pci_pwrctrl_pwrseq_probe,
+ .probe = pwrseq_pwrctrl_probe,
};
-module_platform_driver(pci_pwrctrl_pwrseq_driver);
+module_platform_driver(pwrseq_pwrctrl_driver);
MODULE_AUTHOR("Bartosz Golaszewski <bartosz.golaszewski@linaro.org>");
MODULE_DESCRIPTION("Generic PCI Power Control module for power sequenced devices");
diff --git a/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c b/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c
index ec423432ac65..488e1ec34a7f 100644
--- a/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c
+++ b/drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c
@@ -59,7 +59,7 @@
#define TC9563_POWER_CONTROL_OVREN 0x82b2c8
#define TC9563_GPIO_MASK 0xfffffff3
-#define TC9563_GPIO_DEASSERT_BITS 0xc /* Bits to clear for GPIO deassert */
+#define TC9563_GPIO_DEASSERT_BITS 0xc /* Clear to deassert GPIO */
#define TC9563_TX_MARGIN_MIN_UA 400000
@@ -69,7 +69,7 @@
*/
#define TC9563_OSC_STAB_DELAY_US (10 * USEC_PER_MSEC)
-#define TC9563_L0S_L1_DELAY_UNIT_NS 256 /* Each unit represents 256 nanoseconds */
+#define TC9563_L0S_L1_DELAY_UNIT_NS 256 /* Each unit represents 256 ns */
struct tc9563_pwrctrl_reg_setting {
unsigned int offset;
@@ -105,13 +105,13 @@ static const char *const tc9563_supply_names[TC9563_PWRCTL_MAX_SUPPLY] = {
"vddio18",
};
-struct tc9563_pwrctrl_ctx {
+struct tc9563_pwrctrl {
+ struct pci_pwrctrl pwrctrl;
struct regulator_bulk_data supplies[TC9563_PWRCTL_MAX_SUPPLY];
struct tc9563_pwrctrl_cfg cfg[TC9563_MAX];
struct gpio_desc *reset_gpio;
struct i2c_adapter *adapter;
struct i2c_client *client;
- struct pci_pwrctrl pwrctrl;
};
/*
@@ -217,7 +217,8 @@ static int tc9563_pwrctrl_i2c_read(struct i2c_client *client,
}
static int tc9563_pwrctrl_i2c_bulk_write(struct i2c_client *client,
- const struct tc9563_pwrctrl_reg_setting *seq, int len)
+ const struct tc9563_pwrctrl_reg_setting *seq,
+ int len)
{
int ret, i;
@@ -230,10 +231,10 @@ static int tc9563_pwrctrl_i2c_bulk_write(struct i2c_client *client,
return 0;
}
-static int tc9563_pwrctrl_disable_port(struct tc9563_pwrctrl_ctx *ctx,
+static int tc9563_pwrctrl_disable_port(struct tc9563_pwrctrl *tc9563,
enum tc9563_pwrctrl_ports port)
{
- struct tc9563_pwrctrl_cfg *cfg = &ctx->cfg[port];
+ struct tc9563_pwrctrl_cfg *cfg = &tc9563->cfg[port];
const struct tc9563_pwrctrl_reg_setting *seq;
int ret, len;
@@ -248,16 +249,17 @@ static int tc9563_pwrctrl_disable_port(struct tc9563_pwrctrl_ctx *ctx,
len = ARRAY_SIZE(dsp2_pwroff_seq);
}
- ret = tc9563_pwrctrl_i2c_bulk_write(ctx->client, seq, len);
+ ret = tc9563_pwrctrl_i2c_bulk_write(tc9563->client, seq, len);
if (ret)
return ret;
- return tc9563_pwrctrl_i2c_bulk_write(ctx->client,
- common_pwroff_seq, ARRAY_SIZE(common_pwroff_seq));
+ return tc9563_pwrctrl_i2c_bulk_write(tc9563->client, common_pwroff_seq,
+ ARRAY_SIZE(common_pwroff_seq));
}
-static int tc9563_pwrctrl_set_l0s_l1_entry_delay(struct tc9563_pwrctrl_ctx *ctx,
- enum tc9563_pwrctrl_ports port, bool is_l1, u32 ns)
+static int tc9563_pwrctrl_set_l0s_l1_entry_delay(struct tc9563_pwrctrl *tc9563,
+ enum tc9563_pwrctrl_ports port,
+ bool is_l1, u32 ns)
{
u32 rd_val, units;
int ret;
@@ -269,30 +271,38 @@ static int tc9563_pwrctrl_set_l0s_l1_entry_delay(struct tc9563_pwrctrl_ctx *ctx,
units = ns / TC9563_L0S_L1_DELAY_UNIT_NS;
if (port == TC9563_ETHERNET) {
- ret = tc9563_pwrctrl_i2c_read(ctx->client, TC9563_EMBEDDED_ETH_DELAY, &rd_val);
+ ret = tc9563_pwrctrl_i2c_read(tc9563->client,
+ TC9563_EMBEDDED_ETH_DELAY,
+ &rd_val);
if (ret)
return ret;
if (is_l1)
- rd_val = u32_replace_bits(rd_val, units, TC9563_ETH_L1_DELAY_MASK);
+ rd_val = u32_replace_bits(rd_val, units,
+ TC9563_ETH_L1_DELAY_MASK);
else
- rd_val = u32_replace_bits(rd_val, units, TC9563_ETH_L0S_DELAY_MASK);
+ rd_val = u32_replace_bits(rd_val, units,
+ TC9563_ETH_L0S_DELAY_MASK);
- return tc9563_pwrctrl_i2c_write(ctx->client, TC9563_EMBEDDED_ETH_DELAY, rd_val);
+ return tc9563_pwrctrl_i2c_write(tc9563->client,
+ TC9563_EMBEDDED_ETH_DELAY,
+ rd_val);
}
- ret = tc9563_pwrctrl_i2c_write(ctx->client, TC9563_PORT_SELECT, BIT(port));
+ ret = tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_PORT_SELECT,
+ BIT(port));
if (ret)
return ret;
- return tc9563_pwrctrl_i2c_write(ctx->client,
- is_l1 ? TC9563_PORT_L1_DELAY : TC9563_PORT_L0S_DELAY, units);
+ return tc9563_pwrctrl_i2c_write(tc9563->client,
+ is_l1 ? TC9563_PORT_L1_DELAY : TC9563_PORT_L0S_DELAY,
+ units);
}
-static int tc9563_pwrctrl_set_tx_amplitude(struct tc9563_pwrctrl_ctx *ctx,
+static int tc9563_pwrctrl_set_tx_amplitude(struct tc9563_pwrctrl *tc9563,
enum tc9563_pwrctrl_ports port)
{
- u32 amp = ctx->cfg[port].tx_amp;
+ u32 amp = tc9563->cfg[port].tx_amp;
int port_access;
if (amp < TC9563_TX_MARGIN_MIN_UA)
@@ -321,13 +331,14 @@ static int tc9563_pwrctrl_set_tx_amplitude(struct tc9563_pwrctrl_ctx *ctx,
{TC9563_TX_MARGIN, amp},
};
- return tc9563_pwrctrl_i2c_bulk_write(ctx->client, tx_amp_seq, ARRAY_SIZE(tx_amp_seq));
+ return tc9563_pwrctrl_i2c_bulk_write(tc9563->client, tx_amp_seq,
+ ARRAY_SIZE(tx_amp_seq));
}
-static int tc9563_pwrctrl_disable_dfe(struct tc9563_pwrctrl_ctx *ctx,
+static int tc9563_pwrctrl_disable_dfe(struct tc9563_pwrctrl *tc9563,
enum tc9563_pwrctrl_ports port)
{
- struct tc9563_pwrctrl_cfg *cfg = &ctx->cfg[port];
+ struct tc9563_pwrctrl_cfg *cfg = &tc9563->cfg[port];
int port_access, lane_access = 0x3;
u32 phy_rate = 0x21;
@@ -364,14 +375,14 @@ static int tc9563_pwrctrl_disable_dfe(struct tc9563_pwrctrl_ctx *ctx,
{TC9563_PHY_RATE_CHANGE_OVERRIDE, 0x0},
};
- return tc9563_pwrctrl_i2c_bulk_write(ctx->client,
- disable_dfe_seq, ARRAY_SIZE(disable_dfe_seq));
+ return tc9563_pwrctrl_i2c_bulk_write(tc9563->client, disable_dfe_seq,
+ ARRAY_SIZE(disable_dfe_seq));
}
-static int tc9563_pwrctrl_set_nfts(struct tc9563_pwrctrl_ctx *ctx,
+static int tc9563_pwrctrl_set_nfts(struct tc9563_pwrctrl *tc9563,
enum tc9563_pwrctrl_ports port)
{
- u8 *nfts = ctx->cfg[port].nfts;
+ u8 *nfts = tc9563->cfg[port].nfts;
struct tc9563_pwrctrl_reg_setting nfts_seq[] = {
{TC9563_NFTS_2_5_GT, nfts[0]},
{TC9563_NFTS_5_GT, nfts[1]},
@@ -381,30 +392,35 @@ static int tc9563_pwrctrl_set_nfts(struct tc9563_pwrctrl_ctx *ctx,
if (!nfts[0])
return 0;
- ret = tc9563_pwrctrl_i2c_write(ctx->client, TC9563_PORT_SELECT, BIT(port));
+ ret = tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_PORT_SELECT,
+ BIT(port));
if (ret)
return ret;
- return tc9563_pwrctrl_i2c_bulk_write(ctx->client, nfts_seq, ARRAY_SIZE(nfts_seq));
+ return tc9563_pwrctrl_i2c_bulk_write(tc9563->client, nfts_seq,
+ ARRAY_SIZE(nfts_seq));
}
-static int tc9563_pwrctrl_assert_deassert_reset(struct tc9563_pwrctrl_ctx *ctx, bool deassert)
+static int tc9563_pwrctrl_assert_deassert_reset(struct tc9563_pwrctrl *tc9563,
+ bool deassert)
{
int ret, val;
- ret = tc9563_pwrctrl_i2c_write(ctx->client, TC9563_GPIO_CONFIG, TC9563_GPIO_MASK);
+ ret = tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_GPIO_CONFIG,
+ TC9563_GPIO_MASK);
if (ret)
return ret;
val = deassert ? TC9563_GPIO_DEASSERT_BITS : 0;
- return tc9563_pwrctrl_i2c_write(ctx->client, TC9563_RESET_GPIO, val);
+ return tc9563_pwrctrl_i2c_write(tc9563->client, TC9563_RESET_GPIO, val);
}
-static int tc9563_pwrctrl_parse_device_dt(struct tc9563_pwrctrl_ctx *ctx, struct device_node *node,
+static int tc9563_pwrctrl_parse_device_dt(struct tc9563_pwrctrl *tc9563,
+ struct device_node *node,
enum tc9563_pwrctrl_ports port)
{
- struct tc9563_pwrctrl_cfg *cfg = &ctx->cfg[port];
+ struct tc9563_pwrctrl_cfg *cfg = &tc9563->cfg[port];
int ret;
/* Disable port if the status of the port is disabled. */
@@ -434,128 +450,137 @@ static int tc9563_pwrctrl_parse_device_dt(struct tc9563_pwrctrl_ctx *ctx, struct
return 0;
}
-static void tc9563_pwrctrl_power_off(struct tc9563_pwrctrl_ctx *ctx)
+static int tc9563_pwrctrl_power_off(struct pci_pwrctrl *pwrctrl)
{
- gpiod_set_value(ctx->reset_gpio, 1);
+ struct tc9563_pwrctrl *tc9563 = container_of(pwrctrl,
+ struct tc9563_pwrctrl, pwrctrl);
+
+ gpiod_set_value(tc9563->reset_gpio, 1);
+
+ regulator_bulk_disable(ARRAY_SIZE(tc9563->supplies), tc9563->supplies);
- regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+ return 0;
}
-static int tc9563_pwrctrl_bring_up(struct tc9563_pwrctrl_ctx *ctx)
+static int tc9563_pwrctrl_power_on(struct pci_pwrctrl *pwrctrl)
{
+ struct tc9563_pwrctrl *tc9563 = container_of(pwrctrl,
+ struct tc9563_pwrctrl, pwrctrl);
+ struct device *dev = tc9563->pwrctrl.dev;
struct tc9563_pwrctrl_cfg *cfg;
int ret, i;
- ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
+ ret = regulator_bulk_enable(ARRAY_SIZE(tc9563->supplies),
+ tc9563->supplies);
if (ret < 0)
- return dev_err_probe(ctx->pwrctrl.dev, ret, "cannot enable regulators\n");
+ return dev_err_probe(dev, ret, "cannot enable regulators\n");
- gpiod_set_value(ctx->reset_gpio, 0);
+ gpiod_set_value(tc9563->reset_gpio, 0);
fsleep(TC9563_OSC_STAB_DELAY_US);
- ret = tc9563_pwrctrl_assert_deassert_reset(ctx, false);
+ ret = tc9563_pwrctrl_assert_deassert_reset(tc9563, false);
if (ret)
goto power_off;
for (i = 0; i < TC9563_MAX; i++) {
- cfg = &ctx->cfg[i];
- ret = tc9563_pwrctrl_disable_port(ctx, i);
+ cfg = &tc9563->cfg[i];
+ ret = tc9563_pwrctrl_disable_port(tc9563, i);
if (ret) {
- dev_err(ctx->pwrctrl.dev, "Disabling port failed\n");
+ dev_err(dev, "Disabling port failed\n");
goto power_off;
}
- ret = tc9563_pwrctrl_set_l0s_l1_entry_delay(ctx, i, false, cfg->l0s_delay);
+ ret = tc9563_pwrctrl_set_l0s_l1_entry_delay(tc9563, i, false, cfg->l0s_delay);
if (ret) {
- dev_err(ctx->pwrctrl.dev, "Setting L0s entry delay failed\n");
+ dev_err(dev, "Setting L0s entry delay failed\n");
goto power_off;
}
- ret = tc9563_pwrctrl_set_l0s_l1_entry_delay(ctx, i, true, cfg->l1_delay);
+ ret = tc9563_pwrctrl_set_l0s_l1_entry_delay(tc9563, i, true, cfg->l1_delay);
if (ret) {
- dev_err(ctx->pwrctrl.dev, "Setting L1 entry delay failed\n");
+ dev_err(dev, "Setting L1 entry delay failed\n");
goto power_off;
}
- ret = tc9563_pwrctrl_set_tx_amplitude(ctx, i);
+ ret = tc9563_pwrctrl_set_tx_amplitude(tc9563, i);
if (ret) {
- dev_err(ctx->pwrctrl.dev, "Setting Tx amplitude failed\n");
+ dev_err(dev, "Setting Tx amplitude failed\n");
goto power_off;
}
- ret = tc9563_pwrctrl_set_nfts(ctx, i);
+ ret = tc9563_pwrctrl_set_nfts(tc9563, i);
if (ret) {
- dev_err(ctx->pwrctrl.dev, "Setting N_FTS failed\n");
+ dev_err(dev, "Setting N_FTS failed\n");
goto power_off;
}
- ret = tc9563_pwrctrl_disable_dfe(ctx, i);
+ ret = tc9563_pwrctrl_disable_dfe(tc9563, i);
if (ret) {
- dev_err(ctx->pwrctrl.dev, "Disabling DFE failed\n");
+ dev_err(dev, "Disabling DFE failed\n");
goto power_off;
}
}
- ret = tc9563_pwrctrl_assert_deassert_reset(ctx, true);
+ ret = tc9563_pwrctrl_assert_deassert_reset(tc9563, true);
if (!ret)
return 0;
power_off:
- tc9563_pwrctrl_power_off(ctx);
+ tc9563_pwrctrl_power_off(&tc9563->pwrctrl);
return ret;
}
static int tc9563_pwrctrl_probe(struct platform_device *pdev)
{
- struct pci_host_bridge *bridge = to_pci_host_bridge(pdev->dev.parent);
- struct pci_bus *bus = bridge->bus;
+ struct device_node *node = pdev->dev.of_node;
struct device *dev = &pdev->dev;
enum tc9563_pwrctrl_ports port;
- struct tc9563_pwrctrl_ctx *ctx;
+ struct tc9563_pwrctrl *tc9563;
struct device_node *i2c_node;
int ret, addr;
- ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
- if (!ctx)
+ tc9563 = devm_kzalloc(dev, sizeof(*tc9563), GFP_KERNEL);
+ if (!tc9563)
return -ENOMEM;
- ret = of_property_read_u32_index(pdev->dev.of_node, "i2c-parent", 1, &addr);
+ ret = of_property_read_u32_index(node, "i2c-parent", 1, &addr);
if (ret)
return dev_err_probe(dev, ret, "Failed to read i2c-parent property\n");
i2c_node = of_parse_phandle(dev->of_node, "i2c-parent", 0);
- ctx->adapter = of_find_i2c_adapter_by_node(i2c_node);
+ tc9563->adapter = of_find_i2c_adapter_by_node(i2c_node);
of_node_put(i2c_node);
- if (!ctx->adapter)
+ if (!tc9563->adapter)
return dev_err_probe(dev, -EPROBE_DEFER, "Failed to find I2C adapter\n");
- ctx->client = i2c_new_dummy_device(ctx->adapter, addr);
- if (IS_ERR(ctx->client)) {
+ tc9563->client = i2c_new_dummy_device(tc9563->adapter, addr);
+ if (IS_ERR(tc9563->client)) {
dev_err(dev, "Failed to create I2C client\n");
- i2c_put_adapter(ctx->adapter);
- return PTR_ERR(ctx->client);
+ put_device(&tc9563->adapter->dev);
+ return PTR_ERR(tc9563->client);
}
for (int i = 0; i < ARRAY_SIZE(tc9563_supply_names); i++)
- ctx->supplies[i].supply = tc9563_supply_names[i];
+ tc9563->supplies[i].supply = tc9563_supply_names[i];
- ret = devm_regulator_bulk_get(dev, TC9563_PWRCTL_MAX_SUPPLY, ctx->supplies);
+ ret = devm_regulator_bulk_get(dev, TC9563_PWRCTL_MAX_SUPPLY,
+ tc9563->supplies);
if (ret) {
dev_err_probe(dev, ret, "failed to get supply regulator\n");
goto remove_i2c;
}
- ctx->reset_gpio = devm_gpiod_get(dev, "resx", GPIOD_OUT_HIGH);
- if (IS_ERR(ctx->reset_gpio)) {
- ret = dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), "failed to get resx GPIO\n");
+ tc9563->reset_gpio = devm_gpiod_get(dev, "resx", GPIOD_OUT_HIGH);
+ if (IS_ERR(tc9563->reset_gpio)) {
+ ret = dev_err_probe(dev, PTR_ERR(tc9563->reset_gpio), "failed to get resx GPIO\n");
goto remove_i2c;
}
- pci_pwrctrl_init(&ctx->pwrctrl, dev);
+ pci_pwrctrl_init(&tc9563->pwrctrl, dev);
port = TC9563_USP;
- ret = tc9563_pwrctrl_parse_device_dt(ctx, pdev->dev.of_node, port);
+ ret = tc9563_pwrctrl_parse_device_dt(tc9563, node, port);
if (ret) {
dev_err(dev, "failed to parse device tree properties: %d\n", ret);
goto remove_i2c;
@@ -563,18 +588,20 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev)
/*
* Downstream ports are always children of the upstream port.
- * The first node represents DSP1, the second node represents DSP2, and so on.
+ * The first node represents DSP1, the second node represents DSP2,
+ * and so on.
*/
- for_each_child_of_node_scoped(pdev->dev.of_node, child) {
+ for_each_child_of_node_scoped(node, child) {
port++;
- ret = tc9563_pwrctrl_parse_device_dt(ctx, child, port);
+ ret = tc9563_pwrctrl_parse_device_dt(tc9563, child, port);
if (ret)
break;
/* Embedded ethernet device are under DSP3 */
if (port == TC9563_DSP3) {
for_each_child_of_node_scoped(child, child1) {
port++;
- ret = tc9563_pwrctrl_parse_device_dt(ctx, child1, port);
+ ret = tc9563_pwrctrl_parse_device_dt(tc9563,
+ child1, port);
if (ret)
break;
}
@@ -585,45 +612,32 @@ static int tc9563_pwrctrl_probe(struct platform_device *pdev)
goto remove_i2c;
}
- if (bridge->ops->assert_perst) {
- ret = bridge->ops->assert_perst(bus, true);
- if (ret)
- goto remove_i2c;
- }
+ tc9563->pwrctrl.power_on = tc9563_pwrctrl_power_on;
+ tc9563->pwrctrl.power_off = tc9563_pwrctrl_power_off;
- ret = tc9563_pwrctrl_bring_up(ctx);
- if (ret)
- goto remove_i2c;
-
- if (bridge->ops->assert_perst) {
- ret = bridge->ops->assert_perst(bus, false);
- if (ret)
- goto power_off;
- }
-
- ret = devm_pci_pwrctrl_device_set_ready(dev, &ctx->pwrctrl);
+ ret = devm_pci_pwrctrl_device_set_ready(dev, &tc9563->pwrctrl);
if (ret)
goto power_off;
- platform_set_drvdata(pdev, ctx);
-
return 0;
power_off:
- tc9563_pwrctrl_power_off(ctx);
+ tc9563_pwrctrl_power_off(&tc9563->pwrctrl);
remove_i2c:
- i2c_unregister_device(ctx->client);
- i2c_put_adapter(ctx->adapter);
+ i2c_unregister_device(tc9563->client);
+ put_device(&tc9563->adapter->dev);
return ret;
}
static void tc9563_pwrctrl_remove(struct platform_device *pdev)
{
- struct tc9563_pwrctrl_ctx *ctx = 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(ctx);
- i2c_unregister_device(ctx->client);
- i2c_put_adapter(ctx->adapter);
+ tc9563_pwrctrl_power_off(&tc9563->pwrctrl);
+ i2c_unregister_device(tc9563->client);
+ put_device(&tc9563->adapter->dev);
}
static const struct of_device_id tc9563_pwrctrl_of_match[] = {
diff --git a/drivers/pci/pwrctrl/slot.c b/drivers/pci/pwrctrl/slot.c
index 3320494b62d8..082af81efe25 100644
--- a/drivers/pci/pwrctrl/slot.c
+++ b/drivers/pci/pwrctrl/slot.c
@@ -8,36 +8,84 @@
#include <linux/device.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
+#include <linux/of_graph.h>
#include <linux/pci-pwrctrl.h>
#include <linux/platform_device.h>
+#include <linux/pwrseq/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
-struct pci_pwrctrl_slot_data {
- struct pci_pwrctrl ctx;
+struct slot_pwrctrl {
+ struct pci_pwrctrl pwrctrl;
struct regulator_bulk_data *supplies;
int num_supplies;
+ struct clk *clk;
+ struct pwrseq_desc *pwrseq;
};
-static void devm_pci_pwrctrl_slot_power_off(void *data)
+static int slot_pwrctrl_power_on(struct pci_pwrctrl *pwrctrl)
{
- struct pci_pwrctrl_slot_data *slot = data;
+ struct slot_pwrctrl *slot = container_of(pwrctrl,
+ struct slot_pwrctrl, pwrctrl);
+ int ret;
+
+ if (slot->pwrseq) {
+ pwrseq_power_on(slot->pwrseq);
+ return 0;
+ }
+
+ ret = regulator_bulk_enable(slot->num_supplies, slot->supplies);
+ if (ret < 0) {
+ dev_err(slot->pwrctrl.dev, "Failed to enable slot regulators\n");
+ return ret;
+ }
+
+ return clk_prepare_enable(slot->clk);
+}
+
+static int slot_pwrctrl_power_off(struct pci_pwrctrl *pwrctrl)
+{
+ struct slot_pwrctrl *slot = container_of(pwrctrl,
+ struct slot_pwrctrl, pwrctrl);
+
+ if (slot->pwrseq) {
+ pwrseq_power_off(slot->pwrseq);
+ return 0;
+ }
regulator_bulk_disable(slot->num_supplies, slot->supplies);
+ clk_disable_unprepare(slot->clk);
+
+ return 0;
+}
+
+static void devm_slot_pwrctrl_release(void *data)
+{
+ struct slot_pwrctrl *slot = data;
+
+ slot_pwrctrl_power_off(&slot->pwrctrl);
regulator_bulk_free(slot->num_supplies, slot->supplies);
}
-static int pci_pwrctrl_slot_probe(struct platform_device *pdev)
+static int slot_pwrctrl_probe(struct platform_device *pdev)
{
- struct pci_pwrctrl_slot_data *slot;
+ struct slot_pwrctrl *slot;
struct device *dev = &pdev->dev;
- struct clk *clk;
int ret;
slot = devm_kzalloc(dev, sizeof(*slot), GFP_KERNEL);
if (!slot)
return -ENOMEM;
+ if (of_graph_is_present(dev_of_node(dev))) {
+ slot->pwrseq = devm_pwrseq_get(dev, "pcie");
+ if (IS_ERR(slot->pwrseq))
+ return dev_err_probe(dev, PTR_ERR(slot->pwrseq),
+ "Failed to get the power sequencer\n");
+
+ goto skip_resources;
+ }
+
ret = of_regulator_bulk_get_all(dev, dev_of_node(dev),
&slot->supplies);
if (ret < 0) {
@@ -46,49 +94,46 @@ static int pci_pwrctrl_slot_probe(struct platform_device *pdev)
}
slot->num_supplies = ret;
- ret = regulator_bulk_enable(slot->num_supplies, slot->supplies);
- if (ret < 0) {
- dev_err_probe(dev, ret, "Failed to enable slot regulators\n");
- regulator_bulk_free(slot->num_supplies, slot->supplies);
- return ret;
+
+ slot->clk = devm_clk_get_optional(dev, NULL);
+ if (IS_ERR(slot->clk)) {
+ return dev_err_probe(dev, PTR_ERR(slot->clk),
+ "Failed to enable slot clock\n");
}
- ret = devm_add_action_or_reset(dev, devm_pci_pwrctrl_slot_power_off,
- slot);
+skip_resources:
+ slot->pwrctrl.power_on = slot_pwrctrl_power_on;
+ slot->pwrctrl.power_off = slot_pwrctrl_power_off;
+
+ ret = devm_add_action_or_reset(dev, devm_slot_pwrctrl_release, slot);
if (ret)
return ret;
- clk = devm_clk_get_optional_enabled(dev, NULL);
- if (IS_ERR(clk)) {
- return dev_err_probe(dev, PTR_ERR(clk),
- "Failed to enable slot clock\n");
- }
-
- pci_pwrctrl_init(&slot->ctx, dev);
+ pci_pwrctrl_init(&slot->pwrctrl, dev);
- ret = devm_pci_pwrctrl_device_set_ready(dev, &slot->ctx);
+ ret = devm_pci_pwrctrl_device_set_ready(dev, &slot->pwrctrl);
if (ret)
return dev_err_probe(dev, ret, "Failed to register pwrctrl driver\n");
return 0;
}
-static const struct of_device_id pci_pwrctrl_slot_of_match[] = {
+static const struct of_device_id slot_pwrctrl_of_match[] = {
{
.compatible = "pciclass,0604",
},
{ }
};
-MODULE_DEVICE_TABLE(of, pci_pwrctrl_slot_of_match);
+MODULE_DEVICE_TABLE(of, slot_pwrctrl_of_match);
-static struct platform_driver pci_pwrctrl_slot_driver = {
+static struct platform_driver slot_pwrctrl_driver = {
.driver = {
.name = "pci-pwrctrl-slot",
- .of_match_table = pci_pwrctrl_slot_of_match,
+ .of_match_table = slot_pwrctrl_of_match,
},
- .probe = pci_pwrctrl_slot_probe,
+ .probe = slot_pwrctrl_probe,
};
-module_platform_driver(pci_pwrctrl_slot_driver);
+module_platform_driver(slot_pwrctrl_driver);
MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
MODULE_DESCRIPTION("Generic PCI Power Control driver for PCI Slots");
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index 417a9ea59117..e9d519993853 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -17,25 +17,6 @@ static void pci_free_resources(struct pci_dev *dev)
}
}
-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);
@@ -73,7 +54,6 @@ static void pci_destroy_dev(struct pci_dev *dev)
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);
}
diff --git a/include/linux/pci-pwrctrl.h b/include/linux/pci-pwrctrl.h
index 4aefc7901cd1..1192a2527521 100644
--- a/include/linux/pci-pwrctrl.h
+++ b/include/linux/pci-pwrctrl.h
@@ -31,6 +31,8 @@ struct device_link;
/**
* struct pci_pwrctrl - PCI device power control context.
* @dev: Address of the power controlling device.
+ * @power_on: Callback to power on the power controlling device.
+ * @power_off: Callback to power off the power controlling device.
*
* An object of this type must be allocated by the PCI power control device and
* passed to the pwrctrl subsystem to trigger a bus rescan and setup a device
@@ -38,6 +40,8 @@ struct device_link;
*/
struct pci_pwrctrl {
struct device *dev;
+ int (*power_on)(struct pci_pwrctrl *pwrctrl);
+ int (*power_off)(struct pci_pwrctrl *pwrctrl);
/* private: internal use only */
struct notifier_block nb;
@@ -50,5 +54,15 @@ int pci_pwrctrl_device_set_ready(struct pci_pwrctrl *pwrctrl);
void pci_pwrctrl_device_unset_ready(struct pci_pwrctrl *pwrctrl);
int devm_pci_pwrctrl_device_set_ready(struct device *dev,
struct pci_pwrctrl *pwrctrl);
-
+#if IS_ENABLED(CONFIG_PCI_PWRCTRL)
+int pci_pwrctrl_create_devices(struct device *parent);
+void pci_pwrctrl_destroy_devices(struct device *parent);
+int pci_pwrctrl_power_on_devices(struct device *parent);
+void pci_pwrctrl_power_off_devices(struct device *parent);
+#else
+static inline int pci_pwrctrl_create_devices(struct device *parent) { return 0; }
+static void pci_pwrctrl_destroy_devices(struct device *parent) { }
+static inline int pci_pwrctrl_power_on_devices(struct device *parent) { return 0; }
+static void pci_pwrctrl_power_off_devices(struct device *parent) { }
+#endif
#endif /* __PCI_PWRCTRL_H__ */
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 09be570e8829..dd46f3d38e20 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -860,7 +860,6 @@ struct pci_ops {
void __iomem *(*map_bus)(struct pci_bus *bus, unsigned int devfn, int where);
int (*read)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val);
int (*write)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val);
- int (*assert_perst)(struct pci_bus *bus, bool assert);
};
/*