From 03978d42ed0d69cb5d38dfb0aeb5216d19507c60 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Fri, 28 May 2021 02:54:01 +0300 Subject: soc/tegra: regulators: Bump voltages on system reboot MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ensure that SoC voltages are at a level suitable for a system reboot. This is important for some devices that use CPU reset method for the rebooting. SoC CPU and core voltages now are be restored to a level that is suitable for rebooting. This patch fixes hang on reboot on Asus Transformer TF101, it was also reported as fixing some of reboot issues on Toshiba AC100. Reported-by: Nikola Milosavljević Tested-by: Nikola Milosavljević # TF101 Signed-off-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/soc/tegra/regulators-tegra20.c | 75 +++++++++++++++++++++++++++++++++- drivers/soc/tegra/regulators-tegra30.c | 75 +++++++++++++++++++++++++++++++++- 2 files changed, 148 insertions(+), 2 deletions(-) (limited to 'drivers/soc/tegra') diff --git a/drivers/soc/tegra/regulators-tegra20.c b/drivers/soc/tegra/regulators-tegra20.c index 367a71a3cd10..3479be5ee494 100644 --- a/drivers/soc/tegra/regulators-tegra20.c +++ b/drivers/soc/tegra/regulators-tegra20.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -21,7 +22,10 @@ struct tegra_regulator_coupler { struct regulator_dev *core_rdev; struct regulator_dev *cpu_rdev; struct regulator_dev *rtc_rdev; - int core_min_uV; + struct notifier_block reboot_notifier; + int core_min_uV, cpu_min_uV; + bool sys_reboot_mode_req; + bool sys_reboot_mode; }; static inline struct tegra_regulator_coupler * @@ -242,6 +246,10 @@ static int tegra20_cpu_voltage_update(struct tegra_regulator_coupler *tegra, if (cpu_uV < 0) return cpu_uV; + /* store boot voltage level */ + if (!tegra->cpu_min_uV) + tegra->cpu_min_uV = cpu_uV; + /* * CPU's regulator may not have any consumers, hence the voltage * must not be changed in that case because CPU simply won't @@ -250,6 +258,10 @@ static int tegra20_cpu_voltage_update(struct tegra_regulator_coupler *tegra, if (!cpu_min_uV_consumers) cpu_min_uV = cpu_uV; + /* restore boot voltage level */ + if (tegra->sys_reboot_mode) + cpu_min_uV = max(cpu_min_uV, tegra->cpu_min_uV); + if (cpu_min_uV > cpu_uV) { err = tegra20_core_rtc_update(tegra, core_rdev, rtc_rdev, cpu_uV, cpu_min_uV); @@ -290,6 +302,8 @@ static int tegra20_regulator_balance_voltage(struct regulator_coupler *coupler, return -EINVAL; } + tegra->sys_reboot_mode = READ_ONCE(tegra->sys_reboot_mode_req); + if (rdev == cpu_rdev) return tegra20_cpu_voltage_update(tegra, cpu_rdev, core_rdev, rtc_rdev); @@ -303,6 +317,51 @@ static int tegra20_regulator_balance_voltage(struct regulator_coupler *coupler, return -EPERM; } +static int tegra20_regulator_prepare_reboot(struct tegra_regulator_coupler *tegra, + bool sys_reboot_mode) +{ + int err; + + if (!tegra->core_rdev || !tegra->rtc_rdev || !tegra->cpu_rdev) + return 0; + + WRITE_ONCE(tegra->sys_reboot_mode_req, true); + + /* + * Some devices use CPU soft-reboot method and in this case we + * should ensure that voltages are sane for the reboot by restoring + * the minimum boot levels. + */ + err = regulator_sync_voltage_rdev(tegra->cpu_rdev); + if (err) + return err; + + err = regulator_sync_voltage_rdev(tegra->core_rdev); + if (err) + return err; + + WRITE_ONCE(tegra->sys_reboot_mode_req, sys_reboot_mode); + + return 0; +} + +static int tegra20_regulator_reboot(struct notifier_block *notifier, + unsigned long event, void *cmd) +{ + struct tegra_regulator_coupler *tegra; + int ret; + + if (event != SYS_RESTART) + return NOTIFY_DONE; + + tegra = container_of(notifier, struct tegra_regulator_coupler, + reboot_notifier); + + ret = tegra20_regulator_prepare_reboot(tegra, true); + + return notifier_from_errno(ret); +} + static int tegra20_regulator_attach(struct regulator_coupler *coupler, struct regulator_dev *rdev) { @@ -335,6 +394,14 @@ static int tegra20_regulator_detach(struct regulator_coupler *coupler, { struct tegra_regulator_coupler *tegra = to_tegra_coupler(coupler); + /* + * We don't expect regulators to be decoupled during reboot, + * this may race with the reboot handler and shouldn't ever + * happen in practice. + */ + if (WARN_ON_ONCE(system_state > SYSTEM_RUNNING)) + return -EPERM; + if (tegra->core_rdev == rdev) { tegra->core_rdev = NULL; return 0; @@ -359,13 +426,19 @@ static struct tegra_regulator_coupler tegra20_coupler = { .detach_regulator = tegra20_regulator_detach, .balance_voltage = tegra20_regulator_balance_voltage, }, + .reboot_notifier.notifier_call = tegra20_regulator_reboot, }; static int __init tegra_regulator_coupler_init(void) { + int err; + if (!of_machine_is_compatible("nvidia,tegra20")) return 0; + err = register_reboot_notifier(&tegra20_coupler.reboot_notifier); + WARN_ON(err); + return regulator_coupler_register(&tegra20_coupler.coupler); } arch_initcall(tegra_regulator_coupler_init); diff --git a/drivers/soc/tegra/regulators-tegra30.c b/drivers/soc/tegra/regulators-tegra30.c index 0e776b20f625..18fe53d0a870 100644 --- a/drivers/soc/tegra/regulators-tegra30.c +++ b/drivers/soc/tegra/regulators-tegra30.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -22,7 +23,10 @@ struct tegra_regulator_coupler { struct regulator_coupler coupler; struct regulator_dev *core_rdev; struct regulator_dev *cpu_rdev; - int core_min_uV; + struct notifier_block reboot_notifier; + int core_min_uV, cpu_min_uV; + bool sys_reboot_mode_req; + bool sys_reboot_mode; }; static inline struct tegra_regulator_coupler * @@ -172,6 +176,10 @@ static int tegra30_voltage_update(struct tegra_regulator_coupler *tegra, if (cpu_uV < 0) return cpu_uV; + /* store boot voltage level */ + if (!tegra->cpu_min_uV) + tegra->cpu_min_uV = cpu_uV; + /* * CPU's regulator may not have any consumers, hence the voltage * must not be changed in that case because CPU simply won't @@ -195,6 +203,10 @@ static int tegra30_voltage_update(struct tegra_regulator_coupler *tegra, if (err) return err; + /* restore boot voltage level */ + if (tegra->sys_reboot_mode) + cpu_min_uV = max(cpu_min_uV, tegra->cpu_min_uV); + if (core_min_limited_uV > core_uV) { pr_err("core voltage constraint violated: %d %d %d\n", core_uV, core_min_limited_uV, cpu_uV); @@ -263,9 +275,56 @@ static int tegra30_regulator_balance_voltage(struct regulator_coupler *coupler, return -EINVAL; } + tegra->sys_reboot_mode = READ_ONCE(tegra->sys_reboot_mode_req); + return tegra30_voltage_update(tegra, cpu_rdev, core_rdev); } +static int tegra30_regulator_prepare_reboot(struct tegra_regulator_coupler *tegra, + bool sys_reboot_mode) +{ + int err; + + if (!tegra->core_rdev || !tegra->cpu_rdev) + return 0; + + WRITE_ONCE(tegra->sys_reboot_mode_req, true); + + /* + * Some devices use CPU soft-reboot method and in this case we + * should ensure that voltages are sane for the reboot by restoring + * the minimum boot levels. + */ + err = regulator_sync_voltage_rdev(tegra->cpu_rdev); + if (err) + return err; + + err = regulator_sync_voltage_rdev(tegra->core_rdev); + if (err) + return err; + + WRITE_ONCE(tegra->sys_reboot_mode_req, sys_reboot_mode); + + return 0; +} + +static int tegra30_regulator_reboot(struct notifier_block *notifier, + unsigned long event, void *cmd) +{ + struct tegra_regulator_coupler *tegra; + int ret; + + if (event != SYS_RESTART) + return NOTIFY_DONE; + + tegra = container_of(notifier, struct tegra_regulator_coupler, + reboot_notifier); + + ret = tegra30_regulator_prepare_reboot(tegra, true); + + return notifier_from_errno(ret); +} + static int tegra30_regulator_attach(struct regulator_coupler *coupler, struct regulator_dev *rdev) { @@ -292,6 +351,14 @@ static int tegra30_regulator_detach(struct regulator_coupler *coupler, { struct tegra_regulator_coupler *tegra = to_tegra_coupler(coupler); + /* + * We don't expect regulators to be decoupled during reboot, + * this may race with the reboot handler and shouldn't ever + * happen in practice. + */ + if (WARN_ON_ONCE(system_state > SYSTEM_RUNNING)) + return -EPERM; + if (tegra->core_rdev == rdev) { tegra->core_rdev = NULL; return 0; @@ -311,13 +378,19 @@ static struct tegra_regulator_coupler tegra30_coupler = { .detach_regulator = tegra30_regulator_detach, .balance_voltage = tegra30_regulator_balance_voltage, }, + .reboot_notifier.notifier_call = tegra30_regulator_reboot, }; static int __init tegra_regulator_coupler_init(void) { + int err; + if (!of_machine_is_compatible("nvidia,tegra30")) return 0; + err = register_reboot_notifier(&tegra30_coupler.reboot_notifier); + WARN_ON(err); + return regulator_coupler_register(&tegra30_coupler.coupler); } arch_initcall(tegra_regulator_coupler_init); -- cgit v1.2.3-70-g09d2 From b8818de9c0c107019c2a90a50423c1b929176f3c Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Fri, 28 May 2021 02:54:03 +0300 Subject: soc/tegra: Add devm_tegra_core_dev_init_opp_table() Add common helper which initializes OPP table for Tegra SoC core devices. Tested-by: Peter Geis # Ouya T30 Tested-by: Paul Fertser # PAZ00 T20 Tested-by: Nicolas Chauvet # PAZ00 T20 and TK1 T124 Tested-by: Matt Merhar # Ouya T30 Signed-off-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/soc/tegra/common.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++ include/soc/tegra/common.h | 22 +++++++++++ 2 files changed, 119 insertions(+) (limited to 'drivers/soc/tegra') diff --git a/drivers/soc/tegra/common.c b/drivers/soc/tegra/common.c index 3dc54f59cafe..cd33e99249c3 100644 --- a/drivers/soc/tegra/common.c +++ b/drivers/soc/tegra/common.c @@ -3,9 +3,16 @@ * Copyright (C) 2014 NVIDIA CORPORATION. All rights reserved. */ +#define dev_fmt(fmt) "tegra-soc: " fmt + +#include +#include +#include #include +#include #include +#include static const struct of_device_id tegra_machine_match[] = { { .compatible = "nvidia,tegra20", }, @@ -31,3 +38,93 @@ bool soc_is_tegra(void) return match != NULL; } + +static int tegra_core_dev_init_opp_state(struct device *dev) +{ + unsigned long rate; + struct clk *clk; + int err; + + clk = devm_clk_get(dev, NULL); + if (IS_ERR(clk)) { + dev_err(dev, "failed to get clk: %pe\n", clk); + return PTR_ERR(clk); + } + + rate = clk_get_rate(clk); + if (!rate) { + dev_err(dev, "failed to get clk rate\n"); + return -EINVAL; + } + + /* first dummy rate-setting initializes voltage vote */ + err = dev_pm_opp_set_rate(dev, rate); + if (err) { + dev_err(dev, "failed to initialize OPP clock: %d\n", err); + return err; + } + + return 0; +} + +/** + * devm_tegra_core_dev_init_opp_table() - initialize OPP table + * @dev: device for which OPP table is initialized + * @params: pointer to the OPP table configuration + * + * This function will initialize OPP table and sync OPP state of a Tegra SoC + * core device. + * + * Return: 0 on success or errorno. + */ +int devm_tegra_core_dev_init_opp_table(struct device *dev, + struct tegra_core_opp_params *params) +{ + u32 hw_version; + int err; + + err = devm_pm_opp_set_clkname(dev, NULL); + if (err) { + dev_err(dev, "failed to set OPP clk: %d\n", err); + return err; + } + + /* Tegra114+ doesn't support OPP yet */ + if (!of_machine_is_compatible("nvidia,tegra20") && + !of_machine_is_compatible("nvidia,tegra30")) + return -ENODEV; + + if (of_machine_is_compatible("nvidia,tegra20")) + hw_version = BIT(tegra_sku_info.soc_process_id); + else + hw_version = BIT(tegra_sku_info.soc_speedo_id); + + err = devm_pm_opp_set_supported_hw(dev, &hw_version, 1); + if (err) { + dev_err(dev, "failed to set OPP supported HW: %d\n", err); + return err; + } + + /* + * Older device-trees have an empty OPP table, we will get + * -ENODEV from devm_pm_opp_of_add_table() in this case. + */ + err = devm_pm_opp_of_add_table(dev); + if (err) { + if (err == -ENODEV) + dev_err_once(dev, "OPP table not found, please update device-tree\n"); + else + dev_err(dev, "failed to add OPP table: %d\n", err); + + return err; + } + + if (params->init_state) { + err = tegra_core_dev_init_opp_state(dev); + if (err) + return err; + } + + return 0; +} +EXPORT_SYMBOL_GPL(devm_tegra_core_dev_init_opp_table); diff --git a/include/soc/tegra/common.h b/include/soc/tegra/common.h index 744280ecab5f..af41ad80ec21 100644 --- a/include/soc/tegra/common.h +++ b/include/soc/tegra/common.h @@ -6,15 +6,37 @@ #ifndef __SOC_TEGRA_COMMON_H__ #define __SOC_TEGRA_COMMON_H__ +#include #include +struct device; + +/** + * Tegra SoC core device OPP table configuration + * + * @init_state: pre-initialize OPP state of a device + */ +struct tegra_core_opp_params { + bool init_state; +}; + #ifdef CONFIG_ARCH_TEGRA bool soc_is_tegra(void); + +int devm_tegra_core_dev_init_opp_table(struct device *dev, + struct tegra_core_opp_params *params); #else static inline bool soc_is_tegra(void) { return false; } + +static inline int +devm_tegra_core_dev_init_opp_table(struct device *dev, + struct tegra_core_opp_params *params) +{ + return -ENODEV; +} #endif #endif /* __SOC_TEGRA_COMMON_H__ */ -- cgit v1.2.3-70-g09d2 From f880ee9e96887786dc21e9433ede1719bc3b2624 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Tue, 1 Jun 2021 05:31:17 +0300 Subject: soc/tegra: pmc: Add core power domain NVIDIA Tegra SoCs have multiple power domains, each domain corresponds to an external SoC power rail. Core power domain covers vast majority of hardware blocks within a Tegra SoC. The voltage of a power domain should be set to a level which satisfies all devices within the power domain. Add support for the core power domain which controls voltage state of the domain. This allows us to support system-wide DVFS on Tegra20-210 SoCs. The PMC powergate domains now are sub-domains of the core domain, this requires device-tree updating, older DTBs are unaffected and will continue to work as before. Tested-by: Peter Geis # Ouya T30 Tested-by: Paul Fertser # PAZ00 T20 Tested-by: Nicolas Chauvet # PAZ00 T20 and TK1 T124 Tested-by: Matt Merhar # Ouya T30 Signed-off-by: Dmitry Osipenko Reviewed-by: Ulf Hansson [treding@nvidia.com: squash lockdep class removal patch] Signed-off-by: Thierry Reding --- drivers/soc/tegra/Kconfig | 2 + drivers/soc/tegra/pmc.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) (limited to 'drivers/soc/tegra') diff --git a/drivers/soc/tegra/Kconfig b/drivers/soc/tegra/Kconfig index 976dee036470..20ace654553a 100644 --- a/drivers/soc/tegra/Kconfig +++ b/drivers/soc/tegra/Kconfig @@ -144,6 +144,8 @@ config SOC_TEGRA_FLOWCTRL config SOC_TEGRA_PMC bool select GENERIC_PINCONF + select PM_OPP + select PM_GENERIC_DOMAINS config SOC_TEGRA_POWERGATE_BPMP def_bool y diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 8e3b78bb2ac2..ceceb1709e0a 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -1302,12 +1303,100 @@ free_mem: return err; } +static int +tegra_pmc_core_pd_set_performance_state(struct generic_pm_domain *genpd, + unsigned int level) +{ + struct dev_pm_opp *opp; + int err; + + opp = dev_pm_opp_find_level_ceil(&genpd->dev, &level); + if (IS_ERR(opp)) { + dev_err(&genpd->dev, "failed to find OPP for level %u: %pe\n", + level, opp); + return PTR_ERR(opp); + } + + mutex_lock(&pmc->powergates_lock); + err = dev_pm_opp_set_opp(pmc->dev, opp); + mutex_unlock(&pmc->powergates_lock); + + dev_pm_opp_put(opp); + + if (err) { + dev_err(&genpd->dev, "failed to set voltage to %duV: %d\n", + level, err); + return err; + } + + return 0; +} + +static unsigned int +tegra_pmc_core_pd_opp_to_performance_state(struct generic_pm_domain *genpd, + struct dev_pm_opp *opp) +{ + return dev_pm_opp_get_level(opp); +} + +static int tegra_pmc_core_pd_add(struct tegra_pmc *pmc, struct device_node *np) +{ + struct generic_pm_domain *genpd; + const char *rname = "core"; + int err; + + genpd = devm_kzalloc(pmc->dev, sizeof(*genpd), GFP_KERNEL); + if (!genpd) + return -ENOMEM; + + genpd->name = np->name; + genpd->set_performance_state = tegra_pmc_core_pd_set_performance_state; + genpd->opp_to_performance_state = tegra_pmc_core_pd_opp_to_performance_state; + + err = devm_pm_opp_set_regulators(pmc->dev, &rname, 1); + if (err) + return dev_err_probe(pmc->dev, err, + "failed to set core OPP regulator\n"); + + err = pm_genpd_init(genpd, NULL, false); + if (err) { + dev_err(pmc->dev, "failed to init core genpd: %d\n", err); + return err; + } + + err = of_genpd_add_provider_simple(np, genpd); + if (err) { + dev_err(pmc->dev, "failed to add core genpd: %d\n", err); + goto remove_genpd; + } + + return 0; + +remove_genpd: + pm_genpd_remove(genpd); + + return err; +} + static int tegra_powergate_init(struct tegra_pmc *pmc, struct device_node *parent) { + struct of_phandle_args child_args, parent_args; struct device_node *np, *child; int err = 0; + /* + * Core power domain is the parent of powergate domains, hence it + * should be registered first. + */ + np = of_get_child_by_name(parent, "core-domain"); + if (np) { + err = tegra_pmc_core_pd_add(pmc, np); + of_node_put(np); + if (err) + return err; + } + np = of_get_child_by_name(parent, "powergates"); if (!np) return 0; @@ -1318,6 +1407,21 @@ static int tegra_powergate_init(struct tegra_pmc *pmc, of_node_put(child); break; } + + if (of_parse_phandle_with_args(child, "power-domains", + "#power-domain-cells", + 0, &parent_args)) + continue; + + child_args.np = child; + child_args.args_count = 0; + + err = of_genpd_add_subdomain(&parent_args, &child_args); + of_node_put(parent_args.np); + if (err) { + of_node_put(child); + break; + } } of_node_put(np); @@ -1361,6 +1465,12 @@ static void tegra_powergate_remove_all(struct device_node *parent) } of_node_put(np); + + np = of_get_child_by_name(parent, "core-domain"); + if (np) { + of_genpd_del_provider(np); + of_genpd_remove_last(np); + } } static const struct tegra_io_pad_soc * -- cgit v1.2.3-70-g09d2 From 41bafa698ddd07533914f34aeb432e762ed0cd30 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Tue, 1 Jun 2021 05:31:18 +0300 Subject: soc/tegra: pmc: Add driver state syncing Add driver state syncing that is invoked once all PMC consumers are attached and ready. The consumers are the power domain clients. The synchronization callback is invoked once all client drivers are probed, the driver core handles this for us. This callback informs PMC driver that all voltage votes are initialized by each PD client and it's safe to begin voltage scaling of the core power domain. Signed-off-by: Dmitry Osipenko Reviewed-by: Ulf Hansson [treding@nvidia.com: squash DT backwards-compatibility patch] Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 34 ++++++++++++++++++++++++++++++++++ include/soc/tegra/pmc.h | 7 +++++++ 2 files changed, 41 insertions(+) (limited to 'drivers/soc/tegra') diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index ceceb1709e0a..2ace21db4ae8 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -429,6 +429,9 @@ struct tegra_pmc { struct irq_chip irq; struct notifier_block clk_nb; + + bool core_domain_state_synced; + bool core_domain_registered; }; static struct tegra_pmc *pmc = &(struct tegra_pmc) { @@ -1303,6 +1306,11 @@ free_mem: return err; } +bool tegra_pmc_core_domain_state_synced(void) +{ + return pmc->core_domain_state_synced; +} + static int tegra_pmc_core_pd_set_performance_state(struct generic_pm_domain *genpd, unsigned int level) @@ -1370,6 +1378,8 @@ static int tegra_pmc_core_pd_add(struct tegra_pmc *pmc, struct device_node *np) goto remove_genpd; } + pmc->core_domain_registered = true; + return 0; remove_genpd: @@ -3782,6 +3792,29 @@ static const struct of_device_id tegra_pmc_match[] = { { } }; +static void tegra_pmc_sync_state(struct device *dev) +{ + int err; + + /* + * Older device-trees don't have core PD, and thus, there are + * no dependencies that will block the state syncing. We shouldn't + * mark the domain as synced in this case. + */ + if (!pmc->core_domain_registered) + return; + + pmc->core_domain_state_synced = true; + + /* this is a no-op if core regulator isn't used */ + mutex_lock(&pmc->powergates_lock); + err = dev_pm_opp_sync_regulators(dev); + mutex_unlock(&pmc->powergates_lock); + + if (err) + dev_err(dev, "failed to sync regulators: %d\n", err); +} + static struct platform_driver tegra_pmc_driver = { .driver = { .name = "tegra-pmc", @@ -3790,6 +3823,7 @@ static struct platform_driver tegra_pmc_driver = { #if defined(CONFIG_PM_SLEEP) && defined(CONFIG_ARM) .pm = &tegra_pmc_pm_ops, #endif + .sync_state = tegra_pmc_sync_state, }, .probe = tegra_pmc_probe, }; diff --git a/include/soc/tegra/pmc.h b/include/soc/tegra/pmc.h index 361cb64246f7..d186bccd125d 100644 --- a/include/soc/tegra/pmc.h +++ b/include/soc/tegra/pmc.h @@ -171,6 +171,8 @@ int tegra_io_rail_power_off(unsigned int id); void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode); void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode); +bool tegra_pmc_core_domain_state_synced(void); + #else static inline int tegra_powergate_power_on(unsigned int id) { @@ -227,6 +229,11 @@ static inline void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode) { } +static inline bool tegra_pmc_core_domain_state_synced(void) +{ + return false; +} + #endif /* CONFIG_SOC_TEGRA_PMC */ #if defined(CONFIG_SOC_TEGRA_PMC) && defined(CONFIG_PM_SLEEP) -- cgit v1.2.3-70-g09d2 From 029f7e24a65df641ac843cda8dabe359ff0826eb Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Tue, 1 Jun 2021 05:31:19 +0300 Subject: soc/tegra: regulators: Support core domain state syncing The core voltage shall not drop until state of core domain is synced, i.e. all device drivers that use core domain are loaded and ready. Support core domain state syncing. The core domain driver invokes the core-regulator voltage syncing once the state of domain is synced, at this point the core voltage is allowed to go lower than the level left after bootloader. Tested-by: Peter Geis # Ouya T30 Tested-by: Paul Fertser # PAZ00 T20 Tested-by: Nicolas Chauvet # PAZ00 T20 and TK1 T124 Tested-by: Matt Merhar # Ouya T30 Reviewed-by: Ulf Hansson Signed-off-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/soc/tegra/regulators-tegra20.c | 19 ++++++++++++++++++- drivers/soc/tegra/regulators-tegra30.c | 18 +++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) (limited to 'drivers/soc/tegra') diff --git a/drivers/soc/tegra/regulators-tegra20.c b/drivers/soc/tegra/regulators-tegra20.c index 3479be5ee494..b8ce9fd0650d 100644 --- a/drivers/soc/tegra/regulators-tegra20.c +++ b/drivers/soc/tegra/regulators-tegra20.c @@ -17,6 +17,8 @@ #include #include +#include + struct tegra_regulator_coupler { struct regulator_coupler coupler; struct regulator_dev *core_rdev; @@ -42,6 +44,21 @@ static int tegra20_core_limit(struct tegra_regulator_coupler *tegra, int core_cur_uV; int err; + /* + * Tegra20 SoC has critical DVFS-capable devices that are + * permanently-active or active at a boot time, like EMC + * (DRAM controller) or Display controller for example. + * + * The voltage of a CORE SoC power domain shall not be dropped below + * a minimum level, which is determined by device's clock rate. + * This means that we can't fully allow CORE voltage scaling until + * the state of all DVFS-critical CORE devices is synced. + */ + if (tegra_pmc_core_domain_state_synced() && !tegra->sys_reboot_mode) { + pr_info_once("voltage state synced\n"); + return 0; + } + if (tegra->core_min_uV > 0) return tegra->core_min_uV; @@ -62,7 +79,7 @@ static int tegra20_core_limit(struct tegra_regulator_coupler *tegra, */ tegra->core_min_uV = core_max_uV; - pr_info("core minimum voltage limited to %duV\n", tegra->core_min_uV); + pr_info("core voltage initialized to %duV\n", tegra->core_min_uV); return tegra->core_min_uV; } diff --git a/drivers/soc/tegra/regulators-tegra30.c b/drivers/soc/tegra/regulators-tegra30.c index 18fe53d0a870..e74bbc9c7859 100644 --- a/drivers/soc/tegra/regulators-tegra30.c +++ b/drivers/soc/tegra/regulators-tegra30.c @@ -18,6 +18,7 @@ #include #include +#include struct tegra_regulator_coupler { struct regulator_coupler coupler; @@ -43,6 +44,21 @@ static int tegra30_core_limit(struct tegra_regulator_coupler *tegra, int core_cur_uV; int err; + /* + * Tegra30 SoC has critical DVFS-capable devices that are + * permanently-active or active at a boot time, like EMC + * (DRAM controller) or Display controller for example. + * + * The voltage of a CORE SoC power domain shall not be dropped below + * a minimum level, which is determined by device's clock rate. + * This means that we can't fully allow CORE voltage scaling until + * the state of all DVFS-critical CORE devices is synced. + */ + if (tegra_pmc_core_domain_state_synced() && !tegra->sys_reboot_mode) { + pr_info_once("voltage state synced\n"); + return 0; + } + if (tegra->core_min_uV > 0) return tegra->core_min_uV; @@ -63,7 +79,7 @@ static int tegra30_core_limit(struct tegra_regulator_coupler *tegra, */ tegra->core_min_uV = core_max_uV; - pr_info("core minimum voltage limited to %duV\n", tegra->core_min_uV); + pr_info("core voltage initialized to %duV\n", tegra->core_min_uV); return tegra->core_min_uV; } -- cgit v1.2.3-70-g09d2 From 854d128b8c6c4fa47810ac7cf87c16b8085c3199 Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Mon, 12 Apr 2021 22:05:27 +0800 Subject: soc/tegra: fuse: Don't return -ENOMEM when allocate lookups failed fuse->base can not be unmapped if allocate lookups failed in tegra_init_fuse(), because it is an early_initcall, the driver will be loaded anyway and fuse->base will be accessed by other functions later, so remove the return -ENOMEM after allocating lookups failed to make less confusing. Signed-off-by: Yang Yingliang [treding@nvidia.com: drop error message, out-of-memory is noisy anyway] Signed-off-by: Thierry Reding --- drivers/soc/tegra/fuse/fuse-tegra.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/soc/tegra') diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c index 94b60a692b51..3d9da3d359da 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra.c +++ b/drivers/soc/tegra/fuse/fuse-tegra.c @@ -489,10 +489,8 @@ static int __init tegra_init_fuse(void) size_t size = sizeof(*fuse->lookups) * fuse->soc->num_lookups; fuse->lookups = kmemdup(fuse->soc->lookups, size, GFP_KERNEL); - if (!fuse->lookups) - return -ENOMEM; - - nvmem_add_cell_lookups(fuse->lookups, fuse->soc->num_lookups); + if (fuse->lookups) + nvmem_add_cell_lookups(fuse->lookups, fuse->soc->num_lookups); } return 0; -- cgit v1.2.3-70-g09d2 From e2d0ee225e49a5553986f3138dd2803852a31fd5 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Tue, 13 Apr 2021 14:20:57 +0200 Subject: soc/tegra: fuse: Fix Tegra234-only builds The tegra30_fuse_read() symbol is used on Tegra234, so make sure it's available. Signed-off-by: Thierry Reding --- drivers/soc/tegra/fuse/fuse-tegra30.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/soc/tegra') diff --git a/drivers/soc/tegra/fuse/fuse-tegra30.c b/drivers/soc/tegra/fuse/fuse-tegra30.c index 9ea7f0168457..c1aa7815bd6e 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra30.c +++ b/drivers/soc/tegra/fuse/fuse-tegra30.c @@ -37,7 +37,8 @@ defined(CONFIG_ARCH_TEGRA_132_SOC) || \ defined(CONFIG_ARCH_TEGRA_210_SOC) || \ defined(CONFIG_ARCH_TEGRA_186_SOC) || \ - defined(CONFIG_ARCH_TEGRA_194_SOC) + defined(CONFIG_ARCH_TEGRA_194_SOC) || \ + defined(CONFIG_ARCH_TEGRA_234_SOC) static u32 tegra30_fuse_read_early(struct tegra_fuse *fuse, unsigned int offset) { if (WARN_ON(!fuse->base)) -- cgit v1.2.3-70-g09d2