1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2006-2007 PA Semi, Inc |
4 | * |
5 | * Maintained by: Olof Johansson <olof@lixom.net> |
6 | */ |
7 | |
8 | #undef DEBUG |
9 | |
10 | #include <linux/kernel.h> |
11 | #include <linux/string.h> |
12 | #include <linux/irq.h> |
13 | |
14 | #include <asm/machdep.h> |
15 | #include <asm/reg.h> |
16 | #include <asm/smp.h> |
17 | |
18 | #include "pasemi.h" |
19 | |
20 | struct sleep_mode { |
21 | char *name; |
22 | void (*entry)(void); |
23 | }; |
24 | |
25 | static struct sleep_mode modes[] = { |
26 | { .name = "spin" , .entry = &idle_spin }, |
27 | { .name = "doze" , .entry = &idle_doze }, |
28 | }; |
29 | |
30 | static int current_mode = 0; |
31 | |
32 | static int pasemi_system_reset_exception(struct pt_regs *regs) |
33 | { |
34 | /* If we were woken up from power savings, we need to return |
35 | * to the calling function, since nip is not saved across |
36 | * all modes. |
37 | */ |
38 | |
39 | if (regs->msr & SRR1_WAKEMASK) |
40 | regs_set_return_ip(regs, regs->link); |
41 | |
42 | switch (regs->msr & SRR1_WAKEMASK) { |
43 | case SRR1_WAKEDEC: |
44 | set_dec(1); |
45 | break; |
46 | case SRR1_WAKEEE: |
47 | /* |
48 | * Handle these when interrupts get re-enabled and we take |
49 | * them as regular exceptions. We are in an NMI context |
50 | * and can't handle these here. |
51 | */ |
52 | break; |
53 | default: |
54 | /* do system reset */ |
55 | return 0; |
56 | } |
57 | |
58 | /* Set higher astate since we come out of power savings at 0 */ |
59 | restore_astate(cpu: hard_smp_processor_id()); |
60 | |
61 | /* everything handled */ |
62 | regs_set_recoverable(regs); |
63 | return 1; |
64 | } |
65 | |
66 | static int __init pasemi_idle_init(void) |
67 | { |
68 | #ifndef CONFIG_PPC_PASEMI_CPUFREQ |
69 | pr_warn("No cpufreq driver, powersavings modes disabled\n" ); |
70 | current_mode = 0; |
71 | #endif |
72 | |
73 | ppc_md.system_reset_exception = pasemi_system_reset_exception; |
74 | ppc_md.power_save = modes[current_mode].entry; |
75 | pr_info("Using PA6T idle loop (%s)\n" , modes[current_mode].name); |
76 | |
77 | return 0; |
78 | } |
79 | machine_late_initcall(pasemi, pasemi_idle_init); |
80 | |
81 | static int __init idle_param(char *p) |
82 | { |
83 | int i; |
84 | for (i = 0; i < ARRAY_SIZE(modes); i++) { |
85 | if (!strcmp(modes[i].name, p)) { |
86 | current_mode = i; |
87 | break; |
88 | } |
89 | } |
90 | return 0; |
91 | } |
92 | |
93 | early_param("idle" , idle_param); |
94 | |