usb: xhci: tegra: Unlink power domain devices
This commit unlinks xhci-tegra platform device with SS/host power domain devices. Reasons for this change is - at ELPG entry, PHY sleepwalk and wake configuration need to be done before powering down SS/host partitions, and PHY need be powered off after powering down SS/host partitions. Sequence looks like roughly below: tegra_xusb_enter_elpg() -> xhci_suspend() -> enable PHY sleepwalk and wake if needed -> power down SS/host partitions -> power down PHY If SS/host power domains are linked to xhci-tegra platform device, we are not able to perform the sequence like above. This commit introduces: 1. tegra_xusb_unpowergate_partitions() to power up SS and host partitions together. If SS/host power domain devices are available, it invokes pm_runtime_get_sync() to request power driver to power up partitions; If power domain devices are not available, tegra_powergate_sequence_power_up() will be used to power up partitions. 2. tegra_xusb_powergate_partitions() to power down SS and host partitions together. If SS/host power domain devices are available, it invokes pm_runtime_put_sync() to request power driver to power down partitions; If power domain devices are not available, tegra_powergate_power_off() will be used to power down partitions. Signed-off-by: JC Kuo <jckuo@nvidia.com> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Thierry Reding <treding@nvidia.com>
This commit is contained in:
Родитель
23eca83155
Коммит
41a7426d25
|
@ -2,7 +2,7 @@
|
|||
/*
|
||||
* NVIDIA Tegra xHCI host controller driver
|
||||
*
|
||||
* Copyright (C) 2014 NVIDIA Corporation
|
||||
* Copyright (c) 2014-2020, NVIDIA CORPORATION. All rights reserved.
|
||||
* Copyright (C) 2014 Google, Inc.
|
||||
*/
|
||||
|
||||
|
@ -249,8 +249,7 @@ struct tegra_xusb {
|
|||
|
||||
struct device *genpd_dev_host;
|
||||
struct device *genpd_dev_ss;
|
||||
struct device_link *genpd_dl_host;
|
||||
struct device_link *genpd_dl_ss;
|
||||
bool use_genpd;
|
||||
|
||||
struct phy **phys;
|
||||
unsigned int num_phys;
|
||||
|
@ -821,36 +820,12 @@ static void tegra_xusb_phy_disable(struct tegra_xusb *tegra)
|
|||
|
||||
static int tegra_xusb_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct tegra_xusb *tegra = dev_get_drvdata(dev);
|
||||
|
||||
regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
|
||||
tegra_xusb_clk_disable(tegra);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_xusb_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct tegra_xusb *tegra = dev_get_drvdata(dev);
|
||||
int err;
|
||||
|
||||
err = tegra_xusb_clk_enable(tegra);
|
||||
if (err) {
|
||||
dev_err(dev, "failed to enable clocks: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = regulator_bulk_enable(tegra->soc->num_supplies, tegra->supplies);
|
||||
if (err) {
|
||||
dev_err(dev, "failed to enable regulators: %d\n", err);
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
disable_clk:
|
||||
tegra_xusb_clk_disable(tegra);
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
@ -1026,10 +1001,9 @@ static int tegra_xusb_load_firmware(struct tegra_xusb *tegra)
|
|||
static void tegra_xusb_powerdomain_remove(struct device *dev,
|
||||
struct tegra_xusb *tegra)
|
||||
{
|
||||
if (tegra->genpd_dl_ss)
|
||||
device_link_del(tegra->genpd_dl_ss);
|
||||
if (tegra->genpd_dl_host)
|
||||
device_link_del(tegra->genpd_dl_host);
|
||||
if (!tegra->use_genpd)
|
||||
return;
|
||||
|
||||
if (!IS_ERR_OR_NULL(tegra->genpd_dev_ss))
|
||||
dev_pm_domain_detach(tegra->genpd_dev_ss, true);
|
||||
if (!IS_ERR_OR_NULL(tegra->genpd_dev_host))
|
||||
|
@ -1055,20 +1029,84 @@ static int tegra_xusb_powerdomain_init(struct device *dev,
|
|||
return err;
|
||||
}
|
||||
|
||||
tegra->genpd_dl_host = device_link_add(dev, tegra->genpd_dev_host,
|
||||
DL_FLAG_PM_RUNTIME |
|
||||
DL_FLAG_STATELESS);
|
||||
if (!tegra->genpd_dl_host) {
|
||||
dev_err(dev, "adding host device link failed!\n");
|
||||
return -ENODEV;
|
||||
tegra->use_genpd = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_xusb_unpowergate_partitions(struct tegra_xusb *tegra)
|
||||
{
|
||||
struct device *dev = tegra->dev;
|
||||
int rc;
|
||||
|
||||
if (tegra->use_genpd) {
|
||||
rc = pm_runtime_get_sync(tegra->genpd_dev_ss);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "failed to enable XUSB SS partition\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = pm_runtime_get_sync(tegra->genpd_dev_host);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "failed to enable XUSB Host partition\n");
|
||||
pm_runtime_put_sync(tegra->genpd_dev_ss);
|
||||
return rc;
|
||||
}
|
||||
} else {
|
||||
rc = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBA,
|
||||
tegra->ss_clk,
|
||||
tegra->ss_rst);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "failed to enable XUSB SS partition\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBC,
|
||||
tegra->host_clk,
|
||||
tegra->host_rst);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "failed to enable XUSB Host partition\n");
|
||||
tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
tegra->genpd_dl_ss = device_link_add(dev, tegra->genpd_dev_ss,
|
||||
DL_FLAG_PM_RUNTIME |
|
||||
DL_FLAG_STATELESS);
|
||||
if (!tegra->genpd_dl_ss) {
|
||||
dev_err(dev, "adding superspeed device link failed!\n");
|
||||
return -ENODEV;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_xusb_powergate_partitions(struct tegra_xusb *tegra)
|
||||
{
|
||||
struct device *dev = tegra->dev;
|
||||
int rc;
|
||||
|
||||
if (tegra->use_genpd) {
|
||||
rc = pm_runtime_put_sync(tegra->genpd_dev_host);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "failed to disable XUSB Host partition\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = pm_runtime_put_sync(tegra->genpd_dev_ss);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "failed to disable XUSB SS partition\n");
|
||||
pm_runtime_get_sync(tegra->genpd_dev_host);
|
||||
return rc;
|
||||
}
|
||||
} else {
|
||||
rc = tegra_powergate_power_off(TEGRA_POWERGATE_XUSBC);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "failed to disable XUSB Host partition\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "failed to disable XUSB SS partition\n");
|
||||
tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBC,
|
||||
tegra->host_clk,
|
||||
tegra->host_rst);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1432,25 +1470,6 @@ static int tegra_xusb_probe(struct platform_device *pdev)
|
|||
err);
|
||||
goto put_padctl;
|
||||
}
|
||||
|
||||
err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBA,
|
||||
tegra->ss_clk,
|
||||
tegra->ss_rst);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed to enable XUSBA domain: %d\n", err);
|
||||
goto put_padctl;
|
||||
}
|
||||
|
||||
err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_XUSBC,
|
||||
tegra->host_clk,
|
||||
tegra->host_rst);
|
||||
if (err) {
|
||||
tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA);
|
||||
dev_err(&pdev->dev,
|
||||
"failed to enable XUSBC domain: %d\n", err);
|
||||
goto put_padctl;
|
||||
}
|
||||
} else {
|
||||
err = tegra_xusb_powerdomain_init(&pdev->dev, tegra);
|
||||
if (err)
|
||||
|
@ -1525,10 +1544,22 @@ static int tegra_xusb_probe(struct platform_device *pdev)
|
|||
*/
|
||||
platform_set_drvdata(pdev, tegra);
|
||||
|
||||
err = tegra_xusb_clk_enable(tegra);
|
||||
if (err) {
|
||||
dev_err(tegra->dev, "failed to enable clocks: %d\n", err);
|
||||
goto put_hcd;
|
||||
}
|
||||
|
||||
err = regulator_bulk_enable(tegra->soc->num_supplies, tegra->supplies);
|
||||
if (err) {
|
||||
dev_err(tegra->dev, "failed to enable regulators: %d\n", err);
|
||||
goto disable_clk;
|
||||
}
|
||||
|
||||
err = tegra_xusb_phy_enable(tegra);
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev, "failed to enable PHYs: %d\n", err);
|
||||
goto put_hcd;
|
||||
goto disable_regulator;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1547,30 +1578,22 @@ static int tegra_xusb_probe(struct platform_device *pdev)
|
|||
goto disable_phy;
|
||||
}
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
|
||||
if (!pm_runtime_enabled(&pdev->dev))
|
||||
err = tegra_xusb_runtime_resume(&pdev->dev);
|
||||
else
|
||||
err = pm_runtime_get_sync(&pdev->dev);
|
||||
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev, "failed to enable device: %d\n", err);
|
||||
err = tegra_xusb_unpowergate_partitions(tegra);
|
||||
if (err)
|
||||
goto free_firmware;
|
||||
}
|
||||
|
||||
tegra_xusb_config(tegra);
|
||||
|
||||
err = tegra_xusb_load_firmware(tegra);
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev, "failed to load firmware: %d\n", err);
|
||||
goto put_rpm;
|
||||
goto powergate;
|
||||
}
|
||||
|
||||
err = usb_add_hcd(tegra->hcd, tegra->xhci_irq, IRQF_SHARED);
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev, "failed to add USB HCD: %d\n", err);
|
||||
goto put_rpm;
|
||||
goto powergate;
|
||||
}
|
||||
|
||||
device_wakeup_enable(tegra->hcd->self.controller);
|
||||
|
@ -1622,24 +1645,21 @@ put_usb3:
|
|||
usb_put_hcd(xhci->shared_hcd);
|
||||
remove_usb2:
|
||||
usb_remove_hcd(tegra->hcd);
|
||||
put_rpm:
|
||||
if (!pm_runtime_status_suspended(&pdev->dev))
|
||||
tegra_xusb_runtime_suspend(&pdev->dev);
|
||||
put_hcd:
|
||||
usb_put_hcd(tegra->hcd);
|
||||
powergate:
|
||||
tegra_xusb_powergate_partitions(tegra);
|
||||
free_firmware:
|
||||
dma_free_coherent(&pdev->dev, tegra->fw.size, tegra->fw.virt,
|
||||
tegra->fw.phys);
|
||||
disable_phy:
|
||||
tegra_xusb_phy_disable(tegra);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
disable_regulator:
|
||||
regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
|
||||
disable_clk:
|
||||
tegra_xusb_clk_disable(tegra);
|
||||
put_hcd:
|
||||
usb_put_hcd(tegra->hcd);
|
||||
put_powerdomains:
|
||||
if (!of_property_read_bool(pdev->dev.of_node, "power-domains")) {
|
||||
tegra_powergate_power_off(TEGRA_POWERGATE_XUSBC);
|
||||
tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA);
|
||||
} else {
|
||||
tegra_xusb_powerdomain_remove(&pdev->dev, tegra);
|
||||
}
|
||||
tegra_xusb_powerdomain_remove(&pdev->dev, tegra);
|
||||
put_padctl:
|
||||
tegra_xusb_padctl_put(tegra->padctl);
|
||||
return err;
|
||||
|
@ -1664,15 +1684,13 @@ static int tegra_xusb_remove(struct platform_device *pdev)
|
|||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
if (!of_property_read_bool(pdev->dev.of_node, "power-domains")) {
|
||||
tegra_powergate_power_off(TEGRA_POWERGATE_XUSBC);
|
||||
tegra_powergate_power_off(TEGRA_POWERGATE_XUSBA);
|
||||
} else {
|
||||
tegra_xusb_powerdomain_remove(&pdev->dev, tegra);
|
||||
}
|
||||
tegra_xusb_powergate_partitions(tegra);
|
||||
|
||||
tegra_xusb_powerdomain_remove(&pdev->dev, tegra);
|
||||
|
||||
tegra_xusb_phy_disable(tegra);
|
||||
|
||||
tegra_xusb_clk_disable(tegra);
|
||||
regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
|
||||
tegra_xusb_padctl_put(tegra->padctl);
|
||||
|
||||
return 0;
|
||||
|
|
Загрузка…
Ссылка в новой задаче