.. | .. |
---|
2 | 2 | /* |
---|
3 | 3 | * AM33XX Arch Power Management Routines |
---|
4 | 4 | * |
---|
5 | | - * Copyright (C) 2016-2018 Texas Instruments Incorporated - http://www.ti.com/ |
---|
| 5 | + * Copyright (C) 2016-2018 Texas Instruments Incorporated - https://www.ti.com/ |
---|
6 | 6 | * Dave Gerlach |
---|
7 | 7 | */ |
---|
8 | 8 | |
---|
| 9 | +#include <linux/cpuidle.h> |
---|
| 10 | +#include <linux/platform_data/pm33xx.h> |
---|
| 11 | +#include <linux/suspend.h> |
---|
| 12 | +#include <asm/cpuidle.h> |
---|
9 | 13 | #include <asm/smp_scu.h> |
---|
10 | 14 | #include <asm/suspend.h> |
---|
11 | 15 | #include <linux/errno.h> |
---|
12 | | -#include <linux/platform_data/pm33xx.h> |
---|
| 16 | +#include <linux/clk.h> |
---|
| 17 | +#include <linux/cpu.h> |
---|
| 18 | +#include <linux/platform_data/gpio-omap.h> |
---|
| 19 | +#include <linux/pinctrl/pinmux.h> |
---|
| 20 | +#include <linux/wkup_m3_ipc.h> |
---|
| 21 | +#include <linux/of.h> |
---|
| 22 | +#include <linux/rtc.h> |
---|
13 | 23 | |
---|
14 | 24 | #include "cm33xx.h" |
---|
15 | 25 | #include "common.h" |
---|
16 | 26 | #include "control.h" |
---|
17 | 27 | #include "clockdomain.h" |
---|
18 | 28 | #include "iomap.h" |
---|
19 | | -#include "omap_hwmod.h" |
---|
20 | 29 | #include "pm.h" |
---|
21 | 30 | #include "powerdomain.h" |
---|
22 | 31 | #include "prm33xx.h" |
---|
23 | 32 | #include "soc.h" |
---|
24 | 33 | #include "sram.h" |
---|
| 34 | +#include "omap-secure.h" |
---|
25 | 35 | |
---|
26 | 36 | static struct powerdomain *cefuse_pwrdm, *gfx_pwrdm, *per_pwrdm, *mpu_pwrdm; |
---|
27 | 37 | static struct clockdomain *gfx_l4ls_clkdm; |
---|
28 | 38 | static void __iomem *scu_base; |
---|
29 | | -static struct omap_hwmod *rtc_oh; |
---|
30 | 39 | |
---|
31 | | -static int __init am43xx_map_scu(void) |
---|
| 40 | +static int (*idle_fn)(u32 wfi_flags); |
---|
| 41 | + |
---|
| 42 | +struct amx3_idle_state { |
---|
| 43 | + int wfi_flags; |
---|
| 44 | +}; |
---|
| 45 | + |
---|
| 46 | +static struct amx3_idle_state *idle_states; |
---|
| 47 | + |
---|
| 48 | +static int am43xx_map_scu(void) |
---|
32 | 49 | { |
---|
33 | 50 | scu_base = ioremap(scu_a9_get_base(), SZ_256); |
---|
34 | 51 | |
---|
.. | .. |
---|
38 | 55 | return 0; |
---|
39 | 56 | } |
---|
40 | 57 | |
---|
41 | | -static int amx3_common_init(void) |
---|
| 58 | +static int am33xx_check_off_mode_enable(void) |
---|
| 59 | +{ |
---|
| 60 | + if (enable_off_mode) |
---|
| 61 | + pr_warn("WARNING: This platform does not support off-mode, entering DeepSleep suspend.\n"); |
---|
| 62 | + |
---|
| 63 | + /* off mode not supported on am335x so return 0 always */ |
---|
| 64 | + return 0; |
---|
| 65 | +} |
---|
| 66 | + |
---|
| 67 | +static int am43xx_check_off_mode_enable(void) |
---|
| 68 | +{ |
---|
| 69 | + /* |
---|
| 70 | + * Check for am437x-gp-evm which has the right Hardware design to |
---|
| 71 | + * support this mode reliably. |
---|
| 72 | + */ |
---|
| 73 | + if (of_machine_is_compatible("ti,am437x-gp-evm") && enable_off_mode) |
---|
| 74 | + return enable_off_mode; |
---|
| 75 | + else if (enable_off_mode) |
---|
| 76 | + pr_warn("WARNING: This platform does not support off-mode, entering DeepSleep suspend.\n"); |
---|
| 77 | + |
---|
| 78 | + return 0; |
---|
| 79 | +} |
---|
| 80 | + |
---|
| 81 | +static int amx3_common_init(int (*idle)(u32 wfi_flags)) |
---|
42 | 82 | { |
---|
43 | 83 | gfx_pwrdm = pwrdm_lookup("gfx_pwrdm"); |
---|
44 | 84 | per_pwrdm = pwrdm_lookup("per_pwrdm"); |
---|
.. | .. |
---|
58 | 98 | else |
---|
59 | 99 | omap_set_pwrdm_state(cefuse_pwrdm, PWRDM_POWER_OFF); |
---|
60 | 100 | |
---|
| 101 | + idle_fn = idle; |
---|
| 102 | + |
---|
61 | 103 | return 0; |
---|
62 | 104 | } |
---|
63 | 105 | |
---|
64 | | -static int am33xx_suspend_init(void) |
---|
| 106 | +static int am33xx_suspend_init(int (*idle)(u32 wfi_flags)) |
---|
65 | 107 | { |
---|
66 | 108 | int ret; |
---|
67 | 109 | |
---|
.. | .. |
---|
72 | 114 | return -ENODEV; |
---|
73 | 115 | } |
---|
74 | 116 | |
---|
75 | | - ret = amx3_common_init(); |
---|
| 117 | + ret = amx3_common_init(idle); |
---|
76 | 118 | |
---|
77 | 119 | return ret; |
---|
78 | 120 | } |
---|
79 | 121 | |
---|
80 | | -static int am43xx_suspend_init(void) |
---|
| 122 | +static int am43xx_suspend_init(int (*idle)(u32 wfi_flags)) |
---|
81 | 123 | { |
---|
82 | 124 | int ret = 0; |
---|
83 | 125 | |
---|
.. | .. |
---|
87 | 129 | return ret; |
---|
88 | 130 | } |
---|
89 | 131 | |
---|
90 | | - ret = amx3_common_init(); |
---|
| 132 | + ret = amx3_common_init(idle); |
---|
91 | 133 | |
---|
92 | 134 | return ret; |
---|
| 135 | +} |
---|
| 136 | + |
---|
| 137 | +static int amx3_suspend_deinit(void) |
---|
| 138 | +{ |
---|
| 139 | + idle_fn = NULL; |
---|
| 140 | + return 0; |
---|
93 | 141 | } |
---|
94 | 142 | |
---|
95 | 143 | static void amx3_pre_suspend_common(void) |
---|
.. | .. |
---|
137 | 185 | { |
---|
138 | 186 | int ret = 0; |
---|
139 | 187 | |
---|
| 188 | + /* Suspend secure side on HS devices */ |
---|
| 189 | + if (omap_type() != OMAP2_DEVICE_TYPE_GP) { |
---|
| 190 | + if (optee_available) |
---|
| 191 | + omap_smccc_smc(AM43xx_PPA_SVC_PM_SUSPEND, 0); |
---|
| 192 | + else |
---|
| 193 | + omap_secure_dispatcher(AM43xx_PPA_SVC_PM_SUSPEND, |
---|
| 194 | + FLAG_START_CRITICAL, |
---|
| 195 | + 0, 0, 0, 0, 0); |
---|
| 196 | + } |
---|
| 197 | + |
---|
140 | 198 | amx3_pre_suspend_common(); |
---|
141 | 199 | scu_power_mode(scu_base, SCU_PM_POWEROFF); |
---|
142 | 200 | ret = cpu_suspend(args, fn); |
---|
143 | 201 | scu_power_mode(scu_base, SCU_PM_NORMAL); |
---|
144 | | - amx3_post_suspend_common(); |
---|
| 202 | + |
---|
| 203 | + if (!am43xx_check_off_mode_enable()) |
---|
| 204 | + amx3_post_suspend_common(); |
---|
| 205 | + |
---|
| 206 | + /* |
---|
| 207 | + * Resume secure side on HS devices. |
---|
| 208 | + * |
---|
| 209 | + * Note that even on systems with OP-TEE available this resume call is |
---|
| 210 | + * issued to the ROM. This is because upon waking from suspend the ROM |
---|
| 211 | + * is restored as the secure monitor. On systems with OP-TEE ROM will |
---|
| 212 | + * restore OP-TEE during this call. |
---|
| 213 | + */ |
---|
| 214 | + if (omap_type() != OMAP2_DEVICE_TYPE_GP) |
---|
| 215 | + omap_secure_dispatcher(AM43xx_PPA_SVC_PM_RESUME, |
---|
| 216 | + FLAG_START_CRITICAL, |
---|
| 217 | + 0, 0, 0, 0, 0); |
---|
145 | 218 | |
---|
146 | 219 | return ret; |
---|
147 | 220 | } |
---|
| 221 | + |
---|
| 222 | +static int am33xx_cpu_suspend(int (*fn)(unsigned long), unsigned long args) |
---|
| 223 | +{ |
---|
| 224 | + int ret = 0; |
---|
| 225 | + |
---|
| 226 | + if (omap_irq_pending() || need_resched()) |
---|
| 227 | + return ret; |
---|
| 228 | + |
---|
| 229 | + ret = cpu_suspend(args, fn); |
---|
| 230 | + |
---|
| 231 | + return ret; |
---|
| 232 | +} |
---|
| 233 | + |
---|
| 234 | +static int am43xx_cpu_suspend(int (*fn)(unsigned long), unsigned long args) |
---|
| 235 | +{ |
---|
| 236 | + int ret = 0; |
---|
| 237 | + |
---|
| 238 | + if (!scu_base) |
---|
| 239 | + return 0; |
---|
| 240 | + |
---|
| 241 | + scu_power_mode(scu_base, SCU_PM_DORMANT); |
---|
| 242 | + ret = cpu_suspend(args, fn); |
---|
| 243 | + scu_power_mode(scu_base, SCU_PM_NORMAL); |
---|
| 244 | + |
---|
| 245 | + return ret; |
---|
| 246 | +} |
---|
| 247 | + |
---|
| 248 | +static void amx3_begin_suspend(void) |
---|
| 249 | +{ |
---|
| 250 | + cpu_idle_poll_ctrl(true); |
---|
| 251 | +} |
---|
| 252 | + |
---|
| 253 | +static void amx3_finish_suspend(void) |
---|
| 254 | +{ |
---|
| 255 | + cpu_idle_poll_ctrl(false); |
---|
| 256 | +} |
---|
| 257 | + |
---|
148 | 258 | |
---|
149 | 259 | static struct am33xx_pm_sram_addr *amx3_get_sram_addrs(void) |
---|
150 | 260 | { |
---|
.. | .. |
---|
156 | 266 | return NULL; |
---|
157 | 267 | } |
---|
158 | 268 | |
---|
159 | | -void __iomem *am43xx_get_rtc_base_addr(void) |
---|
| 269 | +static void am43xx_save_context(void) |
---|
160 | 270 | { |
---|
161 | | - rtc_oh = omap_hwmod_lookup("rtc"); |
---|
| 271 | +} |
---|
162 | 272 | |
---|
163 | | - return omap_hwmod_get_mpu_rt_va(rtc_oh); |
---|
| 273 | +static void am33xx_save_context(void) |
---|
| 274 | +{ |
---|
| 275 | + omap_intc_save_context(); |
---|
| 276 | +} |
---|
| 277 | + |
---|
| 278 | +static void am33xx_restore_context(void) |
---|
| 279 | +{ |
---|
| 280 | + omap_intc_restore_context(); |
---|
| 281 | +} |
---|
| 282 | + |
---|
| 283 | +static void am43xx_restore_context(void) |
---|
| 284 | +{ |
---|
| 285 | + /* |
---|
| 286 | + * HACK: restore dpll_per_clkdcoldo register contents, to avoid |
---|
| 287 | + * breaking suspend-resume |
---|
| 288 | + */ |
---|
| 289 | + writel_relaxed(0x0, AM33XX_L4_WK_IO_ADDRESS(0x44df2e14)); |
---|
164 | 290 | } |
---|
165 | 291 | |
---|
166 | 292 | static struct am33xx_pm_platform_data am33xx_ops = { |
---|
167 | 293 | .init = am33xx_suspend_init, |
---|
| 294 | + .deinit = amx3_suspend_deinit, |
---|
168 | 295 | .soc_suspend = am33xx_suspend, |
---|
| 296 | + .cpu_suspend = am33xx_cpu_suspend, |
---|
| 297 | + .begin_suspend = amx3_begin_suspend, |
---|
| 298 | + .finish_suspend = amx3_finish_suspend, |
---|
169 | 299 | .get_sram_addrs = amx3_get_sram_addrs, |
---|
170 | | - .get_rtc_base_addr = am43xx_get_rtc_base_addr, |
---|
| 300 | + .save_context = am33xx_save_context, |
---|
| 301 | + .restore_context = am33xx_restore_context, |
---|
| 302 | + .check_off_mode_enable = am33xx_check_off_mode_enable, |
---|
171 | 303 | }; |
---|
172 | 304 | |
---|
173 | 305 | static struct am33xx_pm_platform_data am43xx_ops = { |
---|
174 | 306 | .init = am43xx_suspend_init, |
---|
| 307 | + .deinit = amx3_suspend_deinit, |
---|
175 | 308 | .soc_suspend = am43xx_suspend, |
---|
| 309 | + .cpu_suspend = am43xx_cpu_suspend, |
---|
| 310 | + .begin_suspend = amx3_begin_suspend, |
---|
| 311 | + .finish_suspend = amx3_finish_suspend, |
---|
176 | 312 | .get_sram_addrs = amx3_get_sram_addrs, |
---|
177 | | - .get_rtc_base_addr = am43xx_get_rtc_base_addr, |
---|
| 313 | + .save_context = am43xx_save_context, |
---|
| 314 | + .restore_context = am43xx_restore_context, |
---|
| 315 | + .check_off_mode_enable = am43xx_check_off_mode_enable, |
---|
178 | 316 | }; |
---|
179 | 317 | |
---|
180 | 318 | static struct am33xx_pm_platform_data *am33xx_pm_get_pdata(void) |
---|
.. | .. |
---|
186 | 324 | else |
---|
187 | 325 | return NULL; |
---|
188 | 326 | } |
---|
| 327 | + |
---|
| 328 | +#ifdef CONFIG_SUSPEND |
---|
| 329 | +/* |
---|
| 330 | + * Block system suspend initially. Later on pm33xx sets up it's own |
---|
| 331 | + * platform_suspend_ops after probe. That depends also on loaded |
---|
| 332 | + * wkup_m3_ipc and booted am335x-pm-firmware.elf. |
---|
| 333 | + */ |
---|
| 334 | +static int amx3_suspend_block(suspend_state_t state) |
---|
| 335 | +{ |
---|
| 336 | + pr_warn("PM not initialized for pm33xx, wkup_m3_ipc, or am335x-pm-firmware.elf\n"); |
---|
| 337 | + |
---|
| 338 | + return -EINVAL; |
---|
| 339 | +} |
---|
| 340 | + |
---|
| 341 | +static int amx3_pm_valid(suspend_state_t state) |
---|
| 342 | +{ |
---|
| 343 | + switch (state) { |
---|
| 344 | + case PM_SUSPEND_STANDBY: |
---|
| 345 | + return 1; |
---|
| 346 | + default: |
---|
| 347 | + return 0; |
---|
| 348 | + } |
---|
| 349 | +} |
---|
| 350 | + |
---|
| 351 | +static const struct platform_suspend_ops amx3_blocked_pm_ops = { |
---|
| 352 | + .begin = amx3_suspend_block, |
---|
| 353 | + .valid = amx3_pm_valid, |
---|
| 354 | +}; |
---|
| 355 | + |
---|
| 356 | +static void __init amx3_block_suspend(void) |
---|
| 357 | +{ |
---|
| 358 | + suspend_set_ops(&amx3_blocked_pm_ops); |
---|
| 359 | +} |
---|
| 360 | +#else |
---|
| 361 | +static inline void amx3_block_suspend(void) |
---|
| 362 | +{ |
---|
| 363 | +} |
---|
| 364 | +#endif /* CONFIG_SUSPEND */ |
---|
189 | 365 | |
---|
190 | 366 | int __init amx3_common_pm_init(void) |
---|
191 | 367 | { |
---|
.. | .. |
---|
200 | 376 | devinfo.size_data = sizeof(*pdata); |
---|
201 | 377 | devinfo.id = -1; |
---|
202 | 378 | platform_device_register_full(&devinfo); |
---|
| 379 | + amx3_block_suspend(); |
---|
203 | 380 | |
---|
204 | 381 | return 0; |
---|
205 | 382 | } |
---|
| 383 | + |
---|
| 384 | +static int __init amx3_idle_init(struct device_node *cpu_node, int cpu) |
---|
| 385 | +{ |
---|
| 386 | + struct device_node *state_node; |
---|
| 387 | + struct amx3_idle_state states[CPUIDLE_STATE_MAX]; |
---|
| 388 | + int i; |
---|
| 389 | + int state_count = 1; |
---|
| 390 | + |
---|
| 391 | + for (i = 0; ; i++) { |
---|
| 392 | + state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i); |
---|
| 393 | + if (!state_node) |
---|
| 394 | + break; |
---|
| 395 | + |
---|
| 396 | + if (!of_device_is_available(state_node)) |
---|
| 397 | + continue; |
---|
| 398 | + |
---|
| 399 | + if (i == CPUIDLE_STATE_MAX) { |
---|
| 400 | + pr_warn("%s: cpuidle states reached max possible\n", |
---|
| 401 | + __func__); |
---|
| 402 | + break; |
---|
| 403 | + } |
---|
| 404 | + |
---|
| 405 | + states[state_count].wfi_flags = 0; |
---|
| 406 | + |
---|
| 407 | + if (of_property_read_bool(state_node, "ti,idle-wkup-m3")) |
---|
| 408 | + states[state_count].wfi_flags |= WFI_FLAG_WAKE_M3 | |
---|
| 409 | + WFI_FLAG_FLUSH_CACHE; |
---|
| 410 | + |
---|
| 411 | + state_count++; |
---|
| 412 | + } |
---|
| 413 | + |
---|
| 414 | + idle_states = kcalloc(state_count, sizeof(*idle_states), GFP_KERNEL); |
---|
| 415 | + if (!idle_states) |
---|
| 416 | + return -ENOMEM; |
---|
| 417 | + |
---|
| 418 | + for (i = 1; i < state_count; i++) |
---|
| 419 | + idle_states[i].wfi_flags = states[i].wfi_flags; |
---|
| 420 | + |
---|
| 421 | + return 0; |
---|
| 422 | +} |
---|
| 423 | + |
---|
| 424 | +static int amx3_idle_enter(unsigned long index) |
---|
| 425 | +{ |
---|
| 426 | + struct amx3_idle_state *idle_state = &idle_states[index]; |
---|
| 427 | + |
---|
| 428 | + if (!idle_state) |
---|
| 429 | + return -EINVAL; |
---|
| 430 | + |
---|
| 431 | + if (idle_fn) |
---|
| 432 | + idle_fn(idle_state->wfi_flags); |
---|
| 433 | + |
---|
| 434 | + return 0; |
---|
| 435 | +} |
---|
| 436 | + |
---|
| 437 | +static struct cpuidle_ops amx3_cpuidle_ops __initdata = { |
---|
| 438 | + .init = amx3_idle_init, |
---|
| 439 | + .suspend = amx3_idle_enter, |
---|
| 440 | +}; |
---|
| 441 | + |
---|
| 442 | +CPUIDLE_METHOD_OF_DECLARE(pm33xx_idle, "ti,am3352", &amx3_cpuidle_ops); |
---|
| 443 | +CPUIDLE_METHOD_OF_DECLARE(pm43xx_idle, "ti,am4372", &amx3_cpuidle_ops); |
---|