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