Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/powerpc/platforms/powernv/opal-power.c
26481 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* PowerNV OPAL power control for graceful shutdown handling
4
*
5
* Copyright 2015 IBM Corp.
6
*/
7
8
#define pr_fmt(fmt) "opal-power: " fmt
9
10
#include <linux/kernel.h>
11
#include <linux/reboot.h>
12
#include <linux/notifier.h>
13
#include <linux/of.h>
14
15
#include <asm/opal.h>
16
#include <asm/machdep.h>
17
18
#define SOFT_OFF 0x00
19
#define SOFT_REBOOT 0x01
20
21
/* Detect EPOW event */
22
static bool detect_epow(void)
23
{
24
u16 epow;
25
int i, rc;
26
__be16 epow_classes;
27
__be16 opal_epow_status[OPAL_SYSEPOW_MAX] = {0};
28
29
/*
30
* Check for EPOW event. Kernel sends supported EPOW classes info
31
* to OPAL. OPAL returns EPOW info along with classes present.
32
*/
33
epow_classes = cpu_to_be16(OPAL_SYSEPOW_MAX);
34
rc = opal_get_epow_status(opal_epow_status, &epow_classes);
35
if (rc != OPAL_SUCCESS) {
36
pr_err("Failed to get EPOW event information\n");
37
return false;
38
}
39
40
/* Look for EPOW events present */
41
for (i = 0; i < be16_to_cpu(epow_classes); i++) {
42
epow = be16_to_cpu(opal_epow_status[i]);
43
44
/* Filter events which do not need shutdown. */
45
if (i == OPAL_SYSEPOW_POWER)
46
epow &= ~(OPAL_SYSPOWER_CHNG | OPAL_SYSPOWER_FAIL |
47
OPAL_SYSPOWER_INCL);
48
if (epow)
49
return true;
50
}
51
52
return false;
53
}
54
55
/* Check for existing EPOW, DPO events */
56
static bool __init poweroff_pending(void)
57
{
58
int rc;
59
__be64 opal_dpo_timeout;
60
61
/* Check for DPO event */
62
rc = opal_get_dpo_status(&opal_dpo_timeout);
63
if (rc == OPAL_SUCCESS) {
64
pr_info("Existing DPO event detected.\n");
65
return true;
66
}
67
68
/* Check for EPOW event */
69
if (detect_epow()) {
70
pr_info("Existing EPOW event detected.\n");
71
return true;
72
}
73
74
return false;
75
}
76
77
/* OPAL power-control events notifier */
78
static int opal_power_control_event(struct notifier_block *nb,
79
unsigned long msg_type, void *msg)
80
{
81
uint64_t type;
82
83
switch (msg_type) {
84
case OPAL_MSG_EPOW:
85
if (detect_epow()) {
86
pr_info("EPOW msg received. Powering off system\n");
87
orderly_poweroff(true);
88
}
89
break;
90
case OPAL_MSG_DPO:
91
pr_info("DPO msg received. Powering off system\n");
92
orderly_poweroff(true);
93
break;
94
case OPAL_MSG_SHUTDOWN:
95
type = be64_to_cpu(((struct opal_msg *)msg)->params[0]);
96
switch (type) {
97
case SOFT_REBOOT:
98
pr_info("Reboot requested\n");
99
orderly_reboot();
100
break;
101
case SOFT_OFF:
102
pr_info("Poweroff requested\n");
103
orderly_poweroff(true);
104
break;
105
default:
106
pr_err("Unknown power-control type %llu\n", type);
107
}
108
break;
109
default:
110
pr_err("Unknown OPAL message type %lu\n", msg_type);
111
}
112
113
return 0;
114
}
115
116
/* OPAL EPOW event notifier block */
117
static struct notifier_block opal_epow_nb = {
118
.notifier_call = opal_power_control_event,
119
.next = NULL,
120
.priority = 0,
121
};
122
123
/* OPAL DPO event notifier block */
124
static struct notifier_block opal_dpo_nb = {
125
.notifier_call = opal_power_control_event,
126
.next = NULL,
127
.priority = 0,
128
};
129
130
/* OPAL power-control event notifier block */
131
static struct notifier_block opal_power_control_nb = {
132
.notifier_call = opal_power_control_event,
133
.next = NULL,
134
.priority = 0,
135
};
136
137
int __init opal_power_control_init(void)
138
{
139
int ret, supported = 0;
140
struct device_node *np;
141
142
/* Register OPAL power-control events notifier */
143
ret = opal_message_notifier_register(OPAL_MSG_SHUTDOWN,
144
&opal_power_control_nb);
145
if (ret)
146
pr_err("Failed to register SHUTDOWN notifier, ret = %d\n", ret);
147
148
/* Determine OPAL EPOW, DPO support */
149
np = of_find_node_by_path("/ibm,opal/epow");
150
if (np) {
151
supported = of_device_is_compatible(np, "ibm,opal-v3-epow");
152
of_node_put(np);
153
}
154
155
if (!supported)
156
return 0;
157
pr_info("OPAL EPOW, DPO support detected.\n");
158
159
/* Register EPOW event notifier */
160
ret = opal_message_notifier_register(OPAL_MSG_EPOW, &opal_epow_nb);
161
if (ret)
162
pr_err("Failed to register EPOW notifier, ret = %d\n", ret);
163
164
/* Register DPO event notifier */
165
ret = opal_message_notifier_register(OPAL_MSG_DPO, &opal_dpo_nb);
166
if (ret)
167
pr_err("Failed to register DPO notifier, ret = %d\n", ret);
168
169
/* Check for any pending EPOW or DPO events. */
170
if (poweroff_pending())
171
orderly_poweroff(true);
172
173
return 0;
174
}
175
176