mirror of
https://github.com/torvalds/linux.git
synced 2026-05-27 08:33:17 +02:00
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:
commit
bf37448d9b
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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[] = {
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user