Path: blob/master/arch/powerpc/platforms/powernv/opal-power.c
26481 views
// SPDX-License-Identifier: GPL-2.0-or-later1/*2* PowerNV OPAL power control for graceful shutdown handling3*4* Copyright 2015 IBM Corp.5*/67#define pr_fmt(fmt) "opal-power: " fmt89#include <linux/kernel.h>10#include <linux/reboot.h>11#include <linux/notifier.h>12#include <linux/of.h>1314#include <asm/opal.h>15#include <asm/machdep.h>1617#define SOFT_OFF 0x0018#define SOFT_REBOOT 0x011920/* Detect EPOW event */21static bool detect_epow(void)22{23u16 epow;24int i, rc;25__be16 epow_classes;26__be16 opal_epow_status[OPAL_SYSEPOW_MAX] = {0};2728/*29* Check for EPOW event. Kernel sends supported EPOW classes info30* to OPAL. OPAL returns EPOW info along with classes present.31*/32epow_classes = cpu_to_be16(OPAL_SYSEPOW_MAX);33rc = opal_get_epow_status(opal_epow_status, &epow_classes);34if (rc != OPAL_SUCCESS) {35pr_err("Failed to get EPOW event information\n");36return false;37}3839/* Look for EPOW events present */40for (i = 0; i < be16_to_cpu(epow_classes); i++) {41epow = be16_to_cpu(opal_epow_status[i]);4243/* Filter events which do not need shutdown. */44if (i == OPAL_SYSEPOW_POWER)45epow &= ~(OPAL_SYSPOWER_CHNG | OPAL_SYSPOWER_FAIL |46OPAL_SYSPOWER_INCL);47if (epow)48return true;49}5051return false;52}5354/* Check for existing EPOW, DPO events */55static bool __init poweroff_pending(void)56{57int rc;58__be64 opal_dpo_timeout;5960/* Check for DPO event */61rc = opal_get_dpo_status(&opal_dpo_timeout);62if (rc == OPAL_SUCCESS) {63pr_info("Existing DPO event detected.\n");64return true;65}6667/* Check for EPOW event */68if (detect_epow()) {69pr_info("Existing EPOW event detected.\n");70return true;71}7273return false;74}7576/* OPAL power-control events notifier */77static int opal_power_control_event(struct notifier_block *nb,78unsigned long msg_type, void *msg)79{80uint64_t type;8182switch (msg_type) {83case OPAL_MSG_EPOW:84if (detect_epow()) {85pr_info("EPOW msg received. Powering off system\n");86orderly_poweroff(true);87}88break;89case OPAL_MSG_DPO:90pr_info("DPO msg received. Powering off system\n");91orderly_poweroff(true);92break;93case OPAL_MSG_SHUTDOWN:94type = be64_to_cpu(((struct opal_msg *)msg)->params[0]);95switch (type) {96case SOFT_REBOOT:97pr_info("Reboot requested\n");98orderly_reboot();99break;100case SOFT_OFF:101pr_info("Poweroff requested\n");102orderly_poweroff(true);103break;104default:105pr_err("Unknown power-control type %llu\n", type);106}107break;108default:109pr_err("Unknown OPAL message type %lu\n", msg_type);110}111112return 0;113}114115/* OPAL EPOW event notifier block */116static struct notifier_block opal_epow_nb = {117.notifier_call = opal_power_control_event,118.next = NULL,119.priority = 0,120};121122/* OPAL DPO event notifier block */123static struct notifier_block opal_dpo_nb = {124.notifier_call = opal_power_control_event,125.next = NULL,126.priority = 0,127};128129/* OPAL power-control event notifier block */130static struct notifier_block opal_power_control_nb = {131.notifier_call = opal_power_control_event,132.next = NULL,133.priority = 0,134};135136int __init opal_power_control_init(void)137{138int ret, supported = 0;139struct device_node *np;140141/* Register OPAL power-control events notifier */142ret = opal_message_notifier_register(OPAL_MSG_SHUTDOWN,143&opal_power_control_nb);144if (ret)145pr_err("Failed to register SHUTDOWN notifier, ret = %d\n", ret);146147/* Determine OPAL EPOW, DPO support */148np = of_find_node_by_path("/ibm,opal/epow");149if (np) {150supported = of_device_is_compatible(np, "ibm,opal-v3-epow");151of_node_put(np);152}153154if (!supported)155return 0;156pr_info("OPAL EPOW, DPO support detected.\n");157158/* Register EPOW event notifier */159ret = opal_message_notifier_register(OPAL_MSG_EPOW, &opal_epow_nb);160if (ret)161pr_err("Failed to register EPOW notifier, ret = %d\n", ret);162163/* Register DPO event notifier */164ret = opal_message_notifier_register(OPAL_MSG_DPO, &opal_dpo_nb);165if (ret)166pr_err("Failed to register DPO notifier, ret = %d\n", ret);167168/* Check for any pending EPOW or DPO events. */169if (poweroff_pending())170orderly_poweroff(true);171172return 0;173}174175176