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
This commit is contained in:
Bjorn Helgaas 2026-02-06 17:09:24 -06:00
commit bf37448d9b
14 changed files with 527 additions and 318 deletions

View File

@ -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
@ -362,24 +361,6 @@ void pci_bus_add_device(struct pci_dev *dev)
/* Save config space for error recoverability */
pci_save_state(dev);
/*
* If the PCI device is associated with a pwrctrl device with a
* power supply, create a device link between the PCI device and
* pwrctrl device. This ensures that pwrctrl drivers are probed
* before PCI client drivers.
*/
pdev = of_find_device_by_node(dn);
if (pdev) {
if (of_pci_supply_present(dn)) {
if (!device_link_add(&dev->dev, &pdev->dev,
DL_FLAG_AUTOREMOVE_CONSUMER)) {
pci_err(dev, "failed to add device link to power control device %s\n",
pdev->name);
}
}
put_device(&pdev->dev);
}
/*
* Enable runtime PM, which potentially allows the device to
* suspend immediately, only after the PCI state has been

View File

@ -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 = {

View File

@ -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;

View File

@ -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;
}

View File

@ -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 */

View File

@ -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;

View File

@ -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

View File

@ -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");

View File

@ -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;
ret = devm_pci_pwrctrl_device_set_ready(dev, &data->ctx);
pci_pwrctrl_init(&pwrseq->pwrctrl, dev);
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");

View File

@ -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);
regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies);
gpiod_set_value(tc9563->reset_gpio, 1);
regulator_bulk_disable(ARRAY_SIZE(tc9563->supplies), tc9563->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[] = {

View File

@ -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;
}
ret = devm_add_action_or_reset(dev, devm_pci_pwrctrl_slot_power_off,
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),
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");
}
pci_pwrctrl_init(&slot->ctx, dev);
skip_resources:
slot->pwrctrl.power_on = slot_pwrctrl_power_on;
slot->pwrctrl.power_off = slot_pwrctrl_power_off;
ret = devm_pci_pwrctrl_device_set_ready(dev, &slot->ctx);
ret = devm_add_action_or_reset(dev, devm_slot_pwrctrl_release, slot);
if (ret)
return ret;
pci_pwrctrl_init(&slot->pwrctrl, dev);
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");

View File

@ -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);
}

View File

@ -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__ */

View File

@ -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);
};
/*