These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / arch / powerpc / platforms / powernv / opal-power.c
index ac46c2c..58dc330 100644 (file)
@@ -9,9 +9,12 @@
  * 2 of the License, or (at your option) any later version.
  */
 
+#define pr_fmt(fmt)    "opal-power: "  fmt
+
 #include <linux/kernel.h>
 #include <linux/reboot.h>
 #include <linux/notifier.h>
+#include <linux/of.h>
 
 #include <asm/opal.h>
 #include <asm/machdep.h>
 #define SOFT_OFF 0x00
 #define SOFT_REBOOT 0x01
 
+/* Detect EPOW event */
+static bool detect_epow(void)
+{
+       u16 epow;
+       int i, rc;
+       __be16 epow_classes;
+       __be16 opal_epow_status[OPAL_SYSEPOW_MAX] = {0};
+
+       /*
+       * Check for EPOW event. Kernel sends supported EPOW classes info
+       * to OPAL. OPAL returns EPOW info along with classes present.
+       */
+       epow_classes = cpu_to_be16(OPAL_SYSEPOW_MAX);
+       rc = opal_get_epow_status(opal_epow_status, &epow_classes);
+       if (rc != OPAL_SUCCESS) {
+               pr_err("Failed to get EPOW event information\n");
+               return false;
+       }
+
+       /* Look for EPOW events present */
+       for (i = 0; i < be16_to_cpu(epow_classes); i++) {
+               epow = be16_to_cpu(opal_epow_status[i]);
+
+               /* Filter events which do not need shutdown. */
+               if (i == OPAL_SYSEPOW_POWER)
+                       epow &= ~(OPAL_SYSPOWER_CHNG | OPAL_SYSPOWER_FAIL |
+                                       OPAL_SYSPOWER_INCL);
+               if (epow)
+                       return true;
+       }
+
+       return false;
+}
+
+/* Check for existing EPOW, DPO events */
+static bool poweroff_pending(void)
+{
+       int rc;
+       __be64 opal_dpo_timeout;
+
+       /* Check for DPO event */
+       rc = opal_get_dpo_status(&opal_dpo_timeout);
+       if (rc == OPAL_SUCCESS) {
+               pr_info("Existing DPO event detected.\n");
+               return true;
+       }
+
+       /* Check for EPOW event */
+       if (detect_epow()) {
+               pr_info("Existing EPOW event detected.\n");
+               return true;
+       }
+
+       return false;
+}
+
+/* OPAL power-control events notifier */
 static int opal_power_control_event(struct notifier_block *nb,
-                                   unsigned long msg_type, void *msg)
+                                       unsigned long msg_type, void *msg)
 {
-       struct opal_msg *power_msg = msg;
        uint64_t type;
 
-       type = be64_to_cpu(power_msg->params[0]);
-
-       switch (type) {
-       case SOFT_REBOOT:
-               pr_info("OPAL: reboot requested\n");
-               orderly_reboot();
+       switch (msg_type) {
+       case OPAL_MSG_EPOW:
+               if (detect_epow()) {
+                       pr_info("EPOW msg received. Powering off system\n");
+                       orderly_poweroff(true);
+               }
                break;
-       case SOFT_OFF:
-               pr_info("OPAL: poweroff requested\n");
+       case OPAL_MSG_DPO:
+               pr_info("DPO msg received. Powering off system\n");
                orderly_poweroff(true);
                break;
+       case OPAL_MSG_SHUTDOWN:
+               type = be64_to_cpu(((struct opal_msg *)msg)->params[0]);
+               switch (type) {
+               case SOFT_REBOOT:
+                       pr_info("Reboot requested\n");
+                       orderly_reboot();
+                       break;
+               case SOFT_OFF:
+                       pr_info("Poweroff requested\n");
+                       orderly_poweroff(true);
+                       break;
+               default:
+                       pr_err("Unknown power-control type %llu\n", type);
+               }
+               break;
        default:
-               pr_err("OPAL: power control type unexpected %016llx\n", type);
+               pr_err("Unknown OPAL message type %lu\n", msg_type);
        }
 
        return 0;
 }
 
+/* OPAL EPOW event notifier block */
+static struct notifier_block opal_epow_nb = {
+       .notifier_call  = opal_power_control_event,
+       .next           = NULL,
+       .priority       = 0,
+};
+
+/* OPAL DPO event notifier block */
+static struct notifier_block opal_dpo_nb = {
+       .notifier_call  = opal_power_control_event,
+       .next           = NULL,
+       .priority       = 0,
+};
+
+/* OPAL power-control event notifier block */
 static struct notifier_block opal_power_control_nb = {
        .notifier_call  = opal_power_control_event,
        .next           = NULL,
@@ -51,16 +140,40 @@ static struct notifier_block opal_power_control_nb = {
 
 static int __init opal_power_control_init(void)
 {
-       int ret;
+       int ret, supported = 0;
+       struct device_node *np;
 
+       /* Register OPAL power-control events notifier */
        ret = opal_message_notifier_register(OPAL_MSG_SHUTDOWN,
-                                            &opal_power_control_nb);
-       if (ret) {
-               pr_err("%s: Can't register OPAL event notifier (%d)\n",
-                               __func__, ret);
-               return ret;
+                                               &opal_power_control_nb);
+       if (ret)
+               pr_err("Failed to register SHUTDOWN notifier, ret = %d\n", ret);
+
+       /* Determine OPAL EPOW, DPO support */
+       np = of_find_node_by_path("/ibm,opal/epow");
+       if (np) {
+               supported = of_device_is_compatible(np, "ibm,opal-v3-epow");
+               of_node_put(np);
        }
 
+       if (!supported)
+               return 0;
+       pr_info("OPAL EPOW, DPO support detected.\n");
+
+       /* Register EPOW event notifier */
+       ret = opal_message_notifier_register(OPAL_MSG_EPOW, &opal_epow_nb);
+       if (ret)
+               pr_err("Failed to register EPOW notifier, ret = %d\n", ret);
+
+       /* Register DPO event notifier */
+       ret = opal_message_notifier_register(OPAL_MSG_DPO, &opal_dpo_nb);
+       if (ret)
+               pr_err("Failed to register DPO notifier, ret = %d\n", ret);
+
+       /* Check for any pending EPOW or DPO events. */
+       if (poweroff_pending())
+               orderly_poweroff(true);
+
        return 0;
 }
 machine_subsys_initcall(powernv, opal_power_control_init);