opal-power.c (4058B)
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}