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
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.