162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * SCLP early driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright IBM Corp. 2013 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define KMSG_COMPONENT "sclp_early" 962306a36Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/errno.h> 1262306a36Sopenharmony_ci#include <linux/memblock.h> 1362306a36Sopenharmony_ci#include <asm/ctl_reg.h> 1462306a36Sopenharmony_ci#include <asm/sclp.h> 1562306a36Sopenharmony_ci#include <asm/ipl.h> 1662306a36Sopenharmony_ci#include <asm/setup.h> 1762306a36Sopenharmony_ci#include <asm/facility.h> 1862306a36Sopenharmony_ci#include "sclp_sdias.h" 1962306a36Sopenharmony_ci#include "sclp.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic struct sclp_ipl_info sclp_ipl_info; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistruct sclp_info sclp; 2462306a36Sopenharmony_ciEXPORT_SYMBOL(sclp); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic void __init sclp_early_facilities_detect(void) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci struct sclp_core_entry *cpue; 2962306a36Sopenharmony_ci struct read_info_sccb *sccb; 3062306a36Sopenharmony_ci u16 boot_cpu_address, cpu; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci sccb = sclp_early_get_info(); 3362306a36Sopenharmony_ci if (!sccb) 3462306a36Sopenharmony_ci return; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci sclp.facilities = sccb->facilities; 3762306a36Sopenharmony_ci sclp.has_sprp = !!(sccb->fac84 & 0x02); 3862306a36Sopenharmony_ci sclp.has_core_type = !!(sccb->fac84 & 0x01); 3962306a36Sopenharmony_ci sclp.has_gsls = !!(sccb->fac85 & 0x80); 4062306a36Sopenharmony_ci sclp.has_64bscao = !!(sccb->fac116 & 0x80); 4162306a36Sopenharmony_ci sclp.has_cmma = !!(sccb->fac116 & 0x40); 4262306a36Sopenharmony_ci sclp.has_esca = !!(sccb->fac116 & 0x08); 4362306a36Sopenharmony_ci sclp.has_pfmfi = !!(sccb->fac117 & 0x40); 4462306a36Sopenharmony_ci sclp.has_ibs = !!(sccb->fac117 & 0x20); 4562306a36Sopenharmony_ci sclp.has_gisaf = !!(sccb->fac118 & 0x08); 4662306a36Sopenharmony_ci sclp.has_hvs = !!(sccb->fac119 & 0x80); 4762306a36Sopenharmony_ci sclp.has_kss = !!(sccb->fac98 & 0x01); 4862306a36Sopenharmony_ci sclp.has_aisii = !!(sccb->fac118 & 0x40); 4962306a36Sopenharmony_ci sclp.has_aeni = !!(sccb->fac118 & 0x20); 5062306a36Sopenharmony_ci sclp.has_aisi = !!(sccb->fac118 & 0x10); 5162306a36Sopenharmony_ci sclp.has_zpci_lsi = !!(sccb->fac118 & 0x01); 5262306a36Sopenharmony_ci if (sccb->fac85 & 0x02) 5362306a36Sopenharmony_ci S390_lowcore.machine_flags |= MACHINE_FLAG_ESOP; 5462306a36Sopenharmony_ci if (sccb->fac91 & 0x40) 5562306a36Sopenharmony_ci S390_lowcore.machine_flags |= MACHINE_FLAG_TLB_GUEST; 5662306a36Sopenharmony_ci if (sccb->cpuoff > 134) { 5762306a36Sopenharmony_ci sclp.has_diag318 = !!(sccb->byte_134 & 0x80); 5862306a36Sopenharmony_ci sclp.has_diag320 = !!(sccb->byte_134 & 0x04); 5962306a36Sopenharmony_ci sclp.has_iplcc = !!(sccb->byte_134 & 0x02); 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci if (sccb->cpuoff > 137) { 6262306a36Sopenharmony_ci sclp.has_sipl = !!(sccb->cbl & 0x4000); 6362306a36Sopenharmony_ci sclp.has_sipl_eckd = !!(sccb->cbl & 0x2000); 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci sclp.rnmax = sccb->rnmax ? sccb->rnmax : sccb->rnmax2; 6662306a36Sopenharmony_ci sclp.rzm = sccb->rnsize ? sccb->rnsize : sccb->rnsize2; 6762306a36Sopenharmony_ci sclp.rzm <<= 20; 6862306a36Sopenharmony_ci sclp.ibc = sccb->ibc; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (sccb->hamaxpow && sccb->hamaxpow < 64) 7162306a36Sopenharmony_ci sclp.hamax = (1UL << sccb->hamaxpow) - 1; 7262306a36Sopenharmony_ci else 7362306a36Sopenharmony_ci sclp.hamax = U64_MAX; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (!sccb->hcpua) { 7662306a36Sopenharmony_ci if (MACHINE_IS_VM) 7762306a36Sopenharmony_ci sclp.max_cores = 64; 7862306a36Sopenharmony_ci else 7962306a36Sopenharmony_ci sclp.max_cores = sccb->ncpurl; 8062306a36Sopenharmony_ci } else { 8162306a36Sopenharmony_ci sclp.max_cores = sccb->hcpua + 1; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci boot_cpu_address = stap(); 8562306a36Sopenharmony_ci cpue = (void *)sccb + sccb->cpuoff; 8662306a36Sopenharmony_ci for (cpu = 0; cpu < sccb->ncpurl; cpue++, cpu++) { 8762306a36Sopenharmony_ci if (boot_cpu_address != cpue->core_id) 8862306a36Sopenharmony_ci continue; 8962306a36Sopenharmony_ci sclp.has_siif = cpue->siif; 9062306a36Sopenharmony_ci sclp.has_sigpif = cpue->sigpif; 9162306a36Sopenharmony_ci sclp.has_sief2 = cpue->sief2; 9262306a36Sopenharmony_ci sclp.has_gpere = cpue->gpere; 9362306a36Sopenharmony_ci sclp.has_ib = cpue->ib; 9462306a36Sopenharmony_ci sclp.has_cei = cpue->cei; 9562306a36Sopenharmony_ci sclp.has_skey = cpue->skey; 9662306a36Sopenharmony_ci break; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci /* Save IPL information */ 10062306a36Sopenharmony_ci sclp_ipl_info.is_valid = 1; 10162306a36Sopenharmony_ci if (sccb->fac91 & 0x2) 10262306a36Sopenharmony_ci sclp_ipl_info.has_dump = 1; 10362306a36Sopenharmony_ci memcpy(&sclp_ipl_info.loadparm, &sccb->loadparm, LOADPARM_LEN); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (sccb->hsa_size) 10662306a36Sopenharmony_ci sclp.hsa_size = (sccb->hsa_size - 1) * PAGE_SIZE; 10762306a36Sopenharmony_ci sclp.mtid = (sccb->fac42 & 0x80) ? (sccb->fac42 & 31) : 0; 10862306a36Sopenharmony_ci sclp.mtid_cp = (sccb->fac42 & 0x80) ? (sccb->fac43 & 31) : 0; 10962306a36Sopenharmony_ci sclp.mtid_prev = (sccb->fac42 & 0x80) ? (sccb->fac66 & 31) : 0; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci sclp.hmfai = sccb->hmfai; 11262306a36Sopenharmony_ci sclp.has_dirq = !!(sccb->cpudirq & 0x80); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* 11662306a36Sopenharmony_ci * This function will be called after sclp_early_facilities_detect(), which gets 11762306a36Sopenharmony_ci * called from early.c code. The sclp_early_facilities_detect() function retrieves 11862306a36Sopenharmony_ci * and saves the IPL information. 11962306a36Sopenharmony_ci */ 12062306a36Sopenharmony_civoid __init sclp_early_get_ipl_info(struct sclp_ipl_info *info) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci *info = sclp_ipl_info; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ciint __init sclp_early_get_core_info(struct sclp_core_info *info) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci struct read_cpu_info_sccb *sccb; 12862306a36Sopenharmony_ci int length = test_facility(140) ? EXT_SCCB_READ_CPU : PAGE_SIZE; 12962306a36Sopenharmony_ci int rc = 0; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if (!SCLP_HAS_CPU_INFO) 13262306a36Sopenharmony_ci return -EOPNOTSUPP; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci sccb = memblock_alloc_low(length, PAGE_SIZE); 13562306a36Sopenharmony_ci if (!sccb) 13662306a36Sopenharmony_ci return -ENOMEM; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci memset(sccb, 0, length); 13962306a36Sopenharmony_ci sccb->header.length = length; 14062306a36Sopenharmony_ci sccb->header.control_mask[2] = 0x80; 14162306a36Sopenharmony_ci if (sclp_early_cmd(SCLP_CMDW_READ_CPU_INFO, sccb)) { 14262306a36Sopenharmony_ci rc = -EIO; 14362306a36Sopenharmony_ci goto out; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci if (sccb->header.response_code != 0x0010) { 14662306a36Sopenharmony_ci rc = -EIO; 14762306a36Sopenharmony_ci goto out; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci sclp_fill_core_info(info, sccb); 15062306a36Sopenharmony_ciout: 15162306a36Sopenharmony_ci memblock_free(sccb, length); 15262306a36Sopenharmony_ci return rc; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic void __init sclp_early_console_detect(struct init_sccb *sccb) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci if (sccb->header.response_code != 0x20) 15862306a36Sopenharmony_ci return; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci if (sclp_early_con_check_vt220(sccb)) 16162306a36Sopenharmony_ci sclp.has_vt220 = 1; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (sclp_early_con_check_linemode(sccb)) 16462306a36Sopenharmony_ci sclp.has_linemode = 1; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_civoid __init __no_sanitize_address sclp_early_adjust_va(void) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci sclp_early_sccb = __va((unsigned long)sclp_early_sccb); 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_civoid __init sclp_early_detect(void) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci void *sccb = sclp_early_sccb; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci sclp_early_facilities_detect(); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* 17962306a36Sopenharmony_ci * Turn off SCLP event notifications. Also save remote masks in the 18062306a36Sopenharmony_ci * sccb. These are sufficient to detect sclp console capabilities. 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_ci sclp_early_set_event_mask(sccb, 0, 0); 18362306a36Sopenharmony_ci sclp_early_console_detect(sccb); 18462306a36Sopenharmony_ci} 185