File Index
Files and directories
Sign in
FILE

opal-power.c

File preview with syntax highlighting.

linux-6.19.14/arch/powerpc/platforms/powernv/opal-power.c C
  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 */
 22static 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 */
 56static 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 */
 78static 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 */
117static 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 */
124static 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 */
131static struct notifier_block opal_power_control_nb = {
132	.notifier_call	= opal_power_control_event,
133	.next		= NULL,
134	.priority	= 0,
135};
136
137int __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}
~/ Read-only file preview.