162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * PowerNV OPAL high level interfaces
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2011 IBM Corp.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define pr_fmt(fmt)	"opal: " fmt
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/printk.h>
1162306a36Sopenharmony_ci#include <linux/types.h>
1262306a36Sopenharmony_ci#include <linux/of.h>
1362306a36Sopenharmony_ci#include <linux/of_fdt.h>
1462306a36Sopenharmony_ci#include <linux/of_platform.h>
1562306a36Sopenharmony_ci#include <linux/of_address.h>
1662306a36Sopenharmony_ci#include <linux/interrupt.h>
1762306a36Sopenharmony_ci#include <linux/notifier.h>
1862306a36Sopenharmony_ci#include <linux/slab.h>
1962306a36Sopenharmony_ci#include <linux/sched.h>
2062306a36Sopenharmony_ci#include <linux/kobject.h>
2162306a36Sopenharmony_ci#include <linux/delay.h>
2262306a36Sopenharmony_ci#include <linux/memblock.h>
2362306a36Sopenharmony_ci#include <linux/kthread.h>
2462306a36Sopenharmony_ci#include <linux/freezer.h>
2562306a36Sopenharmony_ci#include <linux/kmsg_dump.h>
2662306a36Sopenharmony_ci#include <linux/console.h>
2762306a36Sopenharmony_ci#include <linux/sched/debug.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include <asm/machdep.h>
3062306a36Sopenharmony_ci#include <asm/opal.h>
3162306a36Sopenharmony_ci#include <asm/firmware.h>
3262306a36Sopenharmony_ci#include <asm/mce.h>
3362306a36Sopenharmony_ci#include <asm/imc-pmu.h>
3462306a36Sopenharmony_ci#include <asm/bug.h>
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#include "powernv.h"
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define OPAL_MSG_QUEUE_MAX 16
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistruct opal_msg_node {
4162306a36Sopenharmony_ci	struct list_head	list;
4262306a36Sopenharmony_ci	struct opal_msg		msg;
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic DEFINE_SPINLOCK(msg_list_lock);
4662306a36Sopenharmony_cistatic LIST_HEAD(msg_list);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/* /sys/firmware/opal */
4962306a36Sopenharmony_cistruct kobject *opal_kobj;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistruct opal {
5262306a36Sopenharmony_ci	u64 base;
5362306a36Sopenharmony_ci	u64 entry;
5462306a36Sopenharmony_ci	u64 size;
5562306a36Sopenharmony_ci} opal;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistruct mcheck_recoverable_range {
5862306a36Sopenharmony_ci	u64 start_addr;
5962306a36Sopenharmony_ci	u64 end_addr;
6062306a36Sopenharmony_ci	u64 recover_addr;
6162306a36Sopenharmony_ci};
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic int msg_list_size;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic struct mcheck_recoverable_range *mc_recoverable_range;
6662306a36Sopenharmony_cistatic int mc_recoverable_range_len;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistruct device_node *opal_node;
6962306a36Sopenharmony_cistatic DEFINE_SPINLOCK(opal_write_lock);
7062306a36Sopenharmony_cistatic struct atomic_notifier_head opal_msg_notifier_head[OPAL_MSG_TYPE_MAX];
7162306a36Sopenharmony_cistatic uint32_t opal_heartbeat;
7262306a36Sopenharmony_cistatic struct task_struct *kopald_tsk;
7362306a36Sopenharmony_cistatic struct opal_msg *opal_msg;
7462306a36Sopenharmony_cistatic u32 opal_msg_size __ro_after_init;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_civoid __init opal_configure_cores(void)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	u64 reinit_flags = 0;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	/* Do the actual re-init, This will clobber all FPRs, VRs, etc...
8162306a36Sopenharmony_ci	 *
8262306a36Sopenharmony_ci	 * It will preserve non volatile GPRs and HSPRG0/1. It will
8362306a36Sopenharmony_ci	 * also restore HIDs and other SPRs to their original value
8462306a36Sopenharmony_ci	 * but it might clobber a bunch.
8562306a36Sopenharmony_ci	 */
8662306a36Sopenharmony_ci#ifdef __BIG_ENDIAN__
8762306a36Sopenharmony_ci	reinit_flags |= OPAL_REINIT_CPUS_HILE_BE;
8862306a36Sopenharmony_ci#else
8962306a36Sopenharmony_ci	reinit_flags |= OPAL_REINIT_CPUS_HILE_LE;
9062306a36Sopenharmony_ci#endif
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	/*
9362306a36Sopenharmony_ci	 * POWER9 always support running hash:
9462306a36Sopenharmony_ci	 *  ie. Host hash  supports  hash guests
9562306a36Sopenharmony_ci	 *      Host radix supports  hash/radix guests
9662306a36Sopenharmony_ci	 */
9762306a36Sopenharmony_ci	if (early_cpu_has_feature(CPU_FTR_ARCH_300)) {
9862306a36Sopenharmony_ci		reinit_flags |= OPAL_REINIT_CPUS_MMU_HASH;
9962306a36Sopenharmony_ci		if (early_radix_enabled())
10062306a36Sopenharmony_ci			reinit_flags |= OPAL_REINIT_CPUS_MMU_RADIX;
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	opal_reinit_cpus(reinit_flags);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	/* Restore some bits */
10662306a36Sopenharmony_ci	if (cur_cpu_spec->cpu_restore)
10762306a36Sopenharmony_ci		cur_cpu_spec->cpu_restore();
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ciint __init early_init_dt_scan_opal(unsigned long node,
11162306a36Sopenharmony_ci				   const char *uname, int depth, void *data)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	const void *basep, *entryp, *sizep;
11462306a36Sopenharmony_ci	int basesz, entrysz, runtimesz;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (depth != 1 || strcmp(uname, "ibm,opal") != 0)
11762306a36Sopenharmony_ci		return 0;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	basep  = of_get_flat_dt_prop(node, "opal-base-address", &basesz);
12062306a36Sopenharmony_ci	entryp = of_get_flat_dt_prop(node, "opal-entry-address", &entrysz);
12162306a36Sopenharmony_ci	sizep = of_get_flat_dt_prop(node, "opal-runtime-size", &runtimesz);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	if (!basep || !entryp || !sizep)
12462306a36Sopenharmony_ci		return 1;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	opal.base = of_read_number(basep, basesz/4);
12762306a36Sopenharmony_ci	opal.entry = of_read_number(entryp, entrysz/4);
12862306a36Sopenharmony_ci	opal.size = of_read_number(sizep, runtimesz/4);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	pr_debug("OPAL Base  = 0x%llx (basep=%p basesz=%d)\n",
13162306a36Sopenharmony_ci		 opal.base, basep, basesz);
13262306a36Sopenharmony_ci	pr_debug("OPAL Entry = 0x%llx (entryp=%p basesz=%d)\n",
13362306a36Sopenharmony_ci		 opal.entry, entryp, entrysz);
13462306a36Sopenharmony_ci	pr_debug("OPAL Entry = 0x%llx (sizep=%p runtimesz=%d)\n",
13562306a36Sopenharmony_ci		 opal.size, sizep, runtimesz);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	if (of_flat_dt_is_compatible(node, "ibm,opal-v3")) {
13862306a36Sopenharmony_ci		powerpc_firmware_features |= FW_FEATURE_OPAL;
13962306a36Sopenharmony_ci		pr_debug("OPAL detected !\n");
14062306a36Sopenharmony_ci	} else {
14162306a36Sopenharmony_ci		panic("OPAL != V3 detected, no longer supported.\n");
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	return 1;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ciint __init early_init_dt_scan_recoverable_ranges(unsigned long node,
14862306a36Sopenharmony_ci				   const char *uname, int depth, void *data)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	int i, psize, size;
15162306a36Sopenharmony_ci	const __be32 *prop;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	if (depth != 1 || strcmp(uname, "ibm,opal") != 0)
15462306a36Sopenharmony_ci		return 0;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	prop = of_get_flat_dt_prop(node, "mcheck-recoverable-ranges", &psize);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	if (!prop)
15962306a36Sopenharmony_ci		return 1;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	pr_debug("Found machine check recoverable ranges.\n");
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	/*
16462306a36Sopenharmony_ci	 * Calculate number of available entries.
16562306a36Sopenharmony_ci	 *
16662306a36Sopenharmony_ci	 * Each recoverable address range entry is (start address, len,
16762306a36Sopenharmony_ci	 * recovery address), 2 cells each for start and recovery address,
16862306a36Sopenharmony_ci	 * 1 cell for len, totalling 5 cells per entry.
16962306a36Sopenharmony_ci	 */
17062306a36Sopenharmony_ci	mc_recoverable_range_len = psize / (sizeof(*prop) * 5);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/* Sanity check */
17362306a36Sopenharmony_ci	if (!mc_recoverable_range_len)
17462306a36Sopenharmony_ci		return 1;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	/* Size required to hold all the entries. */
17762306a36Sopenharmony_ci	size = mc_recoverable_range_len *
17862306a36Sopenharmony_ci			sizeof(struct mcheck_recoverable_range);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	/*
18162306a36Sopenharmony_ci	 * Allocate a buffer to hold the MC recoverable ranges.
18262306a36Sopenharmony_ci	 */
18362306a36Sopenharmony_ci	mc_recoverable_range = memblock_alloc(size, __alignof__(u64));
18462306a36Sopenharmony_ci	if (!mc_recoverable_range)
18562306a36Sopenharmony_ci		panic("%s: Failed to allocate %u bytes align=0x%lx\n",
18662306a36Sopenharmony_ci		      __func__, size, __alignof__(u64));
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	for (i = 0; i < mc_recoverable_range_len; i++) {
18962306a36Sopenharmony_ci		mc_recoverable_range[i].start_addr =
19062306a36Sopenharmony_ci					of_read_number(prop + (i * 5) + 0, 2);
19162306a36Sopenharmony_ci		mc_recoverable_range[i].end_addr =
19262306a36Sopenharmony_ci					mc_recoverable_range[i].start_addr +
19362306a36Sopenharmony_ci					of_read_number(prop + (i * 5) + 2, 1);
19462306a36Sopenharmony_ci		mc_recoverable_range[i].recover_addr =
19562306a36Sopenharmony_ci					of_read_number(prop + (i * 5) + 3, 2);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci		pr_debug("Machine check recoverable range: %llx..%llx: %llx\n",
19862306a36Sopenharmony_ci				mc_recoverable_range[i].start_addr,
19962306a36Sopenharmony_ci				mc_recoverable_range[i].end_addr,
20062306a36Sopenharmony_ci				mc_recoverable_range[i].recover_addr);
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci	return 1;
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic int __init opal_register_exception_handlers(void)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci#ifdef __BIG_ENDIAN__
20862306a36Sopenharmony_ci	u64 glue;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	if (!(powerpc_firmware_features & FW_FEATURE_OPAL))
21162306a36Sopenharmony_ci		return -ENODEV;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	/* Hookup some exception handlers except machine check. We use the
21462306a36Sopenharmony_ci	 * fwnmi area at 0x7000 to provide the glue space to OPAL
21562306a36Sopenharmony_ci	 */
21662306a36Sopenharmony_ci	glue = 0x7000;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	/*
21962306a36Sopenharmony_ci	 * Only ancient OPAL firmware requires this.
22062306a36Sopenharmony_ci	 * Specifically, firmware from FW810.00 (released June 2014)
22162306a36Sopenharmony_ci	 * through FW810.20 (Released October 2014).
22262306a36Sopenharmony_ci	 *
22362306a36Sopenharmony_ci	 * Check if we are running on newer (post Oct 2014) firmware that
22462306a36Sopenharmony_ci	 * exports the OPAL_HANDLE_HMI token. If yes, then don't ask OPAL to
22562306a36Sopenharmony_ci	 * patch the HMI interrupt and we catch it directly in Linux.
22662306a36Sopenharmony_ci	 *
22762306a36Sopenharmony_ci	 * For older firmware (i.e < FW810.20), we fallback to old behavior and
22862306a36Sopenharmony_ci	 * let OPAL patch the HMI vector and handle it inside OPAL firmware.
22962306a36Sopenharmony_ci	 *
23062306a36Sopenharmony_ci	 * For newer firmware we catch/handle the HMI directly in Linux.
23162306a36Sopenharmony_ci	 */
23262306a36Sopenharmony_ci	if (!opal_check_token(OPAL_HANDLE_HMI)) {
23362306a36Sopenharmony_ci		pr_info("Old firmware detected, OPAL handles HMIs.\n");
23462306a36Sopenharmony_ci		opal_register_exception_handler(
23562306a36Sopenharmony_ci				OPAL_HYPERVISOR_MAINTENANCE_HANDLER,
23662306a36Sopenharmony_ci				0, glue);
23762306a36Sopenharmony_ci		glue += 128;
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	/*
24162306a36Sopenharmony_ci	 * Only applicable to ancient firmware, all modern
24262306a36Sopenharmony_ci	 * (post March 2015/skiboot 5.0) firmware will just return
24362306a36Sopenharmony_ci	 * OPAL_UNSUPPORTED.
24462306a36Sopenharmony_ci	 */
24562306a36Sopenharmony_ci	opal_register_exception_handler(OPAL_SOFTPATCH_HANDLER, 0, glue);
24662306a36Sopenharmony_ci#endif
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	return 0;
24962306a36Sopenharmony_ci}
25062306a36Sopenharmony_cimachine_early_initcall(powernv, opal_register_exception_handlers);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic void queue_replay_msg(void *msg)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	struct opal_msg_node *msg_node;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	if (msg_list_size < OPAL_MSG_QUEUE_MAX) {
25762306a36Sopenharmony_ci		msg_node = kzalloc(sizeof(*msg_node), GFP_ATOMIC);
25862306a36Sopenharmony_ci		if (msg_node) {
25962306a36Sopenharmony_ci			INIT_LIST_HEAD(&msg_node->list);
26062306a36Sopenharmony_ci			memcpy(&msg_node->msg, msg, sizeof(struct opal_msg));
26162306a36Sopenharmony_ci			list_add_tail(&msg_node->list, &msg_list);
26262306a36Sopenharmony_ci			msg_list_size++;
26362306a36Sopenharmony_ci		} else
26462306a36Sopenharmony_ci			pr_warn_once("message queue no memory\n");
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci		if (msg_list_size >= OPAL_MSG_QUEUE_MAX)
26762306a36Sopenharmony_ci			pr_warn_once("message queue full\n");
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic void dequeue_replay_msg(enum opal_msg_type msg_type)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	struct opal_msg_node *msg_node, *tmp;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	list_for_each_entry_safe(msg_node, tmp, &msg_list, list) {
27662306a36Sopenharmony_ci		if (be32_to_cpu(msg_node->msg.msg_type) != msg_type)
27762306a36Sopenharmony_ci			continue;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci		atomic_notifier_call_chain(&opal_msg_notifier_head[msg_type],
28062306a36Sopenharmony_ci					msg_type,
28162306a36Sopenharmony_ci					&msg_node->msg);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci		list_del(&msg_node->list);
28462306a36Sopenharmony_ci		kfree(msg_node);
28562306a36Sopenharmony_ci		msg_list_size--;
28662306a36Sopenharmony_ci	}
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci/*
29062306a36Sopenharmony_ci * Opal message notifier based on message type. Allow subscribers to get
29162306a36Sopenharmony_ci * notified for specific messgae type.
29262306a36Sopenharmony_ci */
29362306a36Sopenharmony_ciint opal_message_notifier_register(enum opal_msg_type msg_type,
29462306a36Sopenharmony_ci					struct notifier_block *nb)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	int ret;
29762306a36Sopenharmony_ci	unsigned long flags;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	if (!nb || msg_type >= OPAL_MSG_TYPE_MAX) {
30062306a36Sopenharmony_ci		pr_warn("%s: Invalid arguments, msg_type:%d\n",
30162306a36Sopenharmony_ci			__func__, msg_type);
30262306a36Sopenharmony_ci		return -EINVAL;
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	spin_lock_irqsave(&msg_list_lock, flags);
30662306a36Sopenharmony_ci	ret = atomic_notifier_chain_register(
30762306a36Sopenharmony_ci		&opal_msg_notifier_head[msg_type], nb);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	/*
31062306a36Sopenharmony_ci	 * If the registration succeeded, replay any queued messages that came
31162306a36Sopenharmony_ci	 * in prior to the notifier chain registration. msg_list_lock held here
31262306a36Sopenharmony_ci	 * to ensure they're delivered prior to any subsequent messages.
31362306a36Sopenharmony_ci	 */
31462306a36Sopenharmony_ci	if (ret == 0)
31562306a36Sopenharmony_ci		dequeue_replay_msg(msg_type);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	spin_unlock_irqrestore(&msg_list_lock, flags);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	return ret;
32062306a36Sopenharmony_ci}
32162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_message_notifier_register);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ciint opal_message_notifier_unregister(enum opal_msg_type msg_type,
32462306a36Sopenharmony_ci				     struct notifier_block *nb)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	return atomic_notifier_chain_unregister(
32762306a36Sopenharmony_ci			&opal_msg_notifier_head[msg_type], nb);
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_message_notifier_unregister);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_cistatic void opal_message_do_notify(uint32_t msg_type, void *msg)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	unsigned long flags;
33462306a36Sopenharmony_ci	bool queued = false;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	spin_lock_irqsave(&msg_list_lock, flags);
33762306a36Sopenharmony_ci	if (opal_msg_notifier_head[msg_type].head == NULL) {
33862306a36Sopenharmony_ci		/*
33962306a36Sopenharmony_ci		 * Queue up the msg since no notifiers have registered
34062306a36Sopenharmony_ci		 * yet for this msg_type.
34162306a36Sopenharmony_ci		 */
34262306a36Sopenharmony_ci		queue_replay_msg(msg);
34362306a36Sopenharmony_ci		queued = true;
34462306a36Sopenharmony_ci	}
34562306a36Sopenharmony_ci	spin_unlock_irqrestore(&msg_list_lock, flags);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	if (queued)
34862306a36Sopenharmony_ci		return;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	/* notify subscribers */
35162306a36Sopenharmony_ci	atomic_notifier_call_chain(&opal_msg_notifier_head[msg_type],
35262306a36Sopenharmony_ci					msg_type, msg);
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic void opal_handle_message(void)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	s64 ret;
35862306a36Sopenharmony_ci	u32 type;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	ret = opal_get_msg(__pa(opal_msg), opal_msg_size);
36162306a36Sopenharmony_ci	/* No opal message pending. */
36262306a36Sopenharmony_ci	if (ret == OPAL_RESOURCE)
36362306a36Sopenharmony_ci		return;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	/* check for errors. */
36662306a36Sopenharmony_ci	if (ret) {
36762306a36Sopenharmony_ci		pr_warn("%s: Failed to retrieve opal message, err=%lld\n",
36862306a36Sopenharmony_ci			__func__, ret);
36962306a36Sopenharmony_ci		return;
37062306a36Sopenharmony_ci	}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	type = be32_to_cpu(opal_msg->msg_type);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	/* Sanity check */
37562306a36Sopenharmony_ci	if (type >= OPAL_MSG_TYPE_MAX) {
37662306a36Sopenharmony_ci		pr_warn_once("%s: Unknown message type: %u\n", __func__, type);
37762306a36Sopenharmony_ci		return;
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_ci	opal_message_do_notify(type, (void *)opal_msg);
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cistatic irqreturn_t opal_message_notify(int irq, void *data)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	opal_handle_message();
38562306a36Sopenharmony_ci	return IRQ_HANDLED;
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_cistatic int __init opal_message_init(struct device_node *opal_node)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	int ret, i, irq;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	ret = of_property_read_u32(opal_node, "opal-msg-size", &opal_msg_size);
39362306a36Sopenharmony_ci	if (ret) {
39462306a36Sopenharmony_ci		pr_notice("Failed to read opal-msg-size property\n");
39562306a36Sopenharmony_ci		opal_msg_size = sizeof(struct opal_msg);
39662306a36Sopenharmony_ci	}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	opal_msg = kmalloc(opal_msg_size, GFP_KERNEL);
39962306a36Sopenharmony_ci	if (!opal_msg) {
40062306a36Sopenharmony_ci		opal_msg_size = sizeof(struct opal_msg);
40162306a36Sopenharmony_ci		/* Try to allocate fixed message size */
40262306a36Sopenharmony_ci		opal_msg = kmalloc(opal_msg_size, GFP_KERNEL);
40362306a36Sopenharmony_ci		BUG_ON(opal_msg == NULL);
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	for (i = 0; i < OPAL_MSG_TYPE_MAX; i++)
40762306a36Sopenharmony_ci		ATOMIC_INIT_NOTIFIER_HEAD(&opal_msg_notifier_head[i]);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	irq = opal_event_request(ilog2(OPAL_EVENT_MSG_PENDING));
41062306a36Sopenharmony_ci	if (!irq) {
41162306a36Sopenharmony_ci		pr_err("%s: Can't register OPAL event irq (%d)\n",
41262306a36Sopenharmony_ci		       __func__, irq);
41362306a36Sopenharmony_ci		return irq;
41462306a36Sopenharmony_ci	}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	ret = request_irq(irq, opal_message_notify,
41762306a36Sopenharmony_ci			IRQ_TYPE_LEVEL_HIGH, "opal-msg", NULL);
41862306a36Sopenharmony_ci	if (ret) {
41962306a36Sopenharmony_ci		pr_err("%s: Can't request OPAL event irq (%d)\n",
42062306a36Sopenharmony_ci		       __func__, ret);
42162306a36Sopenharmony_ci		return ret;
42262306a36Sopenharmony_ci	}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	return 0;
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ciint opal_get_chars(uint32_t vtermno, char *buf, int count)
42862306a36Sopenharmony_ci{
42962306a36Sopenharmony_ci	s64 rc;
43062306a36Sopenharmony_ci	__be64 evt, len;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	if (!opal.entry)
43362306a36Sopenharmony_ci		return -ENODEV;
43462306a36Sopenharmony_ci	opal_poll_events(&evt);
43562306a36Sopenharmony_ci	if ((be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_INPUT) == 0)
43662306a36Sopenharmony_ci		return 0;
43762306a36Sopenharmony_ci	len = cpu_to_be64(count);
43862306a36Sopenharmony_ci	rc = opal_console_read(vtermno, &len, buf);
43962306a36Sopenharmony_ci	if (rc == OPAL_SUCCESS)
44062306a36Sopenharmony_ci		return be64_to_cpu(len);
44162306a36Sopenharmony_ci	return 0;
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cistatic int __opal_put_chars(uint32_t vtermno, const char *data, int total_len, bool atomic)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	unsigned long flags = 0 /* shut up gcc */;
44762306a36Sopenharmony_ci	int written;
44862306a36Sopenharmony_ci	__be64 olen;
44962306a36Sopenharmony_ci	s64 rc;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	if (!opal.entry)
45262306a36Sopenharmony_ci		return -ENODEV;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	if (atomic)
45562306a36Sopenharmony_ci		spin_lock_irqsave(&opal_write_lock, flags);
45662306a36Sopenharmony_ci	rc = opal_console_write_buffer_space(vtermno, &olen);
45762306a36Sopenharmony_ci	if (rc || be64_to_cpu(olen) < total_len) {
45862306a36Sopenharmony_ci		/* Closed -> drop characters */
45962306a36Sopenharmony_ci		if (rc)
46062306a36Sopenharmony_ci			written = total_len;
46162306a36Sopenharmony_ci		else
46262306a36Sopenharmony_ci			written = -EAGAIN;
46362306a36Sopenharmony_ci		goto out;
46462306a36Sopenharmony_ci	}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	/* Should not get a partial write here because space is available. */
46762306a36Sopenharmony_ci	olen = cpu_to_be64(total_len);
46862306a36Sopenharmony_ci	rc = opal_console_write(vtermno, &olen, data);
46962306a36Sopenharmony_ci	if (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
47062306a36Sopenharmony_ci		if (rc == OPAL_BUSY_EVENT)
47162306a36Sopenharmony_ci			opal_poll_events(NULL);
47262306a36Sopenharmony_ci		written = -EAGAIN;
47362306a36Sopenharmony_ci		goto out;
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	/* Closed or other error drop */
47762306a36Sopenharmony_ci	if (rc != OPAL_SUCCESS) {
47862306a36Sopenharmony_ci		written = opal_error_code(rc);
47962306a36Sopenharmony_ci		goto out;
48062306a36Sopenharmony_ci	}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	written = be64_to_cpu(olen);
48362306a36Sopenharmony_ci	if (written < total_len) {
48462306a36Sopenharmony_ci		if (atomic) {
48562306a36Sopenharmony_ci			/* Should not happen */
48662306a36Sopenharmony_ci			pr_warn("atomic console write returned partial "
48762306a36Sopenharmony_ci				"len=%d written=%d\n", total_len, written);
48862306a36Sopenharmony_ci		}
48962306a36Sopenharmony_ci		if (!written)
49062306a36Sopenharmony_ci			written = -EAGAIN;
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ciout:
49462306a36Sopenharmony_ci	if (atomic)
49562306a36Sopenharmony_ci		spin_unlock_irqrestore(&opal_write_lock, flags);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	return written;
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ciint opal_put_chars(uint32_t vtermno, const char *data, int total_len)
50162306a36Sopenharmony_ci{
50262306a36Sopenharmony_ci	return __opal_put_chars(vtermno, data, total_len, false);
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci/*
50662306a36Sopenharmony_ci * opal_put_chars_atomic will not perform partial-writes. Data will be
50762306a36Sopenharmony_ci * atomically written to the terminal or not at all. This is not strictly
50862306a36Sopenharmony_ci * true at the moment because console space can race with OPAL's console
50962306a36Sopenharmony_ci * writes.
51062306a36Sopenharmony_ci */
51162306a36Sopenharmony_ciint opal_put_chars_atomic(uint32_t vtermno, const char *data, int total_len)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	return __opal_put_chars(vtermno, data, total_len, true);
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_cistatic s64 __opal_flush_console(uint32_t vtermno)
51762306a36Sopenharmony_ci{
51862306a36Sopenharmony_ci	s64 rc;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	if (!opal_check_token(OPAL_CONSOLE_FLUSH)) {
52162306a36Sopenharmony_ci		__be64 evt;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci		/*
52462306a36Sopenharmony_ci		 * If OPAL_CONSOLE_FLUSH is not implemented in the firmware,
52562306a36Sopenharmony_ci		 * the console can still be flushed by calling the polling
52662306a36Sopenharmony_ci		 * function while it has OPAL_EVENT_CONSOLE_OUTPUT events.
52762306a36Sopenharmony_ci		 */
52862306a36Sopenharmony_ci		WARN_ONCE(1, "opal: OPAL_CONSOLE_FLUSH missing.\n");
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci		opal_poll_events(&evt);
53162306a36Sopenharmony_ci		if (!(be64_to_cpu(evt) & OPAL_EVENT_CONSOLE_OUTPUT))
53262306a36Sopenharmony_ci			return OPAL_SUCCESS;
53362306a36Sopenharmony_ci		return OPAL_BUSY;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	} else {
53662306a36Sopenharmony_ci		rc = opal_console_flush(vtermno);
53762306a36Sopenharmony_ci		if (rc == OPAL_BUSY_EVENT) {
53862306a36Sopenharmony_ci			opal_poll_events(NULL);
53962306a36Sopenharmony_ci			rc = OPAL_BUSY;
54062306a36Sopenharmony_ci		}
54162306a36Sopenharmony_ci		return rc;
54262306a36Sopenharmony_ci	}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci/*
54762306a36Sopenharmony_ci * opal_flush_console spins until the console is flushed
54862306a36Sopenharmony_ci */
54962306a36Sopenharmony_ciint opal_flush_console(uint32_t vtermno)
55062306a36Sopenharmony_ci{
55162306a36Sopenharmony_ci	for (;;) {
55262306a36Sopenharmony_ci		s64 rc = __opal_flush_console(vtermno);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci		if (rc == OPAL_BUSY || rc == OPAL_PARTIAL) {
55562306a36Sopenharmony_ci			mdelay(1);
55662306a36Sopenharmony_ci			continue;
55762306a36Sopenharmony_ci		}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci		return opal_error_code(rc);
56062306a36Sopenharmony_ci	}
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci/*
56462306a36Sopenharmony_ci * opal_flush_chars is an hvc interface that sleeps until the console is
56562306a36Sopenharmony_ci * flushed if wait, otherwise it will return -EBUSY if the console has data,
56662306a36Sopenharmony_ci * -EAGAIN if it has data and some of it was flushed.
56762306a36Sopenharmony_ci */
56862306a36Sopenharmony_ciint opal_flush_chars(uint32_t vtermno, bool wait)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	for (;;) {
57162306a36Sopenharmony_ci		s64 rc = __opal_flush_console(vtermno);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci		if (rc == OPAL_BUSY || rc == OPAL_PARTIAL) {
57462306a36Sopenharmony_ci			if (wait) {
57562306a36Sopenharmony_ci				msleep(OPAL_BUSY_DELAY_MS);
57662306a36Sopenharmony_ci				continue;
57762306a36Sopenharmony_ci			}
57862306a36Sopenharmony_ci			if (rc == OPAL_PARTIAL)
57962306a36Sopenharmony_ci				return -EAGAIN;
58062306a36Sopenharmony_ci		}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci		return opal_error_code(rc);
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_cistatic int opal_recover_mce(struct pt_regs *regs,
58762306a36Sopenharmony_ci					struct machine_check_event *evt)
58862306a36Sopenharmony_ci{
58962306a36Sopenharmony_ci	int recovered = 0;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	if (regs_is_unrecoverable(regs)) {
59262306a36Sopenharmony_ci		/* If MSR_RI isn't set, we cannot recover */
59362306a36Sopenharmony_ci		pr_err("Machine check interrupt unrecoverable: MSR(RI=0)\n");
59462306a36Sopenharmony_ci		recovered = 0;
59562306a36Sopenharmony_ci	} else if (evt->disposition == MCE_DISPOSITION_RECOVERED) {
59662306a36Sopenharmony_ci		/* Platform corrected itself */
59762306a36Sopenharmony_ci		recovered = 1;
59862306a36Sopenharmony_ci	} else if (evt->severity == MCE_SEV_FATAL) {
59962306a36Sopenharmony_ci		/* Fatal machine check */
60062306a36Sopenharmony_ci		pr_err("Machine check interrupt is fatal\n");
60162306a36Sopenharmony_ci		recovered = 0;
60262306a36Sopenharmony_ci	}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	if (!recovered && evt->sync_error) {
60562306a36Sopenharmony_ci		/*
60662306a36Sopenharmony_ci		 * Try to kill processes if we get a synchronous machine check
60762306a36Sopenharmony_ci		 * (e.g., one caused by execution of this instruction). This
60862306a36Sopenharmony_ci		 * will devolve into a panic if we try to kill init or are in
60962306a36Sopenharmony_ci		 * an interrupt etc.
61062306a36Sopenharmony_ci		 *
61162306a36Sopenharmony_ci		 * TODO: Queue up this address for hwpoisioning later.
61262306a36Sopenharmony_ci		 * TODO: This is not quite right for d-side machine
61362306a36Sopenharmony_ci		 *       checks ->nip is not necessarily the important
61462306a36Sopenharmony_ci		 *       address.
61562306a36Sopenharmony_ci		 */
61662306a36Sopenharmony_ci		if ((user_mode(regs))) {
61762306a36Sopenharmony_ci			_exception(SIGBUS, regs, BUS_MCEERR_AR, regs->nip);
61862306a36Sopenharmony_ci			recovered = 1;
61962306a36Sopenharmony_ci		} else if (die_will_crash()) {
62062306a36Sopenharmony_ci			/*
62162306a36Sopenharmony_ci			 * die() would kill the kernel, so better to go via
62262306a36Sopenharmony_ci			 * the platform reboot code that will log the
62362306a36Sopenharmony_ci			 * machine check.
62462306a36Sopenharmony_ci			 */
62562306a36Sopenharmony_ci			recovered = 0;
62662306a36Sopenharmony_ci		} else {
62762306a36Sopenharmony_ci			die_mce("Machine check", regs, SIGBUS);
62862306a36Sopenharmony_ci			recovered = 1;
62962306a36Sopenharmony_ci		}
63062306a36Sopenharmony_ci	}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	return recovered;
63362306a36Sopenharmony_ci}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_civoid __noreturn pnv_platform_error_reboot(struct pt_regs *regs, const char *msg)
63662306a36Sopenharmony_ci{
63762306a36Sopenharmony_ci	panic_flush_kmsg_start();
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	pr_emerg("Hardware platform error: %s\n", msg);
64062306a36Sopenharmony_ci	if (regs)
64162306a36Sopenharmony_ci		show_regs(regs);
64262306a36Sopenharmony_ci	smp_send_stop();
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	panic_flush_kmsg_end();
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	/*
64762306a36Sopenharmony_ci	 * Don't bother to shut things down because this will
64862306a36Sopenharmony_ci	 * xstop the system.
64962306a36Sopenharmony_ci	 */
65062306a36Sopenharmony_ci	if (opal_cec_reboot2(OPAL_REBOOT_PLATFORM_ERROR, msg)
65162306a36Sopenharmony_ci						== OPAL_UNSUPPORTED) {
65262306a36Sopenharmony_ci		pr_emerg("Reboot type %d not supported for %s\n",
65362306a36Sopenharmony_ci				OPAL_REBOOT_PLATFORM_ERROR, msg);
65462306a36Sopenharmony_ci	}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	/*
65762306a36Sopenharmony_ci	 * We reached here. There can be three possibilities:
65862306a36Sopenharmony_ci	 * 1. We are running on a firmware level that do not support
65962306a36Sopenharmony_ci	 *    opal_cec_reboot2()
66062306a36Sopenharmony_ci	 * 2. We are running on a firmware level that do not support
66162306a36Sopenharmony_ci	 *    OPAL_REBOOT_PLATFORM_ERROR reboot type.
66262306a36Sopenharmony_ci	 * 3. We are running on FSP based system that does not need
66362306a36Sopenharmony_ci	 *    opal to trigger checkstop explicitly for error analysis.
66462306a36Sopenharmony_ci	 *    The FSP PRD component would have already got notified
66562306a36Sopenharmony_ci	 *    about this error through other channels.
66662306a36Sopenharmony_ci	 * 4. We are running on a newer skiboot that by default does
66762306a36Sopenharmony_ci	 *    not cause a checkstop, drops us back to the kernel to
66862306a36Sopenharmony_ci	 *    extract context and state at the time of the error.
66962306a36Sopenharmony_ci	 */
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	panic(msg);
67262306a36Sopenharmony_ci}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ciint opal_machine_check(struct pt_regs *regs)
67562306a36Sopenharmony_ci{
67662306a36Sopenharmony_ci	struct machine_check_event evt;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	if (!get_mce_event(&evt, MCE_EVENT_RELEASE))
67962306a36Sopenharmony_ci		return 0;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	/* Print things out */
68262306a36Sopenharmony_ci	if (evt.version != MCE_V1) {
68362306a36Sopenharmony_ci		pr_err("Machine Check Exception, Unknown event version %d !\n",
68462306a36Sopenharmony_ci		       evt.version);
68562306a36Sopenharmony_ci		return 0;
68662306a36Sopenharmony_ci	}
68762306a36Sopenharmony_ci	machine_check_print_event_info(&evt, user_mode(regs), false);
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	if (opal_recover_mce(regs, &evt))
69062306a36Sopenharmony_ci		return 1;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	pnv_platform_error_reboot(regs, "Unrecoverable Machine Check exception");
69362306a36Sopenharmony_ci}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci/* Early hmi handler called in real mode. */
69662306a36Sopenharmony_ciint opal_hmi_exception_early(struct pt_regs *regs)
69762306a36Sopenharmony_ci{
69862306a36Sopenharmony_ci	s64 rc;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	/*
70162306a36Sopenharmony_ci	 * call opal hmi handler. Pass paca address as token.
70262306a36Sopenharmony_ci	 * The return value OPAL_SUCCESS is an indication that there is
70362306a36Sopenharmony_ci	 * an HMI event generated waiting to pull by Linux.
70462306a36Sopenharmony_ci	 */
70562306a36Sopenharmony_ci	rc = opal_handle_hmi();
70662306a36Sopenharmony_ci	if (rc == OPAL_SUCCESS) {
70762306a36Sopenharmony_ci		local_paca->hmi_event_available = 1;
70862306a36Sopenharmony_ci		return 1;
70962306a36Sopenharmony_ci	}
71062306a36Sopenharmony_ci	return 0;
71162306a36Sopenharmony_ci}
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ciint opal_hmi_exception_early2(struct pt_regs *regs)
71462306a36Sopenharmony_ci{
71562306a36Sopenharmony_ci	s64 rc;
71662306a36Sopenharmony_ci	__be64 out_flags;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	/*
71962306a36Sopenharmony_ci	 * call opal hmi handler.
72062306a36Sopenharmony_ci	 * Check 64-bit flag mask to find out if an event was generated,
72162306a36Sopenharmony_ci	 * and whether TB is still valid or not etc.
72262306a36Sopenharmony_ci	 */
72362306a36Sopenharmony_ci	rc = opal_handle_hmi2(&out_flags);
72462306a36Sopenharmony_ci	if (rc != OPAL_SUCCESS)
72562306a36Sopenharmony_ci		return 0;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	if (be64_to_cpu(out_flags) & OPAL_HMI_FLAGS_NEW_EVENT)
72862306a36Sopenharmony_ci		local_paca->hmi_event_available = 1;
72962306a36Sopenharmony_ci	if (be64_to_cpu(out_flags) & OPAL_HMI_FLAGS_TOD_TB_FAIL)
73062306a36Sopenharmony_ci		tb_invalid = true;
73162306a36Sopenharmony_ci	return 1;
73262306a36Sopenharmony_ci}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci/* HMI exception handler called in virtual mode when irqs are next enabled. */
73562306a36Sopenharmony_ciint opal_handle_hmi_exception(struct pt_regs *regs)
73662306a36Sopenharmony_ci{
73762306a36Sopenharmony_ci	/*
73862306a36Sopenharmony_ci	 * Check if HMI event is available.
73962306a36Sopenharmony_ci	 * if Yes, then wake kopald to process them.
74062306a36Sopenharmony_ci	 */
74162306a36Sopenharmony_ci	if (!local_paca->hmi_event_available)
74262306a36Sopenharmony_ci		return 0;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	local_paca->hmi_event_available = 0;
74562306a36Sopenharmony_ci	opal_wake_poller();
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	return 1;
74862306a36Sopenharmony_ci}
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_cistatic uint64_t find_recovery_address(uint64_t nip)
75162306a36Sopenharmony_ci{
75262306a36Sopenharmony_ci	int i;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	for (i = 0; i < mc_recoverable_range_len; i++)
75562306a36Sopenharmony_ci		if ((nip >= mc_recoverable_range[i].start_addr) &&
75662306a36Sopenharmony_ci		    (nip < mc_recoverable_range[i].end_addr))
75762306a36Sopenharmony_ci		    return mc_recoverable_range[i].recover_addr;
75862306a36Sopenharmony_ci	return 0;
75962306a36Sopenharmony_ci}
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_cibool opal_mce_check_early_recovery(struct pt_regs *regs)
76262306a36Sopenharmony_ci{
76362306a36Sopenharmony_ci	uint64_t recover_addr = 0;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	if (!opal.base || !opal.size)
76662306a36Sopenharmony_ci		goto out;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	if ((regs->nip >= opal.base) &&
76962306a36Sopenharmony_ci			(regs->nip < (opal.base + opal.size)))
77062306a36Sopenharmony_ci		recover_addr = find_recovery_address(regs->nip);
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	/*
77362306a36Sopenharmony_ci	 * Setup regs->nip to rfi into fixup address.
77462306a36Sopenharmony_ci	 */
77562306a36Sopenharmony_ci	if (recover_addr)
77662306a36Sopenharmony_ci		regs_set_return_ip(regs, recover_addr);
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ciout:
77962306a36Sopenharmony_ci	return !!recover_addr;
78062306a36Sopenharmony_ci}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_cistatic int __init opal_sysfs_init(void)
78362306a36Sopenharmony_ci{
78462306a36Sopenharmony_ci	opal_kobj = kobject_create_and_add("opal", firmware_kobj);
78562306a36Sopenharmony_ci	if (!opal_kobj) {
78662306a36Sopenharmony_ci		pr_warn("kobject_create_and_add opal failed\n");
78762306a36Sopenharmony_ci		return -ENOMEM;
78862306a36Sopenharmony_ci	}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	return 0;
79162306a36Sopenharmony_ci}
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_cistatic ssize_t export_attr_read(struct file *fp, struct kobject *kobj,
79462306a36Sopenharmony_ci				struct bin_attribute *bin_attr, char *buf,
79562306a36Sopenharmony_ci				loff_t off, size_t count)
79662306a36Sopenharmony_ci{
79762306a36Sopenharmony_ci	return memory_read_from_buffer(buf, count, &off, bin_attr->private,
79862306a36Sopenharmony_ci				       bin_attr->size);
79962306a36Sopenharmony_ci}
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_cistatic int opal_add_one_export(struct kobject *parent, const char *export_name,
80262306a36Sopenharmony_ci			       struct device_node *np, const char *prop_name)
80362306a36Sopenharmony_ci{
80462306a36Sopenharmony_ci	struct bin_attribute *attr = NULL;
80562306a36Sopenharmony_ci	const char *name = NULL;
80662306a36Sopenharmony_ci	u64 vals[2];
80762306a36Sopenharmony_ci	int rc;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	rc = of_property_read_u64_array(np, prop_name, &vals[0], 2);
81062306a36Sopenharmony_ci	if (rc)
81162306a36Sopenharmony_ci		goto out;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	attr = kzalloc(sizeof(*attr), GFP_KERNEL);
81462306a36Sopenharmony_ci	if (!attr) {
81562306a36Sopenharmony_ci		rc = -ENOMEM;
81662306a36Sopenharmony_ci		goto out;
81762306a36Sopenharmony_ci	}
81862306a36Sopenharmony_ci	name = kstrdup(export_name, GFP_KERNEL);
81962306a36Sopenharmony_ci	if (!name) {
82062306a36Sopenharmony_ci		rc = -ENOMEM;
82162306a36Sopenharmony_ci		goto out;
82262306a36Sopenharmony_ci	}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	sysfs_bin_attr_init(attr);
82562306a36Sopenharmony_ci	attr->attr.name = name;
82662306a36Sopenharmony_ci	attr->attr.mode = 0400;
82762306a36Sopenharmony_ci	attr->read = export_attr_read;
82862306a36Sopenharmony_ci	attr->private = __va(vals[0]);
82962306a36Sopenharmony_ci	attr->size = vals[1];
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	rc = sysfs_create_bin_file(parent, attr);
83262306a36Sopenharmony_ciout:
83362306a36Sopenharmony_ci	if (rc) {
83462306a36Sopenharmony_ci		kfree(name);
83562306a36Sopenharmony_ci		kfree(attr);
83662306a36Sopenharmony_ci	}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	return rc;
83962306a36Sopenharmony_ci}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_cistatic void opal_add_exported_attrs(struct device_node *np,
84262306a36Sopenharmony_ci				    struct kobject *kobj)
84362306a36Sopenharmony_ci{
84462306a36Sopenharmony_ci	struct device_node *child;
84562306a36Sopenharmony_ci	struct property *prop;
84662306a36Sopenharmony_ci
84762306a36Sopenharmony_ci	for_each_property_of_node(np, prop) {
84862306a36Sopenharmony_ci		int rc;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci		if (!strcmp(prop->name, "name") ||
85162306a36Sopenharmony_ci		    !strcmp(prop->name, "phandle"))
85262306a36Sopenharmony_ci			continue;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci		rc = opal_add_one_export(kobj, prop->name, np, prop->name);
85562306a36Sopenharmony_ci		if (rc) {
85662306a36Sopenharmony_ci			pr_warn("Unable to add export %pOF/%s, rc = %d!\n",
85762306a36Sopenharmony_ci				np, prop->name, rc);
85862306a36Sopenharmony_ci		}
85962306a36Sopenharmony_ci	}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	for_each_child_of_node(np, child) {
86262306a36Sopenharmony_ci		struct kobject *child_kobj;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci		child_kobj = kobject_create_and_add(child->name, kobj);
86562306a36Sopenharmony_ci		if (!child_kobj) {
86662306a36Sopenharmony_ci			pr_err("Unable to create export dir for %pOF\n", child);
86762306a36Sopenharmony_ci			continue;
86862306a36Sopenharmony_ci		}
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci		opal_add_exported_attrs(child, child_kobj);
87162306a36Sopenharmony_ci	}
87262306a36Sopenharmony_ci}
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci/*
87562306a36Sopenharmony_ci * opal_export_attrs: creates a sysfs node for each property listed in
87662306a36Sopenharmony_ci * the device-tree under /ibm,opal/firmware/exports/
87762306a36Sopenharmony_ci * All new sysfs nodes are created under /opal/exports/.
87862306a36Sopenharmony_ci * This allows for reserved memory regions (e.g. HDAT) to be read.
87962306a36Sopenharmony_ci * The new sysfs nodes are only readable by root.
88062306a36Sopenharmony_ci */
88162306a36Sopenharmony_cistatic void opal_export_attrs(void)
88262306a36Sopenharmony_ci{
88362306a36Sopenharmony_ci	struct device_node *np;
88462306a36Sopenharmony_ci	struct kobject *kobj;
88562306a36Sopenharmony_ci	int rc;
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	np = of_find_node_by_path("/ibm,opal/firmware/exports");
88862306a36Sopenharmony_ci	if (!np)
88962306a36Sopenharmony_ci		return;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci	/* Create new 'exports' directory - /sys/firmware/opal/exports */
89262306a36Sopenharmony_ci	kobj = kobject_create_and_add("exports", opal_kobj);
89362306a36Sopenharmony_ci	if (!kobj) {
89462306a36Sopenharmony_ci		pr_warn("kobject_create_and_add() of exports failed\n");
89562306a36Sopenharmony_ci		of_node_put(np);
89662306a36Sopenharmony_ci		return;
89762306a36Sopenharmony_ci	}
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	opal_add_exported_attrs(np, kobj);
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	/*
90262306a36Sopenharmony_ci	 * NB: symbol_map existed before the generic export interface so it
90362306a36Sopenharmony_ci	 * lives under the top level opal_kobj.
90462306a36Sopenharmony_ci	 */
90562306a36Sopenharmony_ci	rc = opal_add_one_export(opal_kobj, "symbol_map",
90662306a36Sopenharmony_ci				 np->parent, "symbol-map");
90762306a36Sopenharmony_ci	if (rc)
90862306a36Sopenharmony_ci		pr_warn("Error %d creating OPAL symbols file\n", rc);
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	of_node_put(np);
91162306a36Sopenharmony_ci}
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_cistatic void __init opal_dump_region_init(void)
91462306a36Sopenharmony_ci{
91562306a36Sopenharmony_ci	void *addr;
91662306a36Sopenharmony_ci	uint64_t size;
91762306a36Sopenharmony_ci	int rc;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	if (!opal_check_token(OPAL_REGISTER_DUMP_REGION))
92062306a36Sopenharmony_ci		return;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	/* Register kernel log buffer */
92362306a36Sopenharmony_ci	addr = log_buf_addr_get();
92462306a36Sopenharmony_ci	if (addr == NULL)
92562306a36Sopenharmony_ci		return;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	size = log_buf_len_get();
92862306a36Sopenharmony_ci	if (size == 0)
92962306a36Sopenharmony_ci		return;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	rc = opal_register_dump_region(OPAL_DUMP_REGION_LOG_BUF,
93262306a36Sopenharmony_ci				       __pa(addr), size);
93362306a36Sopenharmony_ci	/* Don't warn if this is just an older OPAL that doesn't
93462306a36Sopenharmony_ci	 * know about that call
93562306a36Sopenharmony_ci	 */
93662306a36Sopenharmony_ci	if (rc && rc != OPAL_UNSUPPORTED)
93762306a36Sopenharmony_ci		pr_warn("DUMP: Failed to register kernel log buffer. "
93862306a36Sopenharmony_ci			"rc = %d\n", rc);
93962306a36Sopenharmony_ci}
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_cistatic void __init opal_pdev_init(const char *compatible)
94262306a36Sopenharmony_ci{
94362306a36Sopenharmony_ci	struct device_node *np;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	for_each_compatible_node(np, NULL, compatible)
94662306a36Sopenharmony_ci		of_platform_device_create(np, NULL, NULL);
94762306a36Sopenharmony_ci}
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_cistatic void __init opal_imc_init_dev(void)
95062306a36Sopenharmony_ci{
95162306a36Sopenharmony_ci	struct device_node *np;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	np = of_find_compatible_node(NULL, NULL, IMC_DTB_COMPAT);
95462306a36Sopenharmony_ci	if (np)
95562306a36Sopenharmony_ci		of_platform_device_create(np, NULL, NULL);
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	of_node_put(np);
95862306a36Sopenharmony_ci}
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_cistatic int kopald(void *unused)
96162306a36Sopenharmony_ci{
96262306a36Sopenharmony_ci	unsigned long timeout = msecs_to_jiffies(opal_heartbeat) + 1;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	set_freezable();
96562306a36Sopenharmony_ci	do {
96662306a36Sopenharmony_ci		try_to_freeze();
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci		opal_handle_events();
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
97162306a36Sopenharmony_ci		if (opal_have_pending_events())
97262306a36Sopenharmony_ci			__set_current_state(TASK_RUNNING);
97362306a36Sopenharmony_ci		else
97462306a36Sopenharmony_ci			schedule_timeout(timeout);
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	} while (!kthread_should_stop());
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	return 0;
97962306a36Sopenharmony_ci}
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_civoid opal_wake_poller(void)
98262306a36Sopenharmony_ci{
98362306a36Sopenharmony_ci	if (kopald_tsk)
98462306a36Sopenharmony_ci		wake_up_process(kopald_tsk);
98562306a36Sopenharmony_ci}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_cistatic void __init opal_init_heartbeat(void)
98862306a36Sopenharmony_ci{
98962306a36Sopenharmony_ci	/* Old firwmware, we assume the HVC heartbeat is sufficient */
99062306a36Sopenharmony_ci	if (of_property_read_u32(opal_node, "ibm,heartbeat-ms",
99162306a36Sopenharmony_ci				 &opal_heartbeat) != 0)
99262306a36Sopenharmony_ci		opal_heartbeat = 0;
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	if (opal_heartbeat)
99562306a36Sopenharmony_ci		kopald_tsk = kthread_run(kopald, NULL, "kopald");
99662306a36Sopenharmony_ci}
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_cistatic int __init opal_init(void)
99962306a36Sopenharmony_ci{
100062306a36Sopenharmony_ci	struct device_node *np, *consoles, *leds;
100162306a36Sopenharmony_ci	int rc;
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	opal_node = of_find_node_by_path("/ibm,opal");
100462306a36Sopenharmony_ci	if (!opal_node) {
100562306a36Sopenharmony_ci		pr_warn("Device node not found\n");
100662306a36Sopenharmony_ci		return -ENODEV;
100762306a36Sopenharmony_ci	}
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	/* Register OPAL consoles if any ports */
101062306a36Sopenharmony_ci	consoles = of_find_node_by_path("/ibm,opal/consoles");
101162306a36Sopenharmony_ci	if (consoles) {
101262306a36Sopenharmony_ci		for_each_child_of_node(consoles, np) {
101362306a36Sopenharmony_ci			if (!of_node_name_eq(np, "serial"))
101462306a36Sopenharmony_ci				continue;
101562306a36Sopenharmony_ci			of_platform_device_create(np, NULL, NULL);
101662306a36Sopenharmony_ci		}
101762306a36Sopenharmony_ci		of_node_put(consoles);
101862306a36Sopenharmony_ci	}
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	/* Initialise OPAL messaging system */
102162306a36Sopenharmony_ci	opal_message_init(opal_node);
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	/* Initialise OPAL asynchronous completion interface */
102462306a36Sopenharmony_ci	opal_async_comp_init();
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	/* Initialise OPAL sensor interface */
102762306a36Sopenharmony_ci	opal_sensor_init();
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	/* Initialise OPAL hypervisor maintainence interrupt handling */
103062306a36Sopenharmony_ci	opal_hmi_handler_init();
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	/* Create i2c platform devices */
103362306a36Sopenharmony_ci	opal_pdev_init("ibm,opal-i2c");
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	/* Handle non-volatile memory devices */
103662306a36Sopenharmony_ci	opal_pdev_init("pmem-region");
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	/* Setup a heatbeat thread if requested by OPAL */
103962306a36Sopenharmony_ci	opal_init_heartbeat();
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	/* Detect In-Memory Collection counters and create devices*/
104262306a36Sopenharmony_ci	opal_imc_init_dev();
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	/* Create leds platform devices */
104562306a36Sopenharmony_ci	leds = of_find_node_by_path("/ibm,opal/leds");
104662306a36Sopenharmony_ci	if (leds) {
104762306a36Sopenharmony_ci		of_platform_device_create(leds, "opal_leds", NULL);
104862306a36Sopenharmony_ci		of_node_put(leds);
104962306a36Sopenharmony_ci	}
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	/* Initialise OPAL message log interface */
105262306a36Sopenharmony_ci	opal_msglog_init();
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	/* Create "opal" kobject under /sys/firmware */
105562306a36Sopenharmony_ci	rc = opal_sysfs_init();
105662306a36Sopenharmony_ci	if (rc == 0) {
105762306a36Sopenharmony_ci		/* Setup dump region interface */
105862306a36Sopenharmony_ci		opal_dump_region_init();
105962306a36Sopenharmony_ci		/* Setup error log interface */
106062306a36Sopenharmony_ci		rc = opal_elog_init();
106162306a36Sopenharmony_ci		/* Setup code update interface */
106262306a36Sopenharmony_ci		opal_flash_update_init();
106362306a36Sopenharmony_ci		/* Setup platform dump extract interface */
106462306a36Sopenharmony_ci		opal_platform_dump_init();
106562306a36Sopenharmony_ci		/* Setup system parameters interface */
106662306a36Sopenharmony_ci		opal_sys_param_init();
106762306a36Sopenharmony_ci		/* Setup message log sysfs interface. */
106862306a36Sopenharmony_ci		opal_msglog_sysfs_init();
106962306a36Sopenharmony_ci		/* Add all export properties*/
107062306a36Sopenharmony_ci		opal_export_attrs();
107162306a36Sopenharmony_ci	}
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	/* Initialize platform devices: IPMI backend, PRD & flash interface */
107462306a36Sopenharmony_ci	opal_pdev_init("ibm,opal-ipmi");
107562306a36Sopenharmony_ci	opal_pdev_init("ibm,opal-flash");
107662306a36Sopenharmony_ci	opal_pdev_init("ibm,opal-prd");
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	/* Initialise platform device: oppanel interface */
107962306a36Sopenharmony_ci	opal_pdev_init("ibm,opal-oppanel");
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	/* Initialise OPAL kmsg dumper for flushing console on panic */
108262306a36Sopenharmony_ci	opal_kmsg_init();
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	/* Initialise OPAL powercap interface */
108562306a36Sopenharmony_ci	opal_powercap_init();
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	/* Initialise OPAL Power-Shifting-Ratio interface */
108862306a36Sopenharmony_ci	opal_psr_init();
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	/* Initialise OPAL sensor groups */
109162306a36Sopenharmony_ci	opal_sensor_groups_init();
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	/* Initialise OPAL Power control interface */
109462306a36Sopenharmony_ci	opal_power_control_init();
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	/* Initialize OPAL secure variables */
109762306a36Sopenharmony_ci	opal_pdev_init("ibm,secvar-backend");
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	return 0;
110062306a36Sopenharmony_ci}
110162306a36Sopenharmony_cimachine_subsys_initcall(powernv, opal_init);
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_civoid opal_shutdown(void)
110462306a36Sopenharmony_ci{
110562306a36Sopenharmony_ci	long rc = OPAL_BUSY;
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci	opal_event_shutdown();
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	/*
111062306a36Sopenharmony_ci	 * Then sync with OPAL which ensure anything that can
111162306a36Sopenharmony_ci	 * potentially write to our memory has completed such
111262306a36Sopenharmony_ci	 * as an ongoing dump retrieval
111362306a36Sopenharmony_ci	 */
111462306a36Sopenharmony_ci	while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
111562306a36Sopenharmony_ci		rc = opal_sync_host_reboot();
111662306a36Sopenharmony_ci		if (rc == OPAL_BUSY)
111762306a36Sopenharmony_ci			opal_poll_events(NULL);
111862306a36Sopenharmony_ci		else
111962306a36Sopenharmony_ci			mdelay(10);
112062306a36Sopenharmony_ci	}
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	/* Unregister memory dump region */
112362306a36Sopenharmony_ci	if (opal_check_token(OPAL_UNREGISTER_DUMP_REGION))
112462306a36Sopenharmony_ci		opal_unregister_dump_region(OPAL_DUMP_REGION_LOG_BUF);
112562306a36Sopenharmony_ci}
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci/* Export this so that test modules can use it */
112862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_invalid_call);
112962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_xscom_read);
113062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_xscom_write);
113162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_ipmi_send);
113262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_ipmi_recv);
113362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_flash_read);
113462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_flash_write);
113562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_flash_erase);
113662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_prd_msg);
113762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_check_token);
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci/* Convert a region of vmalloc memory to an opal sg list */
114062306a36Sopenharmony_cistruct opal_sg_list *opal_vmalloc_to_sg_list(void *vmalloc_addr,
114162306a36Sopenharmony_ci					     unsigned long vmalloc_size)
114262306a36Sopenharmony_ci{
114362306a36Sopenharmony_ci	struct opal_sg_list *sg, *first = NULL;
114462306a36Sopenharmony_ci	unsigned long i = 0;
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	sg = kzalloc(PAGE_SIZE, GFP_KERNEL);
114762306a36Sopenharmony_ci	if (!sg)
114862306a36Sopenharmony_ci		goto nomem;
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	first = sg;
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	while (vmalloc_size > 0) {
115362306a36Sopenharmony_ci		uint64_t data = vmalloc_to_pfn(vmalloc_addr) << PAGE_SHIFT;
115462306a36Sopenharmony_ci		uint64_t length = min(vmalloc_size, PAGE_SIZE);
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci		sg->entry[i].data = cpu_to_be64(data);
115762306a36Sopenharmony_ci		sg->entry[i].length = cpu_to_be64(length);
115862306a36Sopenharmony_ci		i++;
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci		if (i >= SG_ENTRIES_PER_NODE) {
116162306a36Sopenharmony_ci			struct opal_sg_list *next;
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci			next = kzalloc(PAGE_SIZE, GFP_KERNEL);
116462306a36Sopenharmony_ci			if (!next)
116562306a36Sopenharmony_ci				goto nomem;
116662306a36Sopenharmony_ci
116762306a36Sopenharmony_ci			sg->length = cpu_to_be64(
116862306a36Sopenharmony_ci					i * sizeof(struct opal_sg_entry) + 16);
116962306a36Sopenharmony_ci			i = 0;
117062306a36Sopenharmony_ci			sg->next = cpu_to_be64(__pa(next));
117162306a36Sopenharmony_ci			sg = next;
117262306a36Sopenharmony_ci		}
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci		vmalloc_addr += length;
117562306a36Sopenharmony_ci		vmalloc_size -= length;
117662306a36Sopenharmony_ci	}
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	sg->length = cpu_to_be64(i * sizeof(struct opal_sg_entry) + 16);
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	return first;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_cinomem:
118362306a36Sopenharmony_ci	pr_err("%s : Failed to allocate memory\n", __func__);
118462306a36Sopenharmony_ci	opal_free_sg_list(first);
118562306a36Sopenharmony_ci	return NULL;
118662306a36Sopenharmony_ci}
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_civoid opal_free_sg_list(struct opal_sg_list *sg)
118962306a36Sopenharmony_ci{
119062306a36Sopenharmony_ci	while (sg) {
119162306a36Sopenharmony_ci		uint64_t next = be64_to_cpu(sg->next);
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci		kfree(sg);
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci		if (next)
119662306a36Sopenharmony_ci			sg = __va(next);
119762306a36Sopenharmony_ci		else
119862306a36Sopenharmony_ci			sg = NULL;
119962306a36Sopenharmony_ci	}
120062306a36Sopenharmony_ci}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ciint opal_error_code(int rc)
120362306a36Sopenharmony_ci{
120462306a36Sopenharmony_ci	switch (rc) {
120562306a36Sopenharmony_ci	case OPAL_SUCCESS:		return 0;
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	case OPAL_PARAMETER:		return -EINVAL;
120862306a36Sopenharmony_ci	case OPAL_ASYNC_COMPLETION:	return -EINPROGRESS;
120962306a36Sopenharmony_ci	case OPAL_BUSY:
121062306a36Sopenharmony_ci	case OPAL_BUSY_EVENT:		return -EBUSY;
121162306a36Sopenharmony_ci	case OPAL_NO_MEM:		return -ENOMEM;
121262306a36Sopenharmony_ci	case OPAL_PERMISSION:		return -EPERM;
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	case OPAL_UNSUPPORTED:		return -EIO;
121562306a36Sopenharmony_ci	case OPAL_HARDWARE:		return -EIO;
121662306a36Sopenharmony_ci	case OPAL_INTERNAL_ERROR:	return -EIO;
121762306a36Sopenharmony_ci	case OPAL_TIMEOUT:		return -ETIMEDOUT;
121862306a36Sopenharmony_ci	default:
121962306a36Sopenharmony_ci		pr_err("%s: unexpected OPAL error %d\n", __func__, rc);
122062306a36Sopenharmony_ci		return -EIO;
122162306a36Sopenharmony_ci	}
122262306a36Sopenharmony_ci}
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_civoid powernv_set_nmmu_ptcr(unsigned long ptcr)
122562306a36Sopenharmony_ci{
122662306a36Sopenharmony_ci	int rc;
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	if (firmware_has_feature(FW_FEATURE_OPAL)) {
122962306a36Sopenharmony_ci		rc = opal_nmmu_set_ptcr(-1UL, ptcr);
123062306a36Sopenharmony_ci		if (rc != OPAL_SUCCESS && rc != OPAL_UNSUPPORTED)
123162306a36Sopenharmony_ci			pr_warn("%s: Unable to set nest mmu ptcr\n", __func__);
123262306a36Sopenharmony_ci	}
123362306a36Sopenharmony_ci}
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_poll_events);
123662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_rtc_read);
123762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_rtc_write);
123862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_tpo_read);
123962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_tpo_write);
124062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_i2c_request);
124162306a36Sopenharmony_ci/* Export these symbols for PowerNV LED class driver */
124262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_leds_get_ind);
124362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_leds_set_ind);
124462306a36Sopenharmony_ci/* Export this symbol for PowerNV Operator Panel class driver */
124562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_write_oppanel_async);
124662306a36Sopenharmony_ci/* Export this for KVM */
124762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_int_set_mfrr);
124862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_int_eoi);
124962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(opal_error_code);
125062306a36Sopenharmony_ci/* Export the below symbol for NX compression */
125162306a36Sopenharmony_ciEXPORT_SYMBOL(opal_nx_coproc_init);
1252