cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

opal-power.c (4058B)


      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 __init 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}