Skip to content

Commit 80ef351

Browse files
digetxthierryreding
authored andcommitted
soc/tegra: regulators: Prepare for suspend
Depending on hardware version, Tegra SoC may require a higher voltages during resume from system suspend, otherwise hardware will crash. Set SoC voltages to a nominal levels during suspend. Link: https://lore.kernel.org/all/a8280b5b-7347-8995-c97b-10b798cdf057@gmail.com/ Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org> Signed-off-by: Dmitry Osipenko <digetx@gmail.com> Signed-off-by: Thierry Reding <treding@nvidia.com>
1 parent 88724b7 commit 80ef351

2 files changed

Lines changed: 221 additions & 0 deletions

File tree

drivers/soc/tegra/regulators-tegra20.c

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
#include <linux/regulator/coupler.h>
1717
#include <linux/regulator/driver.h>
1818
#include <linux/regulator/machine.h>
19+
#include <linux/suspend.h>
1920

21+
#include <soc/tegra/fuse.h>
2022
#include <soc/tegra/pmc.h>
2123

2224
struct tegra_regulator_coupler {
@@ -25,9 +27,12 @@ struct tegra_regulator_coupler {
2527
struct regulator_dev *cpu_rdev;
2628
struct regulator_dev *rtc_rdev;
2729
struct notifier_block reboot_notifier;
30+
struct notifier_block suspend_notifier;
2831
int core_min_uV, cpu_min_uV;
2932
bool sys_reboot_mode_req;
3033
bool sys_reboot_mode;
34+
bool sys_suspend_mode_req;
35+
bool sys_suspend_mode;
3136
};
3237

3338
static inline struct tegra_regulator_coupler *
@@ -105,6 +110,28 @@ static int tegra20_core_rtc_max_spread(struct regulator_dev *core_rdev,
105110
return 150000;
106111
}
107112

113+
static int tegra20_cpu_nominal_uV(void)
114+
{
115+
switch (tegra_sku_info.soc_speedo_id) {
116+
case 0:
117+
return 1100000;
118+
case 1:
119+
return 1025000;
120+
default:
121+
return 1125000;
122+
}
123+
}
124+
125+
static int tegra20_core_nominal_uV(void)
126+
{
127+
switch (tegra_sku_info.soc_speedo_id) {
128+
default:
129+
return 1225000;
130+
case 2:
131+
return 1300000;
132+
}
133+
}
134+
108135
static int tegra20_core_rtc_update(struct tegra_regulator_coupler *tegra,
109136
struct regulator_dev *core_rdev,
110137
struct regulator_dev *rtc_rdev,
@@ -144,6 +171,11 @@ static int tegra20_core_rtc_update(struct tegra_regulator_coupler *tegra,
144171
if (err)
145172
return err;
146173

174+
/* prepare voltage level for suspend */
175+
if (tegra->sys_suspend_mode)
176+
core_min_uV = clamp(tegra20_core_nominal_uV(),
177+
core_min_uV, core_max_uV);
178+
147179
core_uV = regulator_get_voltage_rdev(core_rdev);
148180
if (core_uV < 0)
149181
return core_uV;
@@ -279,6 +311,11 @@ static int tegra20_cpu_voltage_update(struct tegra_regulator_coupler *tegra,
279311
if (tegra->sys_reboot_mode)
280312
cpu_min_uV = max(cpu_min_uV, tegra->cpu_min_uV);
281313

314+
/* prepare voltage level for suspend */
315+
if (tegra->sys_suspend_mode)
316+
cpu_min_uV = clamp(tegra20_cpu_nominal_uV(),
317+
cpu_min_uV, cpu_max_uV);
318+
282319
if (cpu_min_uV > cpu_uV) {
283320
err = tegra20_core_rtc_update(tegra, core_rdev, rtc_rdev,
284321
cpu_uV, cpu_min_uV);
@@ -320,6 +357,7 @@ static int tegra20_regulator_balance_voltage(struct regulator_coupler *coupler,
320357
}
321358

322359
tegra->sys_reboot_mode = READ_ONCE(tegra->sys_reboot_mode_req);
360+
tegra->sys_suspend_mode = READ_ONCE(tegra->sys_suspend_mode_req);
323361

324362
if (rdev == cpu_rdev)
325363
return tegra20_cpu_voltage_update(tegra, cpu_rdev,
@@ -334,6 +372,63 @@ static int tegra20_regulator_balance_voltage(struct regulator_coupler *coupler,
334372
return -EPERM;
335373
}
336374

375+
static int tegra20_regulator_prepare_suspend(struct tegra_regulator_coupler *tegra,
376+
bool sys_suspend_mode)
377+
{
378+
int err;
379+
380+
if (!tegra->core_rdev || !tegra->rtc_rdev || !tegra->cpu_rdev)
381+
return 0;
382+
383+
/*
384+
* All power domains are enabled early during resume from suspend
385+
* by GENPD core. Domains like VENC may require a higher voltage
386+
* when enabled during resume from suspend. This also prepares
387+
* hardware for resuming from LP0.
388+
*/
389+
390+
WRITE_ONCE(tegra->sys_suspend_mode_req, sys_suspend_mode);
391+
392+
err = regulator_sync_voltage_rdev(tegra->cpu_rdev);
393+
if (err)
394+
return err;
395+
396+
err = regulator_sync_voltage_rdev(tegra->core_rdev);
397+
if (err)
398+
return err;
399+
400+
return 0;
401+
}
402+
403+
static int tegra20_regulator_suspend(struct notifier_block *notifier,
404+
unsigned long mode, void *arg)
405+
{
406+
struct tegra_regulator_coupler *tegra;
407+
int ret = 0;
408+
409+
tegra = container_of(notifier, struct tegra_regulator_coupler,
410+
suspend_notifier);
411+
412+
switch (mode) {
413+
case PM_HIBERNATION_PREPARE:
414+
case PM_RESTORE_PREPARE:
415+
case PM_SUSPEND_PREPARE:
416+
ret = tegra20_regulator_prepare_suspend(tegra, true);
417+
break;
418+
419+
case PM_POST_HIBERNATION:
420+
case PM_POST_RESTORE:
421+
case PM_POST_SUSPEND:
422+
ret = tegra20_regulator_prepare_suspend(tegra, false);
423+
break;
424+
}
425+
426+
if (ret)
427+
pr_err("failed to prepare regulators: %d\n", ret);
428+
429+
return notifier_from_errno(ret);
430+
}
431+
337432
static int tegra20_regulator_prepare_reboot(struct tegra_regulator_coupler *tegra,
338433
bool sys_reboot_mode)
339434
{
@@ -444,6 +539,7 @@ static struct tegra_regulator_coupler tegra20_coupler = {
444539
.balance_voltage = tegra20_regulator_balance_voltage,
445540
},
446541
.reboot_notifier.notifier_call = tegra20_regulator_reboot,
542+
.suspend_notifier.notifier_call = tegra20_regulator_suspend,
447543
};
448544

449545
static int __init tegra_regulator_coupler_init(void)
@@ -456,6 +552,9 @@ static int __init tegra_regulator_coupler_init(void)
456552
err = register_reboot_notifier(&tegra20_coupler.reboot_notifier);
457553
WARN_ON(err);
458554

555+
err = register_pm_notifier(&tegra20_coupler.suspend_notifier);
556+
WARN_ON(err);
557+
459558
return regulator_coupler_register(&tegra20_coupler.coupler);
460559
}
461560
arch_initcall(tegra_regulator_coupler_init);

drivers/soc/tegra/regulators-tegra30.c

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <linux/regulator/coupler.h>
1717
#include <linux/regulator/driver.h>
1818
#include <linux/regulator/machine.h>
19+
#include <linux/suspend.h>
1920

2021
#include <soc/tegra/fuse.h>
2122
#include <soc/tegra/pmc.h>
@@ -25,9 +26,12 @@ struct tegra_regulator_coupler {
2526
struct regulator_dev *core_rdev;
2627
struct regulator_dev *cpu_rdev;
2728
struct notifier_block reboot_notifier;
29+
struct notifier_block suspend_notifier;
2830
int core_min_uV, cpu_min_uV;
2931
bool sys_reboot_mode_req;
3032
bool sys_reboot_mode;
33+
bool sys_suspend_mode_req;
34+
bool sys_suspend_mode;
3135
};
3236

3337
static inline struct tegra_regulator_coupler *
@@ -113,6 +117,52 @@ static int tegra30_core_cpu_limit(int cpu_uV)
113117
return -EINVAL;
114118
}
115119

120+
static int tegra30_cpu_nominal_uV(void)
121+
{
122+
switch (tegra_sku_info.cpu_speedo_id) {
123+
case 10 ... 11:
124+
return 850000;
125+
126+
case 9:
127+
return 912000;
128+
129+
case 1 ... 3:
130+
case 7 ... 8:
131+
return 1050000;
132+
133+
default:
134+
return 1125000;
135+
136+
case 4 ... 6:
137+
case 12 ... 13:
138+
return 1237000;
139+
}
140+
}
141+
142+
static int tegra30_core_nominal_uV(void)
143+
{
144+
switch (tegra_sku_info.soc_speedo_id) {
145+
case 0:
146+
return 1200000;
147+
148+
case 1:
149+
if (tegra_sku_info.cpu_speedo_id != 7 &&
150+
tegra_sku_info.cpu_speedo_id != 8)
151+
return 1200000;
152+
153+
fallthrough;
154+
155+
case 2:
156+
if (tegra_sku_info.cpu_speedo_id != 13)
157+
return 1300000;
158+
159+
return 1350000;
160+
161+
default:
162+
return 1250000;
163+
}
164+
}
165+
116166
static int tegra30_voltage_update(struct tegra_regulator_coupler *tegra,
117167
struct regulator_dev *cpu_rdev,
118168
struct regulator_dev *core_rdev)
@@ -168,6 +218,11 @@ static int tegra30_voltage_update(struct tegra_regulator_coupler *tegra,
168218
if (err)
169219
return err;
170220

221+
/* prepare voltage level for suspend */
222+
if (tegra->sys_suspend_mode)
223+
core_min_uV = clamp(tegra30_core_nominal_uV(),
224+
core_min_uV, core_max_uV);
225+
171226
core_uV = regulator_get_voltage_rdev(core_rdev);
172227
if (core_uV < 0)
173228
return core_uV;
@@ -223,6 +278,11 @@ static int tegra30_voltage_update(struct tegra_regulator_coupler *tegra,
223278
if (tegra->sys_reboot_mode)
224279
cpu_min_uV = max(cpu_min_uV, tegra->cpu_min_uV);
225280

281+
/* prepare voltage level for suspend */
282+
if (tegra->sys_suspend_mode)
283+
cpu_min_uV = clamp(tegra30_cpu_nominal_uV(),
284+
cpu_min_uV, cpu_max_uV);
285+
226286
if (core_min_limited_uV > core_uV) {
227287
pr_err("core voltage constraint violated: %d %d %d\n",
228288
core_uV, core_min_limited_uV, cpu_uV);
@@ -292,10 +352,68 @@ static int tegra30_regulator_balance_voltage(struct regulator_coupler *coupler,
292352
}
293353

294354
tegra->sys_reboot_mode = READ_ONCE(tegra->sys_reboot_mode_req);
355+
tegra->sys_suspend_mode = READ_ONCE(tegra->sys_suspend_mode_req);
295356

296357
return tegra30_voltage_update(tegra, cpu_rdev, core_rdev);
297358
}
298359

360+
static int tegra30_regulator_prepare_suspend(struct tegra_regulator_coupler *tegra,
361+
bool sys_suspend_mode)
362+
{
363+
int err;
364+
365+
if (!tegra->core_rdev || !tegra->cpu_rdev)
366+
return 0;
367+
368+
/*
369+
* All power domains are enabled early during resume from suspend
370+
* by GENPD core. Domains like VENC may require a higher voltage
371+
* when enabled during resume from suspend. This also prepares
372+
* hardware for resuming from LP0.
373+
*/
374+
375+
WRITE_ONCE(tegra->sys_suspend_mode_req, sys_suspend_mode);
376+
377+
err = regulator_sync_voltage_rdev(tegra->cpu_rdev);
378+
if (err)
379+
return err;
380+
381+
err = regulator_sync_voltage_rdev(tegra->core_rdev);
382+
if (err)
383+
return err;
384+
385+
return 0;
386+
}
387+
388+
static int tegra30_regulator_suspend(struct notifier_block *notifier,
389+
unsigned long mode, void *arg)
390+
{
391+
struct tegra_regulator_coupler *tegra;
392+
int ret = 0;
393+
394+
tegra = container_of(notifier, struct tegra_regulator_coupler,
395+
suspend_notifier);
396+
397+
switch (mode) {
398+
case PM_HIBERNATION_PREPARE:
399+
case PM_RESTORE_PREPARE:
400+
case PM_SUSPEND_PREPARE:
401+
ret = tegra30_regulator_prepare_suspend(tegra, true);
402+
break;
403+
404+
case PM_POST_HIBERNATION:
405+
case PM_POST_RESTORE:
406+
case PM_POST_SUSPEND:
407+
ret = tegra30_regulator_prepare_suspend(tegra, false);
408+
break;
409+
}
410+
411+
if (ret)
412+
pr_err("failed to prepare regulators: %d\n", ret);
413+
414+
return notifier_from_errno(ret);
415+
}
416+
299417
static int tegra30_regulator_prepare_reboot(struct tegra_regulator_coupler *tegra,
300418
bool sys_reboot_mode)
301419
{
@@ -395,6 +513,7 @@ static struct tegra_regulator_coupler tegra30_coupler = {
395513
.balance_voltage = tegra30_regulator_balance_voltage,
396514
},
397515
.reboot_notifier.notifier_call = tegra30_regulator_reboot,
516+
.suspend_notifier.notifier_call = tegra30_regulator_suspend,
398517
};
399518

400519
static int __init tegra_regulator_coupler_init(void)
@@ -407,6 +526,9 @@ static int __init tegra_regulator_coupler_init(void)
407526
err = register_reboot_notifier(&tegra30_coupler.reboot_notifier);
408527
WARN_ON(err);
409528

529+
err = register_pm_notifier(&tegra30_coupler.suspend_notifier);
530+
WARN_ON(err);
531+
410532
return regulator_coupler_register(&tegra30_coupler.coupler);
411533
}
412534
arch_initcall(tegra_regulator_coupler_init);

0 commit comments

Comments
 (0)