162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * PowerNV setup code.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2011 IBM Corp.
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#undef DEBUG
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/cpu.h>
1162306a36Sopenharmony_ci#include <linux/errno.h>
1262306a36Sopenharmony_ci#include <linux/sched.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/tty.h>
1562306a36Sopenharmony_ci#include <linux/reboot.h>
1662306a36Sopenharmony_ci#include <linux/init.h>
1762306a36Sopenharmony_ci#include <linux/console.h>
1862306a36Sopenharmony_ci#include <linux/delay.h>
1962306a36Sopenharmony_ci#include <linux/irq.h>
2062306a36Sopenharmony_ci#include <linux/seq_buf.h>
2162306a36Sopenharmony_ci#include <linux/seq_file.h>
2262306a36Sopenharmony_ci#include <linux/of.h>
2362306a36Sopenharmony_ci#include <linux/of_fdt.h>
2462306a36Sopenharmony_ci#include <linux/interrupt.h>
2562306a36Sopenharmony_ci#include <linux/bug.h>
2662306a36Sopenharmony_ci#include <linux/pci.h>
2762306a36Sopenharmony_ci#include <linux/cpufreq.h>
2862306a36Sopenharmony_ci#include <linux/memblock.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include <asm/machdep.h>
3162306a36Sopenharmony_ci#include <asm/firmware.h>
3262306a36Sopenharmony_ci#include <asm/xics.h>
3362306a36Sopenharmony_ci#include <asm/xive.h>
3462306a36Sopenharmony_ci#include <asm/opal.h>
3562306a36Sopenharmony_ci#include <asm/kexec.h>
3662306a36Sopenharmony_ci#include <asm/smp.h>
3762306a36Sopenharmony_ci#include <asm/tm.h>
3862306a36Sopenharmony_ci#include <asm/setup.h>
3962306a36Sopenharmony_ci#include <asm/security_features.h>
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#include "powernv.h"
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic bool __init fw_feature_is(const char *state, const char *name,
4562306a36Sopenharmony_ci			  struct device_node *fw_features)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	struct device_node *np;
4862306a36Sopenharmony_ci	bool rc = false;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	np = of_get_child_by_name(fw_features, name);
5162306a36Sopenharmony_ci	if (np) {
5262306a36Sopenharmony_ci		rc = of_property_read_bool(np, state);
5362306a36Sopenharmony_ci		of_node_put(np);
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	return rc;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic void __init init_fw_feat_flags(struct device_node *np)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	if (fw_feature_is("enabled", "inst-spec-barrier-ori31,31,0", np))
6262306a36Sopenharmony_ci		security_ftr_set(SEC_FTR_SPEC_BAR_ORI31);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	if (fw_feature_is("enabled", "fw-bcctrl-serialized", np))
6562306a36Sopenharmony_ci		security_ftr_set(SEC_FTR_BCCTRL_SERIALISED);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	if (fw_feature_is("enabled", "inst-l1d-flush-ori30,30,0", np))
6862306a36Sopenharmony_ci		security_ftr_set(SEC_FTR_L1D_FLUSH_ORI30);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	if (fw_feature_is("enabled", "inst-l1d-flush-trig2", np))
7162306a36Sopenharmony_ci		security_ftr_set(SEC_FTR_L1D_FLUSH_TRIG2);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	if (fw_feature_is("enabled", "fw-l1d-thread-split", np))
7462306a36Sopenharmony_ci		security_ftr_set(SEC_FTR_L1D_THREAD_PRIV);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	if (fw_feature_is("enabled", "fw-count-cache-disabled", np))
7762306a36Sopenharmony_ci		security_ftr_set(SEC_FTR_COUNT_CACHE_DISABLED);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (fw_feature_is("enabled", "fw-count-cache-flush-bcctr2,0,0", np))
8062306a36Sopenharmony_ci		security_ftr_set(SEC_FTR_BCCTR_FLUSH_ASSIST);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	if (fw_feature_is("enabled", "needs-count-cache-flush-on-context-switch", np))
8362306a36Sopenharmony_ci		security_ftr_set(SEC_FTR_FLUSH_COUNT_CACHE);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	/*
8662306a36Sopenharmony_ci	 * The features below are enabled by default, so we instead look to see
8762306a36Sopenharmony_ci	 * if firmware has *disabled* them, and clear them if so.
8862306a36Sopenharmony_ci	 */
8962306a36Sopenharmony_ci	if (fw_feature_is("disabled", "speculation-policy-favor-security", np))
9062306a36Sopenharmony_ci		security_ftr_clear(SEC_FTR_FAVOUR_SECURITY);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (fw_feature_is("disabled", "needs-l1d-flush-msr-pr-0-to-1", np))
9362306a36Sopenharmony_ci		security_ftr_clear(SEC_FTR_L1D_FLUSH_PR);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	if (fw_feature_is("disabled", "needs-l1d-flush-msr-hv-1-to-0", np))
9662306a36Sopenharmony_ci		security_ftr_clear(SEC_FTR_L1D_FLUSH_HV);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	if (fw_feature_is("disabled", "needs-spec-barrier-for-bound-checks", np))
9962306a36Sopenharmony_ci		security_ftr_clear(SEC_FTR_BNDS_CHK_SPEC_BAR);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if (fw_feature_is("enabled", "no-need-l1d-flush-msr-pr-1-to-0", np))
10262306a36Sopenharmony_ci		security_ftr_clear(SEC_FTR_L1D_FLUSH_ENTRY);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	if (fw_feature_is("enabled", "no-need-l1d-flush-kernel-on-user-access", np))
10562306a36Sopenharmony_ci		security_ftr_clear(SEC_FTR_L1D_FLUSH_UACCESS);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	if (fw_feature_is("enabled", "no-need-store-drain-on-priv-state-switch", np))
10862306a36Sopenharmony_ci		security_ftr_clear(SEC_FTR_STF_BARRIER);
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic void __init pnv_setup_security_mitigations(void)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	struct device_node *np, *fw_features;
11462306a36Sopenharmony_ci	enum l1d_flush_type type;
11562306a36Sopenharmony_ci	bool enable;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	/* Default to fallback in case fw-features are not available */
11862306a36Sopenharmony_ci	type = L1D_FLUSH_FALLBACK;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	np = of_find_node_by_name(NULL, "ibm,opal");
12162306a36Sopenharmony_ci	fw_features = of_get_child_by_name(np, "fw-features");
12262306a36Sopenharmony_ci	of_node_put(np);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	if (fw_features) {
12562306a36Sopenharmony_ci		init_fw_feat_flags(fw_features);
12662306a36Sopenharmony_ci		of_node_put(fw_features);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci		if (security_ftr_enabled(SEC_FTR_L1D_FLUSH_TRIG2))
12962306a36Sopenharmony_ci			type = L1D_FLUSH_MTTRIG;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci		if (security_ftr_enabled(SEC_FTR_L1D_FLUSH_ORI30))
13262306a36Sopenharmony_ci			type = L1D_FLUSH_ORI;
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	/*
13662306a36Sopenharmony_ci	 * The issues addressed by the entry and uaccess flush don't affect P7
13762306a36Sopenharmony_ci	 * or P8, so on bare metal disable them explicitly in case firmware does
13862306a36Sopenharmony_ci	 * not include the features to disable them. POWER9 and newer processors
13962306a36Sopenharmony_ci	 * should have the appropriate firmware flags.
14062306a36Sopenharmony_ci	 */
14162306a36Sopenharmony_ci	if (pvr_version_is(PVR_POWER7) || pvr_version_is(PVR_POWER7p) ||
14262306a36Sopenharmony_ci	    pvr_version_is(PVR_POWER8E) || pvr_version_is(PVR_POWER8NVL) ||
14362306a36Sopenharmony_ci	    pvr_version_is(PVR_POWER8)) {
14462306a36Sopenharmony_ci		security_ftr_clear(SEC_FTR_L1D_FLUSH_ENTRY);
14562306a36Sopenharmony_ci		security_ftr_clear(SEC_FTR_L1D_FLUSH_UACCESS);
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) && \
14962306a36Sopenharmony_ci		 (security_ftr_enabled(SEC_FTR_L1D_FLUSH_PR)   || \
15062306a36Sopenharmony_ci		  security_ftr_enabled(SEC_FTR_L1D_FLUSH_HV));
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	setup_rfi_flush(type, enable);
15362306a36Sopenharmony_ci	setup_count_cache_flush();
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) &&
15662306a36Sopenharmony_ci		 security_ftr_enabled(SEC_FTR_L1D_FLUSH_ENTRY);
15762306a36Sopenharmony_ci	setup_entry_flush(enable);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	enable = security_ftr_enabled(SEC_FTR_FAVOUR_SECURITY) &&
16062306a36Sopenharmony_ci		 security_ftr_enabled(SEC_FTR_L1D_FLUSH_UACCESS);
16162306a36Sopenharmony_ci	setup_uaccess_flush(enable);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	setup_stf_barrier();
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic void __init pnv_check_guarded_cores(void)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	struct device_node *dn;
16962306a36Sopenharmony_ci	int bad_count = 0;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	for_each_node_by_type(dn, "cpu") {
17262306a36Sopenharmony_ci		if (of_property_match_string(dn, "status", "bad") >= 0)
17362306a36Sopenharmony_ci			bad_count++;
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	if (bad_count) {
17762306a36Sopenharmony_ci		printk("  _     _______________\n");
17862306a36Sopenharmony_ci		pr_cont(" | |   /               \\\n");
17962306a36Sopenharmony_ci		pr_cont(" | |   |    WARNING!   |\n");
18062306a36Sopenharmony_ci		pr_cont(" | |   |               |\n");
18162306a36Sopenharmony_ci		pr_cont(" | |   | It looks like |\n");
18262306a36Sopenharmony_ci		pr_cont(" |_|   |  you have %*d |\n", 3, bad_count);
18362306a36Sopenharmony_ci		pr_cont("  _    | guarded cores |\n");
18462306a36Sopenharmony_ci		pr_cont(" (_)   \\_______________/\n");
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic void __init pnv_setup_arch(void)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	set_arch_panic_timeout(10, ARCH_PANIC_TIMEOUT);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	pnv_setup_security_mitigations();
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	/* Initialize SMP */
19562306a36Sopenharmony_ci	pnv_smp_init();
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	/* Setup RTC and NVRAM callbacks */
19862306a36Sopenharmony_ci	if (firmware_has_feature(FW_FEATURE_OPAL))
19962306a36Sopenharmony_ci		opal_nvram_init();
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	/* Enable NAP mode */
20262306a36Sopenharmony_ci	powersave_nap = 1;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	pnv_check_guarded_cores();
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	/* XXX PMCS */
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	pnv_rng_init();
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic void __init pnv_add_hw_description(void)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	struct device_node *dn;
21462306a36Sopenharmony_ci	const char *s;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	dn = of_find_node_by_path("/ibm,opal/firmware");
21762306a36Sopenharmony_ci	if (!dn)
21862306a36Sopenharmony_ci		return;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	if (of_property_read_string(dn, "version", &s) == 0 ||
22162306a36Sopenharmony_ci	    of_property_read_string(dn, "git-id", &s) == 0)
22262306a36Sopenharmony_ci		seq_buf_printf(&ppc_hw_desc, "opal:%s ", s);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	if (of_property_read_string(dn, "mi-version", &s) == 0)
22562306a36Sopenharmony_ci		seq_buf_printf(&ppc_hw_desc, "mi:%s ", s);
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	of_node_put(dn);
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic void __init pnv_init(void)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	pnv_add_hw_description();
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	/*
23562306a36Sopenharmony_ci	 * Initialize the LPC bus now so that legacy serial
23662306a36Sopenharmony_ci	 * ports can be found on it
23762306a36Sopenharmony_ci	 */
23862306a36Sopenharmony_ci	opal_lpc_init();
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci#ifdef CONFIG_HVC_OPAL
24162306a36Sopenharmony_ci	if (firmware_has_feature(FW_FEATURE_OPAL))
24262306a36Sopenharmony_ci		hvc_opal_init_early();
24362306a36Sopenharmony_ci	else
24462306a36Sopenharmony_ci#endif
24562306a36Sopenharmony_ci		add_preferred_console("hvc", 0, NULL);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci#ifdef CONFIG_PPC_64S_HASH_MMU
24862306a36Sopenharmony_ci	if (!radix_enabled()) {
24962306a36Sopenharmony_ci		size_t size = sizeof(struct slb_entry) * mmu_slb_size;
25062306a36Sopenharmony_ci		int i;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci		/* Allocate per cpu area to save old slb contents during MCE */
25362306a36Sopenharmony_ci		for_each_possible_cpu(i) {
25462306a36Sopenharmony_ci			paca_ptrs[i]->mce_faulty_slbs =
25562306a36Sopenharmony_ci					memblock_alloc_node(size,
25662306a36Sopenharmony_ci						__alignof__(struct slb_entry),
25762306a36Sopenharmony_ci						cpu_to_node(i));
25862306a36Sopenharmony_ci		}
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci#endif
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic void __init pnv_init_IRQ(void)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	/* Try using a XIVE if available, otherwise use a XICS */
26662306a36Sopenharmony_ci	if (!xive_native_init())
26762306a36Sopenharmony_ci		xics_init();
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	WARN_ON(!ppc_md.get_irq);
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic void pnv_show_cpuinfo(struct seq_file *m)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	struct device_node *root;
27562306a36Sopenharmony_ci	const char *model = "";
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	root = of_find_node_by_path("/");
27862306a36Sopenharmony_ci	if (root)
27962306a36Sopenharmony_ci		model = of_get_property(root, "model", NULL);
28062306a36Sopenharmony_ci	seq_printf(m, "machine\t\t: PowerNV %s\n", model);
28162306a36Sopenharmony_ci	if (firmware_has_feature(FW_FEATURE_OPAL))
28262306a36Sopenharmony_ci		seq_printf(m, "firmware\t: OPAL\n");
28362306a36Sopenharmony_ci	else
28462306a36Sopenharmony_ci		seq_printf(m, "firmware\t: BML\n");
28562306a36Sopenharmony_ci	of_node_put(root);
28662306a36Sopenharmony_ci	if (radix_enabled())
28762306a36Sopenharmony_ci		seq_printf(m, "MMU\t\t: Radix\n");
28862306a36Sopenharmony_ci	else
28962306a36Sopenharmony_ci		seq_printf(m, "MMU\t\t: Hash\n");
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cistatic void pnv_prepare_going_down(void)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	/*
29562306a36Sopenharmony_ci	 * Disable all notifiers from OPAL, we can't
29662306a36Sopenharmony_ci	 * service interrupts anymore anyway
29762306a36Sopenharmony_ci	 */
29862306a36Sopenharmony_ci	opal_event_shutdown();
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	/* Print flash update message if one is scheduled. */
30162306a36Sopenharmony_ci	opal_flash_update_print_message();
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	smp_send_stop();
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	hard_irq_disable();
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_cistatic void  __noreturn pnv_restart(char *cmd)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	long rc;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	pnv_prepare_going_down();
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	do {
31562306a36Sopenharmony_ci		if (!cmd || !strlen(cmd))
31662306a36Sopenharmony_ci			rc = opal_cec_reboot();
31762306a36Sopenharmony_ci		else if (strcmp(cmd, "full") == 0)
31862306a36Sopenharmony_ci			rc = opal_cec_reboot2(OPAL_REBOOT_FULL_IPL, NULL);
31962306a36Sopenharmony_ci		else if (strcmp(cmd, "mpipl") == 0)
32062306a36Sopenharmony_ci			rc = opal_cec_reboot2(OPAL_REBOOT_MPIPL, NULL);
32162306a36Sopenharmony_ci		else if (strcmp(cmd, "error") == 0)
32262306a36Sopenharmony_ci			rc = opal_cec_reboot2(OPAL_REBOOT_PLATFORM_ERROR, NULL);
32362306a36Sopenharmony_ci		else if (strcmp(cmd, "fast") == 0)
32462306a36Sopenharmony_ci			rc = opal_cec_reboot2(OPAL_REBOOT_FAST, NULL);
32562306a36Sopenharmony_ci		else
32662306a36Sopenharmony_ci			rc = OPAL_UNSUPPORTED;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci		if (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
32962306a36Sopenharmony_ci			/* Opal is busy wait for some time and retry */
33062306a36Sopenharmony_ci			opal_poll_events(NULL);
33162306a36Sopenharmony_ci			mdelay(10);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci		} else	if (cmd && rc) {
33462306a36Sopenharmony_ci			/* Unknown error while issuing reboot */
33562306a36Sopenharmony_ci			if (rc == OPAL_UNSUPPORTED)
33662306a36Sopenharmony_ci				pr_err("Unsupported '%s' reboot.\n", cmd);
33762306a36Sopenharmony_ci			else
33862306a36Sopenharmony_ci				pr_err("Unable to issue '%s' reboot. Err=%ld\n",
33962306a36Sopenharmony_ci				       cmd, rc);
34062306a36Sopenharmony_ci			pr_info("Forcing a cec-reboot\n");
34162306a36Sopenharmony_ci			cmd = NULL;
34262306a36Sopenharmony_ci			rc = OPAL_BUSY;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci		} else if (rc != OPAL_SUCCESS) {
34562306a36Sopenharmony_ci			/* Unknown error while issuing cec-reboot */
34662306a36Sopenharmony_ci			pr_err("Unable to reboot. Err=%ld\n", rc);
34762306a36Sopenharmony_ci		}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	} while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	for (;;)
35262306a36Sopenharmony_ci		opal_poll_events(NULL);
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic void __noreturn pnv_power_off(void)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	long rc = OPAL_BUSY;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	pnv_prepare_going_down();
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
36262306a36Sopenharmony_ci		rc = opal_cec_power_down(0);
36362306a36Sopenharmony_ci		if (rc == OPAL_BUSY_EVENT)
36462306a36Sopenharmony_ci			opal_poll_events(NULL);
36562306a36Sopenharmony_ci		else
36662306a36Sopenharmony_ci			mdelay(10);
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci	for (;;)
36962306a36Sopenharmony_ci		opal_poll_events(NULL);
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic void __noreturn pnv_halt(void)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	pnv_power_off();
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic void pnv_progress(char *s, unsigned short hex)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistatic void pnv_shutdown(void)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	/* Let the PCI code clear up IODA tables */
38462306a36Sopenharmony_ci	pnv_pci_shutdown();
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	/*
38762306a36Sopenharmony_ci	 * Stop OPAL activity: Unregister all OPAL interrupts so they
38862306a36Sopenharmony_ci	 * don't fire up while we kexec and make sure all potentially
38962306a36Sopenharmony_ci	 * DMA'ing ops are complete (such as dump retrieval).
39062306a36Sopenharmony_ci	 */
39162306a36Sopenharmony_ci	opal_shutdown();
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci#ifdef CONFIG_KEXEC_CORE
39562306a36Sopenharmony_cistatic void pnv_kexec_wait_secondaries_down(void)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	int my_cpu, i, notified = -1;
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	my_cpu = get_cpu();
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	for_each_online_cpu(i) {
40262306a36Sopenharmony_ci		uint8_t status;
40362306a36Sopenharmony_ci		int64_t rc, timeout = 1000;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci		if (i == my_cpu)
40662306a36Sopenharmony_ci			continue;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci		for (;;) {
40962306a36Sopenharmony_ci			rc = opal_query_cpu_status(get_hard_smp_processor_id(i),
41062306a36Sopenharmony_ci						   &status);
41162306a36Sopenharmony_ci			if (rc != OPAL_SUCCESS || status != OPAL_THREAD_STARTED)
41262306a36Sopenharmony_ci				break;
41362306a36Sopenharmony_ci			barrier();
41462306a36Sopenharmony_ci			if (i != notified) {
41562306a36Sopenharmony_ci				printk(KERN_INFO "kexec: waiting for cpu %d "
41662306a36Sopenharmony_ci				       "(physical %d) to enter OPAL\n",
41762306a36Sopenharmony_ci				       i, paca_ptrs[i]->hw_cpu_id);
41862306a36Sopenharmony_ci				notified = i;
41962306a36Sopenharmony_ci			}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci			/*
42262306a36Sopenharmony_ci			 * On crash secondaries might be unreachable or hung,
42362306a36Sopenharmony_ci			 * so timeout if we've waited too long
42462306a36Sopenharmony_ci			 * */
42562306a36Sopenharmony_ci			mdelay(1);
42662306a36Sopenharmony_ci			if (timeout-- == 0) {
42762306a36Sopenharmony_ci				printk(KERN_ERR "kexec: timed out waiting for "
42862306a36Sopenharmony_ci				       "cpu %d (physical %d) to enter OPAL\n",
42962306a36Sopenharmony_ci				       i, paca_ptrs[i]->hw_cpu_id);
43062306a36Sopenharmony_ci				break;
43162306a36Sopenharmony_ci			}
43262306a36Sopenharmony_ci		}
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_cistatic void pnv_kexec_cpu_down(int crash_shutdown, int secondary)
43762306a36Sopenharmony_ci{
43862306a36Sopenharmony_ci	u64 reinit_flags;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	if (xive_enabled())
44162306a36Sopenharmony_ci		xive_teardown_cpu();
44262306a36Sopenharmony_ci	else
44362306a36Sopenharmony_ci		xics_kexec_teardown_cpu(secondary);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	/* On OPAL, we return all CPUs to firmware */
44662306a36Sopenharmony_ci	if (!firmware_has_feature(FW_FEATURE_OPAL))
44762306a36Sopenharmony_ci		return;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	if (secondary) {
45062306a36Sopenharmony_ci		/* Return secondary CPUs to firmware on OPAL v3 */
45162306a36Sopenharmony_ci		mb();
45262306a36Sopenharmony_ci		get_paca()->kexec_state = KEXEC_STATE_REAL_MODE;
45362306a36Sopenharmony_ci		mb();
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci		/* Return the CPU to OPAL */
45662306a36Sopenharmony_ci		opal_return_cpu();
45762306a36Sopenharmony_ci	} else {
45862306a36Sopenharmony_ci		/* Primary waits for the secondaries to have reached OPAL */
45962306a36Sopenharmony_ci		pnv_kexec_wait_secondaries_down();
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci		/* Switch XIVE back to emulation mode */
46262306a36Sopenharmony_ci		if (xive_enabled())
46362306a36Sopenharmony_ci			xive_shutdown();
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci		/*
46662306a36Sopenharmony_ci		 * We might be running as little-endian - now that interrupts
46762306a36Sopenharmony_ci		 * are disabled, reset the HILE bit to big-endian so we don't
46862306a36Sopenharmony_ci		 * take interrupts in the wrong endian later
46962306a36Sopenharmony_ci		 *
47062306a36Sopenharmony_ci		 * We reinit to enable both radix and hash on P9 to ensure
47162306a36Sopenharmony_ci		 * the mode used by the next kernel is always supported.
47262306a36Sopenharmony_ci		 */
47362306a36Sopenharmony_ci		reinit_flags = OPAL_REINIT_CPUS_HILE_BE;
47462306a36Sopenharmony_ci		if (cpu_has_feature(CPU_FTR_ARCH_300))
47562306a36Sopenharmony_ci			reinit_flags |= OPAL_REINIT_CPUS_MMU_RADIX |
47662306a36Sopenharmony_ci				OPAL_REINIT_CPUS_MMU_HASH;
47762306a36Sopenharmony_ci		opal_reinit_cpus(reinit_flags);
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci}
48062306a36Sopenharmony_ci#endif /* CONFIG_KEXEC_CORE */
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci#ifdef CONFIG_MEMORY_HOTPLUG
48362306a36Sopenharmony_cistatic unsigned long pnv_memory_block_size(void)
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci	return memory_block_size;
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ci#endif
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic void __init pnv_setup_machdep_opal(void)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	ppc_md.get_boot_time = opal_get_boot_time;
49262306a36Sopenharmony_ci	ppc_md.restart = pnv_restart;
49362306a36Sopenharmony_ci	pm_power_off = pnv_power_off;
49462306a36Sopenharmony_ci	ppc_md.halt = pnv_halt;
49562306a36Sopenharmony_ci	/* ppc_md.system_reset_exception gets filled in by pnv_smp_init() */
49662306a36Sopenharmony_ci	ppc_md.machine_check_exception = opal_machine_check;
49762306a36Sopenharmony_ci	ppc_md.mce_check_early_recovery = opal_mce_check_early_recovery;
49862306a36Sopenharmony_ci	if (opal_check_token(OPAL_HANDLE_HMI2))
49962306a36Sopenharmony_ci		ppc_md.hmi_exception_early = opal_hmi_exception_early2;
50062306a36Sopenharmony_ci	else
50162306a36Sopenharmony_ci		ppc_md.hmi_exception_early = opal_hmi_exception_early;
50262306a36Sopenharmony_ci	ppc_md.handle_hmi_exception = opal_handle_hmi_exception;
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_cistatic int __init pnv_probe(void)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	if (firmware_has_feature(FW_FEATURE_OPAL))
50862306a36Sopenharmony_ci		pnv_setup_machdep_opal();
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	pr_debug("PowerNV detected !\n");
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	pnv_init();
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	return 1;
51562306a36Sopenharmony_ci}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
51862306a36Sopenharmony_civoid __init pnv_tm_init(void)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	if (!firmware_has_feature(FW_FEATURE_OPAL) ||
52162306a36Sopenharmony_ci	    !pvr_version_is(PVR_POWER9) ||
52262306a36Sopenharmony_ci	    early_cpu_has_feature(CPU_FTR_TM))
52362306a36Sopenharmony_ci		return;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	if (opal_reinit_cpus(OPAL_REINIT_CPUS_TM_SUSPEND_DISABLED) != OPAL_SUCCESS)
52662306a36Sopenharmony_ci		return;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	pr_info("Enabling TM (Transactional Memory) with Suspend Disabled\n");
52962306a36Sopenharmony_ci	cur_cpu_spec->cpu_features |= CPU_FTR_TM;
53062306a36Sopenharmony_ci	/* Make sure "normal" HTM is off (it should be) */
53162306a36Sopenharmony_ci	cur_cpu_spec->cpu_user_features2 &= ~PPC_FEATURE2_HTM;
53262306a36Sopenharmony_ci	/* Turn on no suspend mode, and HTM no SC */
53362306a36Sopenharmony_ci	cur_cpu_spec->cpu_user_features2 |= PPC_FEATURE2_HTM_NO_SUSPEND | \
53462306a36Sopenharmony_ci					    PPC_FEATURE2_HTM_NOSC;
53562306a36Sopenharmony_ci	tm_suspend_disabled = true;
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ci#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci/*
54062306a36Sopenharmony_ci * Returns the cpu frequency for 'cpu' in Hz. This is used by
54162306a36Sopenharmony_ci * /proc/cpuinfo
54262306a36Sopenharmony_ci */
54362306a36Sopenharmony_cistatic unsigned long pnv_get_proc_freq(unsigned int cpu)
54462306a36Sopenharmony_ci{
54562306a36Sopenharmony_ci	unsigned long ret_freq;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	ret_freq = cpufreq_get(cpu) * 1000ul;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	/*
55062306a36Sopenharmony_ci	 * If the backend cpufreq driver does not exist,
55162306a36Sopenharmony_ci         * then fallback to old way of reporting the clockrate.
55262306a36Sopenharmony_ci	 */
55362306a36Sopenharmony_ci	if (!ret_freq)
55462306a36Sopenharmony_ci		ret_freq = ppc_proc_freq;
55562306a36Sopenharmony_ci	return ret_freq;
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_cistatic long pnv_machine_check_early(struct pt_regs *regs)
55962306a36Sopenharmony_ci{
56062306a36Sopenharmony_ci	long handled = 0;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	if (cur_cpu_spec && cur_cpu_spec->machine_check_early)
56362306a36Sopenharmony_ci		handled = cur_cpu_spec->machine_check_early(regs);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	return handled;
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_cidefine_machine(powernv) {
56962306a36Sopenharmony_ci	.name			= "PowerNV",
57062306a36Sopenharmony_ci	.compatible		= "ibm,powernv",
57162306a36Sopenharmony_ci	.probe			= pnv_probe,
57262306a36Sopenharmony_ci	.setup_arch		= pnv_setup_arch,
57362306a36Sopenharmony_ci	.init_IRQ		= pnv_init_IRQ,
57462306a36Sopenharmony_ci	.show_cpuinfo		= pnv_show_cpuinfo,
57562306a36Sopenharmony_ci	.get_proc_freq          = pnv_get_proc_freq,
57662306a36Sopenharmony_ci	.discover_phbs		= pnv_pci_init,
57762306a36Sopenharmony_ci	.progress		= pnv_progress,
57862306a36Sopenharmony_ci	.machine_shutdown	= pnv_shutdown,
57962306a36Sopenharmony_ci	.power_save             = NULL,
58062306a36Sopenharmony_ci	.machine_check_early	= pnv_machine_check_early,
58162306a36Sopenharmony_ci#ifdef CONFIG_KEXEC_CORE
58262306a36Sopenharmony_ci	.kexec_cpu_down		= pnv_kexec_cpu_down,
58362306a36Sopenharmony_ci#endif
58462306a36Sopenharmony_ci#ifdef CONFIG_MEMORY_HOTPLUG
58562306a36Sopenharmony_ci	.memory_block_size	= pnv_memory_block_size,
58662306a36Sopenharmony_ci#endif
58762306a36Sopenharmony_ci};
588