linux/arch/powerpc/platforms/powernv/opal-power.c
<<
>>
Prefs
   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 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}
 175