drivers: Changes for v5.17-rc1
This is an assortment of driver patches that rely on some of the changes in the for-5.17/soc branch. These have all been acked by the respective maintainers and go through the Tegra tree to more easily handle the build dependency. -----BEGIN PGP SIGNATURE----- iQJHBAABCAAxFiEEiOrDCAFJzPfAjcif3SOs138+s6EFAmG8sMMTHHRyZWRpbmdA bnZpZGlhLmNvbQAKCRDdI6zXfz6zoZo+EACQhJAgO3Qo/Xaxcyh5qRBAgYKCwGSR eJcuaEcinYFomXUfRb57SycZDVNTFRXnbr/2SbvNlOLVrf7gRVkan/2rdCHlc27n BNDjiCx4c0oD1crnhJriM57ILUYqRNB0YWjnfsv3oOl+ZNkzNdIHmM8hxwEY6+5U Mc2Qj7WADZe0I+dFahyvDyfgBiPuPmamK1tWeJoeXOWNhgY51NFIgvGvp6IPCJFN NVWkhm+YB93e5t5KZMNEETqNx63Ep0k31tP6LPkqKDoScpFkSZLTq5Icx92Kr6Vk e+nSJn8sdsoym2VOHl33Cx7WXIHu+crmM7y/I+BNvgafigOd6LhzA2EnzMDyPHCo 8ngd2x7vvy/CPaI45yczvPyyyptZGYPO2G9PmpFsT8ydPbMOzx0igQ3mtF8cZaeN izkcSvxRczCjN9rVhybghxktAmQihlGJLYB67sDoplLlc5QAJjH8kKSQcaLDmyAu /xgxwssSjWRtVacT7Ro5h8BYD6pmnlH9XmHCYprUQRdd446MWSl56bdw4iZXkpCM UdLnsgjHHNN41d8cKSU97KLxG2ynnPRF2mqxRDEXa8OWQfK/6H6spCkLqPgo572T N+AezZ2hqljuO0AGeixx+bExQLbqKt8becQP6CqynT9xZVkk+b2iYCTGKA04gb1l tpxRe5X1Sracow== =YfSG -----END PGP SIGNATURE----- gpgsig -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEo6/YBQwIrVS28WGKmmx57+YAGNkFAmHAbq4ACgkQmmx57+YA GNncmhAAgYwqoRCobfEDxMm+4zjFoimuZWynahKG0c/t7GcFfBQVIh4uLmTLd0bm 8sjGW7R9BcNRtbI5ZX2buN5LpZ5qDk/HAoNA+QhgWwHuLS4/FHx2bOnYOfvCWKdK elXfI8SaZ3l5DClasvt865niELpj95PHEN2Yr3CqOircgGApazN/GkrefNj3v+YL UqhDNW2XhUKjdKddBXH9n8SftHHdc9CP61c8zNUql7CikpzPAfh/ZjBuxqwF5NtZ C7XaH9uRVr6v/rWl7T5+YSSgXkZFBH/D6NWFg+VVVkZwoZJDHuPY0yv3JwBDxzQL F7WNWxT7fZrXBjFxVvQGLvP5kab4RV4nZAwkwalOZQ7EmtqpKmtwurOh/X8qGbmk IpqeprisWVeUuRQxS3vARMRr9Xvf4yRQ+Z0AIW0u/eadVrpboXvHKQZpxfviA8v2 tITsOHVB8/3hFt7t6FGMdaQhnp8Nsk9WNUv8zDwpa5cNoItZST9t0sXh+XnBKJ7N 5WH83w64N/fBuFs1/c4A3MUP+GX7V7p3IoJIQZYFLRokXz3meuw9fGjQJZWAb+Mv OiWjQS1rUAJl81hU1KWXDelHJ1vg8DMZu1MrLmw4PzgtFq8yy8HICxLuwZdcvDhR 0A8/ue+5r++XtYAfj+euhrUvNVKYXa5M9Th7LtSeATjX5r0D4UU= =lWBt -----END PGP SIGNATURE----- Merge tag 'tegra-for-5.17-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux into arm/drivers drivers: Changes for v5.17-rc1 This is an assortment of driver patches that rely on some of the changes in the for-5.17/soc branch. These have all been acked by the respective maintainers and go through the Tegra tree to more easily handle the build dependency. * tag 'tegra-for-5.17-drivers' of git://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux: media: staging: tegra-vde: Support generic power domain spi: tegra20-slink: Add OPP support mtd: rawnand: tegra: Add runtime PM and OPP support mmc: sdhci-tegra: Add runtime PM and OPP support pwm: tegra: Add runtime PM and OPP support bus: tegra-gmi: Add runtime PM and OPP support usb: chipidea: tegra: Add runtime PM and OPP support soc/tegra: Add devm_tegra_core_dev_init_opp_table_common() soc/tegra: Enable runtime PM during OPP state-syncing Link: https://lore.kernel.org/r/20211217162253.1801077-2-thierry.reding@gmail.com Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
commit
a1539b2e26
|
@ -13,8 +13,11 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include <soc/tegra/common.h>
|
||||
|
||||
#define TEGRA_GMI_CONFIG 0x00
|
||||
#define TEGRA_GMI_CONFIG_GO BIT(31)
|
||||
#define TEGRA_GMI_BUS_WIDTH_32BIT BIT(30)
|
||||
|
@ -54,9 +57,10 @@ static int tegra_gmi_enable(struct tegra_gmi *gmi)
|
|||
{
|
||||
int err;
|
||||
|
||||
err = clk_prepare_enable(gmi->clk);
|
||||
if (err < 0) {
|
||||
dev_err(gmi->dev, "failed to enable clock: %d\n", err);
|
||||
pm_runtime_enable(gmi->dev);
|
||||
err = pm_runtime_resume_and_get(gmi->dev);
|
||||
if (err) {
|
||||
pm_runtime_disable(gmi->dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -83,7 +87,9 @@ static void tegra_gmi_disable(struct tegra_gmi *gmi)
|
|||
writel(config, gmi->base + TEGRA_GMI_CONFIG);
|
||||
|
||||
reset_control_assert(gmi->rst);
|
||||
clk_disable_unprepare(gmi->clk);
|
||||
|
||||
pm_runtime_put_sync_suspend(gmi->dev);
|
||||
pm_runtime_force_suspend(gmi->dev);
|
||||
}
|
||||
|
||||
static int tegra_gmi_parse_dt(struct tegra_gmi *gmi)
|
||||
|
@ -213,6 +219,7 @@ static int tegra_gmi_probe(struct platform_device *pdev)
|
|||
if (!gmi)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, gmi);
|
||||
gmi->dev = dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
@ -232,6 +239,10 @@ static int tegra_gmi_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(gmi->rst);
|
||||
}
|
||||
|
||||
err = devm_tegra_core_dev_init_opp_table_common(&pdev->dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = tegra_gmi_parse_dt(gmi);
|
||||
if (err)
|
||||
return err;
|
||||
|
@ -247,8 +258,6 @@ static int tegra_gmi_probe(struct platform_device *pdev)
|
|||
return err;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, gmi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -262,6 +271,34 @@ static int tegra_gmi_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused tegra_gmi_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct tegra_gmi *gmi = dev_get_drvdata(dev);
|
||||
int err;
|
||||
|
||||
err = clk_prepare_enable(gmi->clk);
|
||||
if (err < 0) {
|
||||
dev_err(gmi->dev, "failed to enable clock: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused tegra_gmi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct tegra_gmi *gmi = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable_unprepare(gmi->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops tegra_gmi_pm = {
|
||||
SET_RUNTIME_PM_OPS(tegra_gmi_runtime_suspend, tegra_gmi_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
||||
static const struct of_device_id tegra_gmi_id_table[] = {
|
||||
{ .compatible = "nvidia,tegra20-gmi", },
|
||||
{ .compatible = "nvidia,tegra30-gmi", },
|
||||
|
@ -275,6 +312,7 @@ static struct platform_driver tegra_gmi_driver = {
|
|||
.driver = {
|
||||
.name = "tegra-gmi",
|
||||
.of_match_table = tegra_gmi_id_table,
|
||||
.pm = &tegra_gmi_pm,
|
||||
},
|
||||
};
|
||||
module_platform_driver(tegra_gmi_driver);
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/mmc/card.h>
|
||||
|
@ -24,6 +26,8 @@
|
|||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/ktime.h>
|
||||
|
||||
#include <soc/tegra/common.h>
|
||||
|
||||
#include "sdhci-pltfm.h"
|
||||
#include "cqhci.h"
|
||||
|
||||
|
@ -760,7 +764,9 @@ static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
{
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
|
||||
struct device *dev = mmc_dev(host->mmc);
|
||||
unsigned long host_clk;
|
||||
int err;
|
||||
|
||||
if (!clock)
|
||||
return sdhci_set_clock(host, clock);
|
||||
|
@ -778,7 +784,12 @@ static void tegra_sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
|
|||
* from clk_get_rate() is used.
|
||||
*/
|
||||
host_clk = tegra_host->ddr_signaling ? clock * 2 : clock;
|
||||
clk_set_rate(pltfm_host->clk, host_clk);
|
||||
|
||||
err = dev_pm_opp_set_rate(dev, host_clk);
|
||||
if (err)
|
||||
dev_err(dev, "failed to set clk rate to %luHz: %d\n",
|
||||
host_clk, err);
|
||||
|
||||
tegra_host->curr_clk_rate = host_clk;
|
||||
if (tegra_host->ddr_signaling)
|
||||
host->max_clk = host_clk;
|
||||
|
@ -1705,7 +1716,6 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
|
|||
"failed to get clock\n");
|
||||
goto err_clk_get;
|
||||
}
|
||||
clk_prepare_enable(clk);
|
||||
pltfm_host->clk = clk;
|
||||
|
||||
tegra_host->rst = devm_reset_control_get_exclusive(&pdev->dev,
|
||||
|
@ -1716,15 +1726,24 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
|
|||
goto err_rst_get;
|
||||
}
|
||||
|
||||
rc = reset_control_assert(tegra_host->rst);
|
||||
rc = devm_tegra_core_dev_init_opp_table_common(&pdev->dev);
|
||||
if (rc)
|
||||
goto err_rst_get;
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
rc = pm_runtime_resume_and_get(&pdev->dev);
|
||||
if (rc)
|
||||
goto err_pm_get;
|
||||
|
||||
rc = reset_control_assert(tegra_host->rst);
|
||||
if (rc)
|
||||
goto err_rst_assert;
|
||||
|
||||
usleep_range(2000, 4000);
|
||||
|
||||
rc = reset_control_deassert(tegra_host->rst);
|
||||
if (rc)
|
||||
goto err_rst_get;
|
||||
goto err_rst_assert;
|
||||
|
||||
usleep_range(2000, 4000);
|
||||
|
||||
|
@ -1736,8 +1755,11 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
|
|||
|
||||
err_add_host:
|
||||
reset_control_assert(tegra_host->rst);
|
||||
err_rst_assert:
|
||||
pm_runtime_put_sync_suspend(&pdev->dev);
|
||||
err_pm_get:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
err_rst_get:
|
||||
clk_disable_unprepare(pltfm_host->clk);
|
||||
err_clk_get:
|
||||
clk_disable_unprepare(tegra_host->tmclk);
|
||||
err_power_req:
|
||||
|
@ -1756,19 +1778,38 @@ static int sdhci_tegra_remove(struct platform_device *pdev)
|
|||
|
||||
reset_control_assert(tegra_host->rst);
|
||||
usleep_range(2000, 4000);
|
||||
clk_disable_unprepare(pltfm_host->clk);
|
||||
clk_disable_unprepare(tegra_host->tmclk);
|
||||
|
||||
pm_runtime_put_sync_suspend(&pdev->dev);
|
||||
pm_runtime_force_suspend(&pdev->dev);
|
||||
|
||||
clk_disable_unprepare(tegra_host->tmclk);
|
||||
sdhci_pltfm_free(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int __maybe_unused sdhci_tegra_suspend(struct device *dev)
|
||||
static int __maybe_unused sdhci_tegra_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
|
||||
clk_disable_unprepare(pltfm_host->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused sdhci_tegra_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
|
||||
return clk_prepare_enable(pltfm_host->clk);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int sdhci_tegra_suspend(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
if (host->mmc->caps2 & MMC_CAP2_CQE) {
|
||||
|
@ -1783,17 +1824,22 @@ static int __maybe_unused sdhci_tegra_suspend(struct device *dev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
clk_disable_unprepare(pltfm_host->clk);
|
||||
ret = pm_runtime_force_suspend(dev);
|
||||
if (ret) {
|
||||
sdhci_resume_host(host);
|
||||
cqhci_resume(host->mmc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused sdhci_tegra_resume(struct device *dev)
|
||||
static int sdhci_tegra_resume(struct device *dev)
|
||||
{
|
||||
struct sdhci_host *host = dev_get_drvdata(dev);
|
||||
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(pltfm_host->clk);
|
||||
ret = pm_runtime_force_resume(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -1812,13 +1858,16 @@ static int __maybe_unused sdhci_tegra_resume(struct device *dev)
|
|||
suspend_host:
|
||||
sdhci_suspend_host(host);
|
||||
disable_clk:
|
||||
clk_disable_unprepare(pltfm_host->clk);
|
||||
pm_runtime_force_suspend(dev);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(sdhci_tegra_dev_pm_ops, sdhci_tegra_suspend,
|
||||
sdhci_tegra_resume);
|
||||
static const struct dev_pm_ops sdhci_tegra_dev_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(sdhci_tegra_runtime_suspend, sdhci_tegra_runtime_resume,
|
||||
NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(sdhci_tegra_suspend, sdhci_tegra_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver sdhci_tegra_driver = {
|
||||
.driver = {
|
||||
|
|
|
@ -17,8 +17,11 @@
|
|||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include <soc/tegra/common.h>
|
||||
|
||||
#define COMMAND 0x00
|
||||
#define COMMAND_GO BIT(31)
|
||||
#define COMMAND_CLE BIT(30)
|
||||
|
@ -1151,6 +1154,7 @@ static int tegra_nand_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
|
||||
ctrl->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, ctrl);
|
||||
nand_controller_init(&ctrl->controller);
|
||||
ctrl->controller.ops = &tegra_nand_controller_ops;
|
||||
|
||||
|
@ -1166,14 +1170,23 @@ static int tegra_nand_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(ctrl->clk))
|
||||
return PTR_ERR(ctrl->clk);
|
||||
|
||||
err = clk_prepare_enable(ctrl->clk);
|
||||
err = devm_tegra_core_dev_init_opp_table_common(&pdev->dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* This driver doesn't support active power management yet,
|
||||
* so we will simply keep device resumed.
|
||||
*/
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
err = pm_runtime_resume_and_get(&pdev->dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = reset_control_reset(rst);
|
||||
if (err) {
|
||||
dev_err(ctrl->dev, "Failed to reset HW: %d\n", err);
|
||||
goto err_disable_clk;
|
||||
goto err_put_pm;
|
||||
}
|
||||
|
||||
writel_relaxed(HWSTATUS_CMD_DEFAULT, ctrl->regs + HWSTATUS_CMD);
|
||||
|
@ -1188,21 +1201,20 @@ static int tegra_nand_probe(struct platform_device *pdev)
|
|||
dev_name(&pdev->dev), ctrl);
|
||||
if (err) {
|
||||
dev_err(ctrl->dev, "Failed to get IRQ: %d\n", err);
|
||||
goto err_disable_clk;
|
||||
goto err_put_pm;
|
||||
}
|
||||
|
||||
writel_relaxed(DMA_MST_CTRL_IS_DONE, ctrl->regs + DMA_MST_CTRL);
|
||||
|
||||
err = tegra_nand_chips_init(ctrl->dev, ctrl);
|
||||
if (err)
|
||||
goto err_disable_clk;
|
||||
|
||||
platform_set_drvdata(pdev, ctrl);
|
||||
goto err_put_pm;
|
||||
|
||||
return 0;
|
||||
|
||||
err_disable_clk:
|
||||
clk_disable_unprepare(ctrl->clk);
|
||||
err_put_pm:
|
||||
pm_runtime_put_sync_suspend(ctrl->dev);
|
||||
pm_runtime_force_suspend(ctrl->dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -1219,11 +1231,40 @@ static int tegra_nand_remove(struct platform_device *pdev)
|
|||
|
||||
nand_cleanup(chip);
|
||||
|
||||
pm_runtime_put_sync_suspend(ctrl->dev);
|
||||
pm_runtime_force_suspend(ctrl->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused tegra_nand_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct tegra_nand_controller *ctrl = dev_get_drvdata(dev);
|
||||
int err;
|
||||
|
||||
err = clk_prepare_enable(ctrl->clk);
|
||||
if (err) {
|
||||
dev_err(dev, "Failed to enable clock: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused tegra_nand_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct tegra_nand_controller *ctrl = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable_unprepare(ctrl->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops tegra_nand_pm = {
|
||||
SET_RUNTIME_PM_OPS(tegra_nand_runtime_suspend, tegra_nand_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
||||
static const struct of_device_id tegra_nand_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra20-nand" },
|
||||
{ /* sentinel */ }
|
||||
|
@ -1234,6 +1275,7 @@ static struct platform_driver tegra_nand_driver = {
|
|||
.driver = {
|
||||
.name = "tegra-nand",
|
||||
.of_match_table = tegra_nand_of_match,
|
||||
.pm = &tegra_nand_pm,
|
||||
},
|
||||
.probe = tegra_nand_probe,
|
||||
.remove = tegra_nand_remove,
|
||||
|
|
|
@ -42,12 +42,16 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/pwm.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include <soc/tegra/common.h>
|
||||
|
||||
#define PWM_ENABLE (1 << 31)
|
||||
#define PWM_DUTY_WIDTH 8
|
||||
#define PWM_DUTY_SHIFT 16
|
||||
|
@ -145,7 +149,7 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
required_clk_rate =
|
||||
(NSEC_PER_SEC / period_ns) << PWM_DUTY_WIDTH;
|
||||
|
||||
err = clk_set_rate(pc->clk, required_clk_rate);
|
||||
err = dev_pm_opp_set_rate(pc->dev, required_clk_rate);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -181,8 +185,8 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
* before writing the register. Otherwise, keep it enabled.
|
||||
*/
|
||||
if (!pwm_is_enabled(pwm)) {
|
||||
err = clk_prepare_enable(pc->clk);
|
||||
if (err < 0)
|
||||
err = pm_runtime_resume_and_get(pc->dev);
|
||||
if (err)
|
||||
return err;
|
||||
} else
|
||||
val |= PWM_ENABLE;
|
||||
|
@ -193,7 +197,7 @@ static int tegra_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
|
|||
* If the PWM is not enabled, turn the clock off again to save power.
|
||||
*/
|
||||
if (!pwm_is_enabled(pwm))
|
||||
clk_disable_unprepare(pc->clk);
|
||||
pm_runtime_put(pc->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -204,8 +208,8 @@ static int tegra_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
|
|||
int rc = 0;
|
||||
u32 val;
|
||||
|
||||
rc = clk_prepare_enable(pc->clk);
|
||||
if (rc < 0)
|
||||
rc = pm_runtime_resume_and_get(pc->dev);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
val = pwm_readl(pc, pwm->hwpwm);
|
||||
|
@ -224,7 +228,7 @@ static void tegra_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
|
|||
val &= ~PWM_ENABLE;
|
||||
pwm_writel(pc, pwm->hwpwm, val);
|
||||
|
||||
clk_disable_unprepare(pc->clk);
|
||||
pm_runtime_put_sync(pc->dev);
|
||||
}
|
||||
|
||||
static const struct pwm_ops tegra_pwm_ops = {
|
||||
|
@ -256,11 +260,20 @@ static int tegra_pwm_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(pwm->clk))
|
||||
return PTR_ERR(pwm->clk);
|
||||
|
||||
ret = devm_tegra_core_dev_init_opp_table_common(&pdev->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
ret = pm_runtime_resume_and_get(&pdev->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Set maximum frequency of the IP */
|
||||
ret = clk_set_rate(pwm->clk, pwm->soc->max_frequency);
|
||||
ret = dev_pm_opp_set_rate(pwm->dev, pwm->soc->max_frequency);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to set max frequency: %d\n", ret);
|
||||
return ret;
|
||||
goto put_pm;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -278,7 +291,7 @@ static int tegra_pwm_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(pwm->rst)) {
|
||||
ret = PTR_ERR(pwm->rst);
|
||||
dev_err(&pdev->dev, "Reset control is not found: %d\n", ret);
|
||||
return ret;
|
||||
goto put_pm;
|
||||
}
|
||||
|
||||
reset_control_deassert(pwm->rst);
|
||||
|
@ -291,10 +304,16 @@ static int tegra_pwm_probe(struct platform_device *pdev)
|
|||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
|
||||
reset_control_assert(pwm->rst);
|
||||
return ret;
|
||||
goto put_pm;
|
||||
}
|
||||
|
||||
pm_runtime_put(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
put_pm:
|
||||
pm_runtime_put_sync_suspend(&pdev->dev);
|
||||
pm_runtime_force_suspend(&pdev->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tegra_pwm_remove(struct platform_device *pdev)
|
||||
|
@ -305,20 +324,44 @@ static int tegra_pwm_remove(struct platform_device *pdev)
|
|||
|
||||
reset_control_assert(pc->rst);
|
||||
|
||||
pm_runtime_force_suspend(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int tegra_pwm_suspend(struct device *dev)
|
||||
static int __maybe_unused tegra_pwm_runtime_suspend(struct device *dev)
|
||||
{
|
||||
return pinctrl_pm_select_sleep_state(dev);
|
||||
struct tegra_pwm_chip *pc = dev_get_drvdata(dev);
|
||||
int err;
|
||||
|
||||
clk_disable_unprepare(pc->clk);
|
||||
|
||||
err = pinctrl_pm_select_sleep_state(dev);
|
||||
if (err) {
|
||||
clk_prepare_enable(pc->clk);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_pwm_resume(struct device *dev)
|
||||
static int __maybe_unused tegra_pwm_runtime_resume(struct device *dev)
|
||||
{
|
||||
return pinctrl_pm_select_default_state(dev);
|
||||
struct tegra_pwm_chip *pc = dev_get_drvdata(dev);
|
||||
int err;
|
||||
|
||||
err = pinctrl_pm_select_default_state(dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = clk_prepare_enable(pc->clk);
|
||||
if (err) {
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct tegra_pwm_soc tegra20_pwm_soc = {
|
||||
.num_channels = 4,
|
||||
|
@ -344,7 +387,10 @@ static const struct of_device_id tegra_pwm_of_match[] = {
|
|||
MODULE_DEVICE_TABLE(of, tegra_pwm_of_match);
|
||||
|
||||
static const struct dev_pm_ops tegra_pwm_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(tegra_pwm_suspend, tegra_pwm_resume)
|
||||
SET_RUNTIME_PM_OPS(tegra_pwm_runtime_suspend, tegra_pwm_runtime_resume,
|
||||
NULL)
|
||||
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver tegra_pwm_driver = {
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/export.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <soc/tegra/common.h>
|
||||
#include <soc/tegra/fuse.h>
|
||||
|
@ -43,6 +44,7 @@ static int tegra_core_dev_init_opp_state(struct device *dev)
|
|||
{
|
||||
unsigned long rate;
|
||||
struct clk *clk;
|
||||
bool rpm_enabled;
|
||||
int err;
|
||||
|
||||
clk = devm_clk_get(dev, NULL);
|
||||
|
@ -57,8 +59,31 @@ static int tegra_core_dev_init_opp_state(struct device *dev)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Runtime PM of the device must be enabled in order to set up
|
||||
* GENPD's performance properly because GENPD core checks whether
|
||||
* device is suspended and this check doesn't work while RPM is
|
||||
* disabled. This makes sure the OPP vote below gets cached in
|
||||
* GENPD for the device. Instead, the vote is done the next time
|
||||
* the device gets runtime resumed.
|
||||
*/
|
||||
rpm_enabled = pm_runtime_enabled(dev);
|
||||
if (!rpm_enabled)
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
/* should never happen in practice */
|
||||
if (!pm_runtime_enabled(dev)) {
|
||||
dev_WARN(dev, "failed to enable runtime PM\n");
|
||||
pm_runtime_disable(dev);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* first dummy rate-setting initializes voltage vote */
|
||||
err = dev_pm_opp_set_rate(dev, rate);
|
||||
|
||||
if (!rpm_enabled)
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
if (err) {
|
||||
dev_err(dev, "failed to initialize OPP clock: %d\n", err);
|
||||
return err;
|
||||
|
|
|
@ -18,12 +18,15 @@
|
|||
#include <linux/kthread.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
||||
#include <soc/tegra/common.h>
|
||||
|
||||
#define SLINK_COMMAND 0x000
|
||||
#define SLINK_BIT_LENGTH(x) (((x) & 0x1f) << 0)
|
||||
#define SLINK_WORD_SIZE(x) (((x) & 0x1f) << 5)
|
||||
|
@ -680,7 +683,7 @@ static int tegra_slink_start_transfer_one(struct spi_device *spi,
|
|||
bits_per_word = t->bits_per_word;
|
||||
speed = t->speed_hz;
|
||||
if (speed != tspi->cur_speed) {
|
||||
clk_set_rate(tspi->clk, speed * 4);
|
||||
dev_pm_opp_set_rate(tspi->dev, speed * 4);
|
||||
tspi->cur_speed = speed;
|
||||
}
|
||||
|
||||
|
@ -1066,6 +1069,10 @@ static int tegra_slink_probe(struct platform_device *pdev)
|
|||
goto exit_free_master;
|
||||
}
|
||||
|
||||
ret = devm_tegra_core_dev_init_opp_table_common(&pdev->dev);
|
||||
if (ret)
|
||||
goto exit_free_master;
|
||||
|
||||
tspi->max_buf_size = SLINK_FIFO_DEPTH << 2;
|
||||
tspi->dma_buf_size = DEFAULT_SPI_DMA_BUF_LEN;
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <soc/tegra/common.h>
|
||||
#include <soc/tegra/pmc.h>
|
||||
|
||||
#include "uapi.h"
|
||||
|
@ -920,13 +921,17 @@ static __maybe_unused int tegra_vde_runtime_suspend(struct device *dev)
|
|||
struct tegra_vde *vde = dev_get_drvdata(dev);
|
||||
int err;
|
||||
|
||||
err = tegra_powergate_power_off(TEGRA_POWERGATE_VDEC);
|
||||
if (err) {
|
||||
dev_err(dev, "Failed to power down HW: %d\n", err);
|
||||
return err;
|
||||
if (!dev->pm_domain) {
|
||||
err = tegra_powergate_power_off(TEGRA_POWERGATE_VDEC);
|
||||
if (err) {
|
||||
dev_err(dev, "Failed to power down HW: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
clk_disable_unprepare(vde->clk);
|
||||
reset_control_release(vde->rst);
|
||||
reset_control_release(vde->rst_mc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -936,14 +941,45 @@ static __maybe_unused int tegra_vde_runtime_resume(struct device *dev)
|
|||
struct tegra_vde *vde = dev_get_drvdata(dev);
|
||||
int err;
|
||||
|
||||
err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_VDEC,
|
||||
vde->clk, vde->rst);
|
||||
err = reset_control_acquire(vde->rst_mc);
|
||||
if (err) {
|
||||
dev_err(dev, "Failed to power up HW : %d\n", err);
|
||||
dev_err(dev, "Failed to acquire mc reset: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = reset_control_acquire(vde->rst);
|
||||
if (err) {
|
||||
dev_err(dev, "Failed to acquire reset: %d\n", err);
|
||||
goto release_mc_reset;
|
||||
}
|
||||
|
||||
if (!dev->pm_domain) {
|
||||
err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_VDEC,
|
||||
vde->clk, vde->rst);
|
||||
if (err) {
|
||||
dev_err(dev, "Failed to power up HW : %d\n", err);
|
||||
goto release_reset;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* tegra_powergate_sequence_power_up() leaves clocks enabled,
|
||||
* while GENPD not.
|
||||
*/
|
||||
err = clk_prepare_enable(vde->clk);
|
||||
if (err) {
|
||||
dev_err(dev, "Failed to enable clock: %d\n", err);
|
||||
goto release_reset;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
release_reset:
|
||||
reset_control_release(vde->rst);
|
||||
release_mc_reset:
|
||||
reset_control_release(vde->rst_mc);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int tegra_vde_probe(struct platform_device *pdev)
|
||||
|
@ -1001,14 +1037,14 @@ static int tegra_vde_probe(struct platform_device *pdev)
|
|||
return err;
|
||||
}
|
||||
|
||||
vde->rst = devm_reset_control_get(dev, NULL);
|
||||
vde->rst = devm_reset_control_get_exclusive_released(dev, NULL);
|
||||
if (IS_ERR(vde->rst)) {
|
||||
err = PTR_ERR(vde->rst);
|
||||
dev_err(dev, "Could not get VDE reset %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
vde->rst_mc = devm_reset_control_get_optional(dev, "mc");
|
||||
vde->rst_mc = devm_reset_control_get_optional_exclusive_released(dev, "mc");
|
||||
if (IS_ERR(vde->rst_mc)) {
|
||||
err = PTR_ERR(vde->rst_mc);
|
||||
dev_err(dev, "Could not get MC reset %d\n", err);
|
||||
|
@ -1026,6 +1062,12 @@ static int tegra_vde_probe(struct platform_device *pdev)
|
|||
return err;
|
||||
}
|
||||
|
||||
err = devm_tegra_core_dev_init_opp_table_common(dev);
|
||||
if (err) {
|
||||
dev_err(dev, "Could initialize OPP table %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
vde->iram_pool = of_gen_pool_get(dev->of_node, "iram", 0);
|
||||
if (!vde->iram_pool) {
|
||||
dev_err(dev, "Could not get IRAM pool\n");
|
||||
|
@ -1133,8 +1175,7 @@ static void tegra_vde_shutdown(struct platform_device *pdev)
|
|||
* On some devices bootloader isn't ready to a power-gated VDE on
|
||||
* a warm-reboot, machine will hang in that case.
|
||||
*/
|
||||
if (pm_runtime_status_suspended(&pdev->dev))
|
||||
tegra_vde_runtime_resume(&pdev->dev);
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
}
|
||||
|
||||
static __maybe_unused int tegra_vde_pm_suspend(struct device *dev)
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include <linux/usb.h>
|
||||
|
@ -15,6 +16,8 @@
|
|||
#include <linux/usb/of.h>
|
||||
#include <linux/usb/phy.h>
|
||||
|
||||
#include <soc/tegra/common.h>
|
||||
|
||||
#include "../host/ehci.h"
|
||||
|
||||
#include "ci.h"
|
||||
|
@ -278,6 +281,8 @@ static int tegra_usb_probe(struct platform_device *pdev)
|
|||
if (!usb)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, usb);
|
||||
|
||||
soc = of_device_get_match_data(&pdev->dev);
|
||||
if (!soc) {
|
||||
dev_err(&pdev->dev, "failed to match OF data\n");
|
||||
|
@ -296,11 +301,14 @@ static int tegra_usb_probe(struct platform_device *pdev)
|
|||
return err;
|
||||
}
|
||||
|
||||
err = clk_prepare_enable(usb->clk);
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->dev, "failed to enable clock: %d\n", err);
|
||||
err = devm_tegra_core_dev_init_opp_table_common(&pdev->dev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
err = pm_runtime_resume_and_get(&pdev->dev);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (device_property_present(&pdev->dev, "nvidia,needs-double-reset"))
|
||||
usb->needs_double_reset = true;
|
||||
|
@ -320,8 +328,6 @@ static int tegra_usb_probe(struct platform_device *pdev)
|
|||
if (err)
|
||||
goto fail_power_off;
|
||||
|
||||
platform_set_drvdata(pdev, usb);
|
||||
|
||||
/* setup and register ChipIdea HDRC device */
|
||||
usb->soc = soc;
|
||||
usb->data.name = "tegra-usb";
|
||||
|
@ -350,7 +356,9 @@ static int tegra_usb_probe(struct platform_device *pdev)
|
|||
phy_shutdown:
|
||||
usb_phy_shutdown(usb->phy);
|
||||
fail_power_off:
|
||||
clk_disable_unprepare(usb->clk);
|
||||
pm_runtime_put_sync_suspend(&pdev->dev);
|
||||
pm_runtime_force_suspend(&pdev->dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -360,15 +368,46 @@ static int tegra_usb_remove(struct platform_device *pdev)
|
|||
|
||||
ci_hdrc_remove_device(usb->dev);
|
||||
usb_phy_shutdown(usb->phy);
|
||||
|
||||
pm_runtime_put_sync_suspend(&pdev->dev);
|
||||
pm_runtime_force_suspend(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused tegra_usb_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct tegra_usb *usb = dev_get_drvdata(dev);
|
||||
int err;
|
||||
|
||||
err = clk_prepare_enable(usb->clk);
|
||||
if (err < 0) {
|
||||
dev_err(dev, "failed to enable clock: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused tegra_usb_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct tegra_usb *usb = dev_get_drvdata(dev);
|
||||
|
||||
clk_disable_unprepare(usb->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops tegra_usb_pm = {
|
||||
SET_RUNTIME_PM_OPS(tegra_usb_runtime_suspend, tegra_usb_runtime_resume,
|
||||
NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver tegra_usb_driver = {
|
||||
.driver = {
|
||||
.name = "tegra-usb",
|
||||
.of_match_table = tegra_usb_of_match,
|
||||
.pm = &tegra_usb_pm,
|
||||
},
|
||||
.probe = tegra_usb_probe,
|
||||
.remove = tegra_usb_remove,
|
||||
|
|
|
@ -39,4 +39,19 @@ devm_tegra_core_dev_init_opp_table(struct device *dev,
|
|||
}
|
||||
#endif
|
||||
|
||||
static inline int
|
||||
devm_tegra_core_dev_init_opp_table_common(struct device *dev)
|
||||
{
|
||||
struct tegra_core_opp_params opp_params = {};
|
||||
int err;
|
||||
|
||||
opp_params.init_state = true;
|
||||
|
||||
err = devm_tegra_core_dev_init_opp_table(dev, &opp_params);
|
||||
if (err != -ENODEV)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* __SOC_TEGRA_COMMON_H__ */
|
||||
|
|
Loading…
Reference in New Issue