diff options
| author | Bjorn Helgaas <bhelgaas@google.com> | 2026-02-06 17:09:24 -0600 |
|---|---|---|
| committer | Bjorn Helgaas <bhelgaas@google.com> | 2026-02-06 17:09:24 -0600 |
| commit | bf37448d9b7793544904ccf21e5844b6ff4af3c0 (patch) | |
| tree | 84cd99bdac41ec05c0c9de9e1e53a9ea4e92cf9b | |
| parent | 65a5ac66cd975d61e674f5633755e68c432888be (diff) | |
| parent | 9db826206f9b7c3b5449848e79adea4756a1605a (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.c | 19 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-designware-host.c | 9 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-designware.h | 9 | ||||
| -rw-r--r-- | drivers/pci/controller/dwc/pcie-qcom.c | 37 | ||||
| -rw-r--r-- | drivers/pci/of.c | 1 | ||||
| -rw-r--r-- | drivers/pci/probe.c | 59 | ||||
| -rw-r--r-- | drivers/pci/pwrctrl/Kconfig | 1 | ||||
| -rw-r--r-- | drivers/pci/pwrctrl/core.c | 260 | ||||
| -rw-r--r-- | drivers/pci/pwrctrl/pci-pwrctrl-pwrseq.c | 84 | ||||
| -rw-r--r-- | drivers/pci/pwrctrl/pci-pwrctrl-tc9563.c | 226 | ||||
| -rw-r--r-- | drivers/pci/pwrctrl/slot.c | 101 | ||||
| -rw-r--r-- | drivers/pci/remove.c | 20 | ||||
| -rw-r--r-- | include/linux/pci-pwrctrl.h | 16 | ||||
| -rw-r--r-- | include/linux/pci.h | 1 |
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); }; /* |
