Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Dmitry Osipenko and committed by
Thierry Reding
80ef351c 88724b78

+221
+99
drivers/soc/tegra/regulators-tegra20.c
··· 16 16 #include <linux/regulator/coupler.h> 17 17 #include <linux/regulator/driver.h> 18 18 #include <linux/regulator/machine.h> 19 + #include <linux/suspend.h> 19 20 21 + #include <soc/tegra/fuse.h> 20 22 #include <soc/tegra/pmc.h> 21 23 22 24 struct tegra_regulator_coupler { ··· 27 25 struct regulator_dev *cpu_rdev; 28 26 struct regulator_dev *rtc_rdev; 29 27 struct notifier_block reboot_notifier; 28 + struct notifier_block suspend_notifier; 30 29 int core_min_uV, cpu_min_uV; 31 30 bool sys_reboot_mode_req; 32 31 bool sys_reboot_mode; 32 + bool sys_suspend_mode_req; 33 + bool sys_suspend_mode; 33 34 }; 34 35 35 36 static inline struct tegra_regulator_coupler * ··· 110 105 return 150000; 111 106 } 112 107 108 + static int tegra20_cpu_nominal_uV(void) 109 + { 110 + switch (tegra_sku_info.soc_speedo_id) { 111 + case 0: 112 + return 1100000; 113 + case 1: 114 + return 1025000; 115 + default: 116 + return 1125000; 117 + } 118 + } 119 + 120 + static int tegra20_core_nominal_uV(void) 121 + { 122 + switch (tegra_sku_info.soc_speedo_id) { 123 + default: 124 + return 1225000; 125 + case 2: 126 + return 1300000; 127 + } 128 + } 129 + 113 130 static int tegra20_core_rtc_update(struct tegra_regulator_coupler *tegra, 114 131 struct regulator_dev *core_rdev, 115 132 struct regulator_dev *rtc_rdev, ··· 170 143 PM_SUSPEND_ON); 171 144 if (err) 172 145 return err; 146 + 147 + /* prepare voltage level for suspend */ 148 + if (tegra->sys_suspend_mode) 149 + core_min_uV = clamp(tegra20_core_nominal_uV(), 150 + core_min_uV, core_max_uV); 173 151 174 152 core_uV = regulator_get_voltage_rdev(core_rdev); 175 153 if (core_uV < 0) ··· 311 279 if (tegra->sys_reboot_mode) 312 280 cpu_min_uV = max(cpu_min_uV, tegra->cpu_min_uV); 313 281 282 + /* prepare voltage level for suspend */ 283 + if (tegra->sys_suspend_mode) 284 + cpu_min_uV = clamp(tegra20_cpu_nominal_uV(), 285 + cpu_min_uV, cpu_max_uV); 286 + 314 287 if (cpu_min_uV > cpu_uV) { 315 288 err = tegra20_core_rtc_update(tegra, core_rdev, rtc_rdev, 316 289 cpu_uV, cpu_min_uV); ··· 357 320 } 358 321 359 322 tegra->sys_reboot_mode = READ_ONCE(tegra->sys_reboot_mode_req); 323 + tegra->sys_suspend_mode = READ_ONCE(tegra->sys_suspend_mode_req); 360 324 361 325 if (rdev == cpu_rdev) 362 326 return tegra20_cpu_voltage_update(tegra, cpu_rdev, ··· 370 332 pr_err("changing %s voltage not permitted\n", rdev_get_name(rtc_rdev)); 371 333 372 334 return -EPERM; 335 + } 336 + 337 + static int tegra20_regulator_prepare_suspend(struct tegra_regulator_coupler *tegra, 338 + bool sys_suspend_mode) 339 + { 340 + int err; 341 + 342 + if (!tegra->core_rdev || !tegra->rtc_rdev || !tegra->cpu_rdev) 343 + return 0; 344 + 345 + /* 346 + * All power domains are enabled early during resume from suspend 347 + * by GENPD core. Domains like VENC may require a higher voltage 348 + * when enabled during resume from suspend. This also prepares 349 + * hardware for resuming from LP0. 350 + */ 351 + 352 + WRITE_ONCE(tegra->sys_suspend_mode_req, sys_suspend_mode); 353 + 354 + err = regulator_sync_voltage_rdev(tegra->cpu_rdev); 355 + if (err) 356 + return err; 357 + 358 + err = regulator_sync_voltage_rdev(tegra->core_rdev); 359 + if (err) 360 + return err; 361 + 362 + return 0; 363 + } 364 + 365 + static int tegra20_regulator_suspend(struct notifier_block *notifier, 366 + unsigned long mode, void *arg) 367 + { 368 + struct tegra_regulator_coupler *tegra; 369 + int ret = 0; 370 + 371 + tegra = container_of(notifier, struct tegra_regulator_coupler, 372 + suspend_notifier); 373 + 374 + switch (mode) { 375 + case PM_HIBERNATION_PREPARE: 376 + case PM_RESTORE_PREPARE: 377 + case PM_SUSPEND_PREPARE: 378 + ret = tegra20_regulator_prepare_suspend(tegra, true); 379 + break; 380 + 381 + case PM_POST_HIBERNATION: 382 + case PM_POST_RESTORE: 383 + case PM_POST_SUSPEND: 384 + ret = tegra20_regulator_prepare_suspend(tegra, false); 385 + break; 386 + } 387 + 388 + if (ret) 389 + pr_err("failed to prepare regulators: %d\n", ret); 390 + 391 + return notifier_from_errno(ret); 373 392 } 374 393 375 394 static int tegra20_regulator_prepare_reboot(struct tegra_regulator_coupler *tegra, ··· 539 444 .balance_voltage = tegra20_regulator_balance_voltage, 540 445 }, 541 446 .reboot_notifier.notifier_call = tegra20_regulator_reboot, 447 + .suspend_notifier.notifier_call = tegra20_regulator_suspend, 542 448 }; 543 449 544 450 static int __init tegra_regulator_coupler_init(void) ··· 550 454 return 0; 551 455 552 456 err = register_reboot_notifier(&tegra20_coupler.reboot_notifier); 457 + WARN_ON(err); 458 + 459 + err = register_pm_notifier(&tegra20_coupler.suspend_notifier); 553 460 WARN_ON(err); 554 461 555 462 return regulator_coupler_register(&tegra20_coupler.coupler);
+122
drivers/soc/tegra/regulators-tegra30.c
··· 16 16 #include <linux/regulator/coupler.h> 17 17 #include <linux/regulator/driver.h> 18 18 #include <linux/regulator/machine.h> 19 + #include <linux/suspend.h> 19 20 20 21 #include <soc/tegra/fuse.h> 21 22 #include <soc/tegra/pmc.h> ··· 26 25 struct regulator_dev *core_rdev; 27 26 struct regulator_dev *cpu_rdev; 28 27 struct notifier_block reboot_notifier; 28 + struct notifier_block suspend_notifier; 29 29 int core_min_uV, cpu_min_uV; 30 30 bool sys_reboot_mode_req; 31 31 bool sys_reboot_mode; 32 + bool sys_suspend_mode_req; 33 + bool sys_suspend_mode; 32 34 }; 33 35 34 36 static inline struct tegra_regulator_coupler * ··· 117 113 return -EINVAL; 118 114 } 119 115 116 + static int tegra30_cpu_nominal_uV(void) 117 + { 118 + switch (tegra_sku_info.cpu_speedo_id) { 119 + case 10 ... 11: 120 + return 850000; 121 + 122 + case 9: 123 + return 912000; 124 + 125 + case 1 ... 3: 126 + case 7 ... 8: 127 + return 1050000; 128 + 129 + default: 130 + return 1125000; 131 + 132 + case 4 ... 6: 133 + case 12 ... 13: 134 + return 1237000; 135 + } 136 + } 137 + 138 + static int tegra30_core_nominal_uV(void) 139 + { 140 + switch (tegra_sku_info.soc_speedo_id) { 141 + case 0: 142 + return 1200000; 143 + 144 + case 1: 145 + if (tegra_sku_info.cpu_speedo_id != 7 && 146 + tegra_sku_info.cpu_speedo_id != 8) 147 + return 1200000; 148 + 149 + fallthrough; 150 + 151 + case 2: 152 + if (tegra_sku_info.cpu_speedo_id != 13) 153 + return 1300000; 154 + 155 + return 1350000; 156 + 157 + default: 158 + return 1250000; 159 + } 160 + } 161 + 120 162 static int tegra30_voltage_update(struct tegra_regulator_coupler *tegra, 121 163 struct regulator_dev *cpu_rdev, 122 164 struct regulator_dev *core_rdev) ··· 218 168 if (err) 219 169 return err; 220 170 171 + /* prepare voltage level for suspend */ 172 + if (tegra->sys_suspend_mode) 173 + core_min_uV = clamp(tegra30_core_nominal_uV(), 174 + core_min_uV, core_max_uV); 175 + 221 176 core_uV = regulator_get_voltage_rdev(core_rdev); 222 177 if (core_uV < 0) 223 178 return core_uV; ··· 277 222 /* restore boot voltage level */ 278 223 if (tegra->sys_reboot_mode) 279 224 cpu_min_uV = max(cpu_min_uV, tegra->cpu_min_uV); 225 + 226 + /* prepare voltage level for suspend */ 227 + if (tegra->sys_suspend_mode) 228 + cpu_min_uV = clamp(tegra30_cpu_nominal_uV(), 229 + cpu_min_uV, cpu_max_uV); 280 230 281 231 if (core_min_limited_uV > core_uV) { 282 232 pr_err("core voltage constraint violated: %d %d %d\n", ··· 352 292 } 353 293 354 294 tegra->sys_reboot_mode = READ_ONCE(tegra->sys_reboot_mode_req); 295 + tegra->sys_suspend_mode = READ_ONCE(tegra->sys_suspend_mode_req); 355 296 356 297 return tegra30_voltage_update(tegra, cpu_rdev, core_rdev); 298 + } 299 + 300 + static int tegra30_regulator_prepare_suspend(struct tegra_regulator_coupler *tegra, 301 + bool sys_suspend_mode) 302 + { 303 + int err; 304 + 305 + if (!tegra->core_rdev || !tegra->cpu_rdev) 306 + return 0; 307 + 308 + /* 309 + * All power domains are enabled early during resume from suspend 310 + * by GENPD core. Domains like VENC may require a higher voltage 311 + * when enabled during resume from suspend. This also prepares 312 + * hardware for resuming from LP0. 313 + */ 314 + 315 + WRITE_ONCE(tegra->sys_suspend_mode_req, sys_suspend_mode); 316 + 317 + err = regulator_sync_voltage_rdev(tegra->cpu_rdev); 318 + if (err) 319 + return err; 320 + 321 + err = regulator_sync_voltage_rdev(tegra->core_rdev); 322 + if (err) 323 + return err; 324 + 325 + return 0; 326 + } 327 + 328 + static int tegra30_regulator_suspend(struct notifier_block *notifier, 329 + unsigned long mode, void *arg) 330 + { 331 + struct tegra_regulator_coupler *tegra; 332 + int ret = 0; 333 + 334 + tegra = container_of(notifier, struct tegra_regulator_coupler, 335 + suspend_notifier); 336 + 337 + switch (mode) { 338 + case PM_HIBERNATION_PREPARE: 339 + case PM_RESTORE_PREPARE: 340 + case PM_SUSPEND_PREPARE: 341 + ret = tegra30_regulator_prepare_suspend(tegra, true); 342 + break; 343 + 344 + case PM_POST_HIBERNATION: 345 + case PM_POST_RESTORE: 346 + case PM_POST_SUSPEND: 347 + ret = tegra30_regulator_prepare_suspend(tegra, false); 348 + break; 349 + } 350 + 351 + if (ret) 352 + pr_err("failed to prepare regulators: %d\n", ret); 353 + 354 + return notifier_from_errno(ret); 357 355 } 358 356 359 357 static int tegra30_regulator_prepare_reboot(struct tegra_regulator_coupler *tegra, ··· 513 395 .balance_voltage = tegra30_regulator_balance_voltage, 514 396 }, 515 397 .reboot_notifier.notifier_call = tegra30_regulator_reboot, 398 + .suspend_notifier.notifier_call = tegra30_regulator_suspend, 516 399 }; 517 400 518 401 static int __init tegra_regulator_coupler_init(void) ··· 524 405 return 0; 525 406 526 407 err = register_reboot_notifier(&tegra30_coupler.reboot_notifier); 408 + WARN_ON(err); 409 + 410 + err = register_pm_notifier(&tegra30_coupler.suspend_notifier); 527 411 WARN_ON(err); 528 412 529 413 return regulator_coupler_register(&tegra30_coupler.coupler);