162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * pSeries firmware setup code. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Portions from arch/powerpc/platforms/pseries/setup.c: 662306a36Sopenharmony_ci * Copyright (C) 1995 Linus Torvalds 762306a36Sopenharmony_ci * Adapted from 'alpha' version by Gary Thomas 862306a36Sopenharmony_ci * Modified by Cort Dougan (cort@cs.nmt.edu) 962306a36Sopenharmony_ci * Modified by PPC64 Team, IBM Corp 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Portions from arch/powerpc/kernel/firmware.c 1262306a36Sopenharmony_ci * Copyright (C) 2001 Ben. Herrenschmidt (benh@kernel.crashing.org) 1362306a36Sopenharmony_ci * Modifications for ppc64: 1462306a36Sopenharmony_ci * Copyright (C) 2003 Dave Engebretsen <engebret@us.ibm.com> 1562306a36Sopenharmony_ci * Copyright (C) 2005 Stephen Rothwell, IBM Corporation 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * Copyright 2006 IBM Corporation. 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/of_fdt.h> 2262306a36Sopenharmony_ci#include <asm/firmware.h> 2362306a36Sopenharmony_ci#include <asm/prom.h> 2462306a36Sopenharmony_ci#include <asm/udbg.h> 2562306a36Sopenharmony_ci#include <asm/svm.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include "pseries.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistruct hypertas_fw_feature { 3062306a36Sopenharmony_ci unsigned long val; 3162306a36Sopenharmony_ci char * name; 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci * The names in this table match names in rtas/ibm,hypertas-functions. If the 3662306a36Sopenharmony_ci * entry ends in a '*', only upto the '*' is matched. Otherwise the entire 3762306a36Sopenharmony_ci * string must match. 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_cistatic __initdata struct hypertas_fw_feature 4062306a36Sopenharmony_cihypertas_fw_features_table[] = { 4162306a36Sopenharmony_ci {FW_FEATURE_PFT, "hcall-pft"}, 4262306a36Sopenharmony_ci {FW_FEATURE_TCE, "hcall-tce"}, 4362306a36Sopenharmony_ci {FW_FEATURE_SPRG0, "hcall-sprg0"}, 4462306a36Sopenharmony_ci {FW_FEATURE_DABR, "hcall-dabr"}, 4562306a36Sopenharmony_ci {FW_FEATURE_COPY, "hcall-copy"}, 4662306a36Sopenharmony_ci {FW_FEATURE_ASR, "hcall-asr"}, 4762306a36Sopenharmony_ci {FW_FEATURE_DEBUG, "hcall-debug"}, 4862306a36Sopenharmony_ci {FW_FEATURE_PERF, "hcall-perf"}, 4962306a36Sopenharmony_ci {FW_FEATURE_DUMP, "hcall-dump"}, 5062306a36Sopenharmony_ci {FW_FEATURE_INTERRUPT, "hcall-interrupt"}, 5162306a36Sopenharmony_ci {FW_FEATURE_MIGRATE, "hcall-migrate"}, 5262306a36Sopenharmony_ci {FW_FEATURE_PERFMON, "hcall-perfmon"}, 5362306a36Sopenharmony_ci {FW_FEATURE_CRQ, "hcall-crq"}, 5462306a36Sopenharmony_ci {FW_FEATURE_VIO, "hcall-vio"}, 5562306a36Sopenharmony_ci {FW_FEATURE_RDMA, "hcall-rdma"}, 5662306a36Sopenharmony_ci {FW_FEATURE_LLAN, "hcall-lLAN"}, 5762306a36Sopenharmony_ci {FW_FEATURE_BULK_REMOVE, "hcall-bulk"}, 5862306a36Sopenharmony_ci {FW_FEATURE_XDABR, "hcall-xdabr"}, 5962306a36Sopenharmony_ci {FW_FEATURE_PUT_TCE_IND | FW_FEATURE_STUFF_TCE, 6062306a36Sopenharmony_ci "hcall-multi-tce"}, 6162306a36Sopenharmony_ci {FW_FEATURE_SPLPAR, "hcall-splpar"}, 6262306a36Sopenharmony_ci {FW_FEATURE_VPHN, "hcall-vphn"}, 6362306a36Sopenharmony_ci {FW_FEATURE_SET_MODE, "hcall-set-mode"}, 6462306a36Sopenharmony_ci {FW_FEATURE_BEST_ENERGY, "hcall-best-energy-1*"}, 6562306a36Sopenharmony_ci {FW_FEATURE_HPT_RESIZE, "hcall-hpt-resize"}, 6662306a36Sopenharmony_ci {FW_FEATURE_BLOCK_REMOVE, "hcall-block-remove"}, 6762306a36Sopenharmony_ci {FW_FEATURE_PAPR_SCM, "hcall-scm"}, 6862306a36Sopenharmony_ci {FW_FEATURE_RPT_INVALIDATE, "hcall-rpt-invalidate"}, 6962306a36Sopenharmony_ci {FW_FEATURE_ENERGY_SCALE_INFO, "hcall-energy-scale-info"}, 7062306a36Sopenharmony_ci {FW_FEATURE_WATCHDOG, "hcall-watchdog"}, 7162306a36Sopenharmony_ci {FW_FEATURE_PLPKS, "hcall-pks"}, 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* Build up the firmware features bitmask using the contents of 7562306a36Sopenharmony_ci * device-tree/ibm,hypertas-functions. Ultimately this functionality may 7662306a36Sopenharmony_ci * be moved into prom.c prom_init(). 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_cistatic void __init fw_hypertas_feature_init(const char *hypertas, 7962306a36Sopenharmony_ci unsigned long len) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci const char *s; 8262306a36Sopenharmony_ci int i; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci pr_debug(" -> fw_hypertas_feature_init()\n"); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci for (s = hypertas; s < hypertas + len; s += strlen(s) + 1) { 8762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(hypertas_fw_features_table); i++) { 8862306a36Sopenharmony_ci const char *name = hypertas_fw_features_table[i].name; 8962306a36Sopenharmony_ci size_t size; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* 9262306a36Sopenharmony_ci * If there is a '*' at the end of name, only check 9362306a36Sopenharmony_ci * upto there 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_ci size = strlen(name); 9662306a36Sopenharmony_ci if (size && name[size - 1] == '*') { 9762306a36Sopenharmony_ci if (strncmp(name, s, size - 1)) 9862306a36Sopenharmony_ci continue; 9962306a36Sopenharmony_ci } else if (strcmp(name, s)) 10062306a36Sopenharmony_ci continue; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* we have a match */ 10362306a36Sopenharmony_ci powerpc_firmware_features |= 10462306a36Sopenharmony_ci hypertas_fw_features_table[i].val; 10562306a36Sopenharmony_ci break; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (is_secure_guest() && 11062306a36Sopenharmony_ci (powerpc_firmware_features & FW_FEATURE_PUT_TCE_IND)) { 11162306a36Sopenharmony_ci powerpc_firmware_features &= ~FW_FEATURE_PUT_TCE_IND; 11262306a36Sopenharmony_ci pr_debug("SVM: disabling PUT_TCE_IND firmware feature\n"); 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci pr_debug(" <- fw_hypertas_feature_init()\n"); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistruct vec5_fw_feature { 11962306a36Sopenharmony_ci unsigned long val; 12062306a36Sopenharmony_ci unsigned int feature; 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic __initdata struct vec5_fw_feature 12462306a36Sopenharmony_civec5_fw_features_table[] = { 12562306a36Sopenharmony_ci {FW_FEATURE_FORM1_AFFINITY, OV5_FORM1_AFFINITY}, 12662306a36Sopenharmony_ci {FW_FEATURE_PRRN, OV5_PRRN}, 12762306a36Sopenharmony_ci {FW_FEATURE_DRMEM_V2, OV5_DRMEM_V2}, 12862306a36Sopenharmony_ci {FW_FEATURE_DRC_INFO, OV5_DRC_INFO}, 12962306a36Sopenharmony_ci {FW_FEATURE_FORM2_AFFINITY, OV5_FORM2_AFFINITY}, 13062306a36Sopenharmony_ci}; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic void __init fw_vec5_feature_init(const char *vec5, unsigned long len) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci unsigned int index, feat; 13562306a36Sopenharmony_ci int i; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci pr_debug(" -> fw_vec5_feature_init()\n"); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vec5_fw_features_table); i++) { 14062306a36Sopenharmony_ci index = OV5_INDX(vec5_fw_features_table[i].feature); 14162306a36Sopenharmony_ci feat = OV5_FEAT(vec5_fw_features_table[i].feature); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (index < len && (vec5[index] & feat)) 14462306a36Sopenharmony_ci powerpc_firmware_features |= 14562306a36Sopenharmony_ci vec5_fw_features_table[i].val; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci pr_debug(" <- fw_vec5_feature_init()\n"); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci/* 15262306a36Sopenharmony_ci * Called very early, MMU is off, device-tree isn't unflattened 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_cistatic int __init probe_fw_features(unsigned long node, const char *uname, int 15562306a36Sopenharmony_ci depth, void *data) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci const char *prop; 15862306a36Sopenharmony_ci int len; 15962306a36Sopenharmony_ci static int hypertas_found; 16062306a36Sopenharmony_ci static int vec5_found; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci if (depth != 1) 16362306a36Sopenharmony_ci return 0; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (!strcmp(uname, "rtas") || !strcmp(uname, "rtas@0")) { 16662306a36Sopenharmony_ci prop = of_get_flat_dt_prop(node, "ibm,hypertas-functions", 16762306a36Sopenharmony_ci &len); 16862306a36Sopenharmony_ci if (prop) { 16962306a36Sopenharmony_ci powerpc_firmware_features |= FW_FEATURE_LPAR; 17062306a36Sopenharmony_ci fw_hypertas_feature_init(prop, len); 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci hypertas_found = 1; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (!strcmp(uname, "chosen")) { 17762306a36Sopenharmony_ci prop = of_get_flat_dt_prop(node, "ibm,architecture-vec-5", 17862306a36Sopenharmony_ci &len); 17962306a36Sopenharmony_ci if (prop) 18062306a36Sopenharmony_ci fw_vec5_feature_init(prop, len); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci vec5_found = 1; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return hypertas_found && vec5_found; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_civoid __init pseries_probe_fw_features(void) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci of_scan_flat_dt(probe_fw_features, NULL); 19162306a36Sopenharmony_ci} 192