Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * PowerNV OPAL power control for graceful shutdown handling
0004  *
0005  * Copyright 2015 IBM Corp.
0006  */
0007 
0008 #define pr_fmt(fmt) "opal-power: "  fmt
0009 
0010 #include <linux/kernel.h>
0011 #include <linux/reboot.h>
0012 #include <linux/notifier.h>
0013 #include <linux/of.h>
0014 
0015 #include <asm/opal.h>
0016 #include <asm/machdep.h>
0017 
0018 #define SOFT_OFF 0x00
0019 #define SOFT_REBOOT 0x01
0020 
0021 /* Detect EPOW event */
0022 static bool detect_epow(void)
0023 {
0024     u16 epow;
0025     int i, rc;
0026     __be16 epow_classes;
0027     __be16 opal_epow_status[OPAL_SYSEPOW_MAX] = {0};
0028 
0029     /*
0030     * Check for EPOW event. Kernel sends supported EPOW classes info
0031     * to OPAL. OPAL returns EPOW info along with classes present.
0032     */
0033     epow_classes = cpu_to_be16(OPAL_SYSEPOW_MAX);
0034     rc = opal_get_epow_status(opal_epow_status, &epow_classes);
0035     if (rc != OPAL_SUCCESS) {
0036         pr_err("Failed to get EPOW event information\n");
0037         return false;
0038     }
0039 
0040     /* Look for EPOW events present */
0041     for (i = 0; i < be16_to_cpu(epow_classes); i++) {
0042         epow = be16_to_cpu(opal_epow_status[i]);
0043 
0044         /* Filter events which do not need shutdown. */
0045         if (i == OPAL_SYSEPOW_POWER)
0046             epow &= ~(OPAL_SYSPOWER_CHNG | OPAL_SYSPOWER_FAIL |
0047                     OPAL_SYSPOWER_INCL);
0048         if (epow)
0049             return true;
0050     }
0051 
0052     return false;
0053 }
0054 
0055 /* Check for existing EPOW, DPO events */
0056 static bool __init poweroff_pending(void)
0057 {
0058     int rc;
0059     __be64 opal_dpo_timeout;
0060 
0061     /* Check for DPO event */
0062     rc = opal_get_dpo_status(&opal_dpo_timeout);
0063     if (rc == OPAL_SUCCESS) {
0064         pr_info("Existing DPO event detected.\n");
0065         return true;
0066     }
0067 
0068     /* Check for EPOW event */
0069     if (detect_epow()) {
0070         pr_info("Existing EPOW event detected.\n");
0071         return true;
0072     }
0073 
0074     return false;
0075 }
0076 
0077 /* OPAL power-control events notifier */
0078 static int opal_power_control_event(struct notifier_block *nb,
0079                     unsigned long msg_type, void *msg)
0080 {
0081     uint64_t type;
0082 
0083     switch (msg_type) {
0084     case OPAL_MSG_EPOW:
0085         if (detect_epow()) {
0086             pr_info("EPOW msg received. Powering off system\n");
0087             orderly_poweroff(true);
0088         }
0089         break;
0090     case OPAL_MSG_DPO:
0091         pr_info("DPO msg received. Powering off system\n");
0092         orderly_poweroff(true);
0093         break;
0094     case OPAL_MSG_SHUTDOWN:
0095         type = be64_to_cpu(((struct opal_msg *)msg)->params[0]);
0096         switch (type) {
0097         case SOFT_REBOOT:
0098             pr_info("Reboot requested\n");
0099             orderly_reboot();
0100             break;
0101         case SOFT_OFF:
0102             pr_info("Poweroff requested\n");
0103             orderly_poweroff(true);
0104             break;
0105         default:
0106             pr_err("Unknown power-control type %llu\n", type);
0107         }
0108         break;
0109     default:
0110         pr_err("Unknown OPAL message type %lu\n", msg_type);
0111     }
0112 
0113     return 0;
0114 }
0115 
0116 /* OPAL EPOW event notifier block */
0117 static struct notifier_block opal_epow_nb = {
0118     .notifier_call  = opal_power_control_event,
0119     .next       = NULL,
0120     .priority   = 0,
0121 };
0122 
0123 /* OPAL DPO event notifier block */
0124 static struct notifier_block opal_dpo_nb = {
0125     .notifier_call  = opal_power_control_event,
0126     .next       = NULL,
0127     .priority   = 0,
0128 };
0129 
0130 /* OPAL power-control event notifier block */
0131 static struct notifier_block opal_power_control_nb = {
0132     .notifier_call  = opal_power_control_event,
0133     .next       = NULL,
0134     .priority   = 0,
0135 };
0136 
0137 int __init opal_power_control_init(void)
0138 {
0139     int ret, supported = 0;
0140     struct device_node *np;
0141 
0142     /* Register OPAL power-control events notifier */
0143     ret = opal_message_notifier_register(OPAL_MSG_SHUTDOWN,
0144                         &opal_power_control_nb);
0145     if (ret)
0146         pr_err("Failed to register SHUTDOWN notifier, ret = %d\n", ret);
0147 
0148     /* Determine OPAL EPOW, DPO support */
0149     np = of_find_node_by_path("/ibm,opal/epow");
0150     if (np) {
0151         supported = of_device_is_compatible(np, "ibm,opal-v3-epow");
0152         of_node_put(np);
0153     }
0154 
0155     if (!supported)
0156         return 0;
0157     pr_info("OPAL EPOW, DPO support detected.\n");
0158 
0159     /* Register EPOW event notifier */
0160     ret = opal_message_notifier_register(OPAL_MSG_EPOW, &opal_epow_nb);
0161     if (ret)
0162         pr_err("Failed to register EPOW notifier, ret = %d\n", ret);
0163 
0164     /* Register DPO event notifier */
0165     ret = opal_message_notifier_register(OPAL_MSG_DPO, &opal_dpo_nb);
0166     if (ret)
0167         pr_err("Failed to register DPO notifier, ret = %d\n", ret);
0168 
0169     /* Check for any pending EPOW or DPO events. */
0170     if (poweroff_pending())
0171         orderly_poweroff(true);
0172 
0173     return 0;
0174 }