1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 2395d31d4SMartin Schwidefsky /* 3395d31d4SMartin Schwidefsky * Copyright IBM Corp. 2008 4395d31d4SMartin Schwidefsky * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com) 5395d31d4SMartin Schwidefsky */ 6395d31d4SMartin Schwidefsky 7395d31d4SMartin Schwidefsky #define KMSG_COMPONENT "cpu" 8395d31d4SMartin Schwidefsky #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 9395d31d4SMartin Schwidefsky 10ca21872eSHeiko Carstens #include <linux/cpufeature.h> 11157467baSHeiko Carstens #include <linux/bitops.h> 12395d31d4SMartin Schwidefsky #include <linux/kernel.h> 1368e21be2SIngo Molnar #include <linux/sched/mm.h> 14395d31d4SMartin Schwidefsky #include <linux/init.h> 15395d31d4SMartin Schwidefsky #include <linux/seq_file.h> 16589ee628SIngo Molnar #include <linux/mm_types.h> 17395d31d4SMartin Schwidefsky #include <linux/delay.h> 1819726cecSHeiko Carstens #include <linux/cpu.h> 1968e21be2SIngo Molnar 201ec2772eSMartin Schwidefsky #include <asm/diag.h> 21097a116cSHeiko Carstens #include <asm/facility.h> 22395d31d4SMartin Schwidefsky #include <asm/elf.h> 23395d31d4SMartin Schwidefsky #include <asm/lowcore.h> 24395d31d4SMartin Schwidefsky #include <asm/param.h> 254d92f502SHeiko Carstens #include <asm/smp.h> 26395d31d4SMartin Schwidefsky 27097a116cSHeiko Carstens struct cpu_info { 28097a116cSHeiko Carstens unsigned int cpu_mhz_dynamic; 29097a116cSHeiko Carstens unsigned int cpu_mhz_static; 30097a116cSHeiko Carstens struct cpuid cpu_id; 31097a116cSHeiko Carstens }; 32097a116cSHeiko Carstens 33097a116cSHeiko Carstens static DEFINE_PER_CPU(struct cpu_info, cpu_info); 34097a116cSHeiko Carstens 35097a116cSHeiko Carstens static bool machine_has_cpu_mhz; 36097a116cSHeiko Carstens 37097a116cSHeiko Carstens void __init cpu_detect_mhz_feature(void) 38097a116cSHeiko Carstens { 39097a116cSHeiko Carstens if (test_facility(34) && __ecag(ECAG_CPU_ATTRIBUTE, 0) != -1UL) 40970ba6acSHeiko Carstens machine_has_cpu_mhz = true; 41097a116cSHeiko Carstens } 42097a116cSHeiko Carstens 43097a116cSHeiko Carstens static void update_cpu_mhz(void *arg) 44097a116cSHeiko Carstens { 45097a116cSHeiko Carstens unsigned long mhz; 46097a116cSHeiko Carstens struct cpu_info *c; 47097a116cSHeiko Carstens 48097a116cSHeiko Carstens mhz = __ecag(ECAG_CPU_ATTRIBUTE, 0); 49097a116cSHeiko Carstens c = this_cpu_ptr(&cpu_info); 50097a116cSHeiko Carstens c->cpu_mhz_dynamic = mhz >> 32; 51097a116cSHeiko Carstens c->cpu_mhz_static = mhz & 0xffffffff; 52097a116cSHeiko Carstens } 53097a116cSHeiko Carstens 54097a116cSHeiko Carstens void s390_update_cpu_mhz(void) 55097a116cSHeiko Carstens { 56097a116cSHeiko Carstens s390_adjust_jiffies(); 57097a116cSHeiko Carstens if (machine_has_cpu_mhz) 58097a116cSHeiko Carstens on_each_cpu(update_cpu_mhz, NULL, 0); 59097a116cSHeiko Carstens } 6094038a99SMartin Schwidefsky 6179ab11cdSChristian Borntraeger void notrace cpu_relax_yield(void) 624d92f502SHeiko Carstens { 631ec2772eSMartin Schwidefsky if (!smp_cpu_mtid && MACHINE_HAS_DIAG44) { 641ec2772eSMartin Schwidefsky diag_stat_inc(DIAG_STAT_X044); 654d92f502SHeiko Carstens asm volatile("diag 0,0,0x44"); 661ec2772eSMartin Schwidefsky } 674d92f502SHeiko Carstens barrier(); 684d92f502SHeiko Carstens } 6979ab11cdSChristian Borntraeger EXPORT_SYMBOL(cpu_relax_yield); 704d92f502SHeiko Carstens 7194038a99SMartin Schwidefsky /* 7294038a99SMartin Schwidefsky * cpu_init - initializes state that is per-CPU. 7394038a99SMartin Schwidefsky */ 74e2741f17SPaul Gortmaker void cpu_init(void) 7594038a99SMartin Schwidefsky { 76097a116cSHeiko Carstens struct cpuid *id = this_cpu_ptr(&cpu_info.cpu_id); 7794038a99SMartin Schwidefsky 7894038a99SMartin Schwidefsky get_cpu_id(id); 79097a116cSHeiko Carstens if (machine_has_cpu_mhz) 80097a116cSHeiko Carstens update_cpu_mhz(NULL); 81f1f10076SVegard Nossum mmgrab(&init_mm); 8294038a99SMartin Schwidefsky current->active_mm = &init_mm; 8394038a99SMartin Schwidefsky BUG_ON(current->mm); 8494038a99SMartin Schwidefsky enter_lazy_tlb(&init_mm, current); 8594038a99SMartin Schwidefsky } 8694038a99SMartin Schwidefsky 8794038a99SMartin Schwidefsky /* 888f00b3e2SHendrik Brueckner * cpu_have_feature - Test CPU features on module initialization 898f00b3e2SHendrik Brueckner */ 908f00b3e2SHendrik Brueckner int cpu_have_feature(unsigned int num) 918f00b3e2SHendrik Brueckner { 928f00b3e2SHendrik Brueckner return elf_hwcap & (1UL << num); 938f00b3e2SHendrik Brueckner } 948f00b3e2SHendrik Brueckner EXPORT_SYMBOL(cpu_have_feature); 958f00b3e2SHendrik Brueckner 96157467baSHeiko Carstens static void show_facilities(struct seq_file *m) 97157467baSHeiko Carstens { 98157467baSHeiko Carstens unsigned int bit; 99157467baSHeiko Carstens long *facilities; 100157467baSHeiko Carstens 101157467baSHeiko Carstens facilities = (long *)&S390_lowcore.stfle_fac_list; 102157467baSHeiko Carstens seq_puts(m, "facilities :"); 103157467baSHeiko Carstens for_each_set_bit_inv(bit, facilities, MAX_FACILITY_BIT) 104157467baSHeiko Carstens seq_printf(m, " %d", bit); 105157467baSHeiko Carstens seq_putc(m, '\n'); 106157467baSHeiko Carstens } 107157467baSHeiko Carstens 108219a21b3SHeiko Carstens static void show_cpu_summary(struct seq_file *m, void *v) 109395d31d4SMartin Schwidefsky { 110fbf3c542SHeiko Carstens static const char *hwcap_str[] = { 111395d31d4SMartin Schwidefsky "esan3", "zarch", "stfle", "msa", "ldisp", "eimm", "dfp", 112a8fd6168SMartin Schwidefsky "edat", "etf3eh", "highgprs", "te", "vx", "vxd", "vxe", "gs", 113a8fd6168SMartin Schwidefsky "vxe2", "vxp", "sort", "dflt" 114395d31d4SMartin Schwidefsky }; 1157f16d7e7SDavid Hildenbrand static const char * const int_hwcap_str[] = { 1167f16d7e7SDavid Hildenbrand "sie" 1177f16d7e7SDavid Hildenbrand }; 118219a21b3SHeiko Carstens int i, cpu; 119395d31d4SMartin Schwidefsky 120395d31d4SMartin Schwidefsky seq_printf(m, "vendor_id : IBM/S390\n" 121395d31d4SMartin Schwidefsky "# processors : %i\n" 122395d31d4SMartin Schwidefsky "bogomips per cpu: %lu.%02lu\n", 123395d31d4SMartin Schwidefsky num_online_cpus(), loops_per_jiffy/(500000/HZ), 124395d31d4SMartin Schwidefsky (loops_per_jiffy/(5000/HZ))%100); 12510f4954aSHeiko Carstens seq_printf(m, "max thread id : %d\n", smp_cpu_mtid); 126395d31d4SMartin Schwidefsky seq_puts(m, "features\t: "); 127fbf3c542SHeiko Carstens for (i = 0; i < ARRAY_SIZE(hwcap_str); i++) 128395d31d4SMartin Schwidefsky if (hwcap_str[i] && (elf_hwcap & (1UL << i))) 129395d31d4SMartin Schwidefsky seq_printf(m, "%s ", hwcap_str[i]); 1307f16d7e7SDavid Hildenbrand for (i = 0; i < ARRAY_SIZE(int_hwcap_str); i++) 1317f16d7e7SDavid Hildenbrand if (int_hwcap_str[i] && (int_hwcap & (1UL << i))) 1327f16d7e7SDavid Hildenbrand seq_printf(m, "%s ", int_hwcap_str[i]); 133395d31d4SMartin Schwidefsky seq_puts(m, "\n"); 134157467baSHeiko Carstens show_facilities(m); 1356668022cSHeiko Carstens show_cacheinfo(m); 136219a21b3SHeiko Carstens for_each_online_cpu(cpu) { 137097a116cSHeiko Carstens struct cpuid *id = &per_cpu(cpu_info.cpu_id, cpu); 138219a21b3SHeiko Carstens 139219a21b3SHeiko Carstens seq_printf(m, "processor %d: " 140395d31d4SMartin Schwidefsky "version = %02X, " 141395d31d4SMartin Schwidefsky "identification = %06X, " 142395d31d4SMartin Schwidefsky "machine = %04X\n", 143219a21b3SHeiko Carstens cpu, id->version, id->ident, id->machine); 144395d31d4SMartin Schwidefsky } 145219a21b3SHeiko Carstens } 146219a21b3SHeiko Carstens 147097a116cSHeiko Carstens static void show_cpu_mhz(struct seq_file *m, unsigned long n) 148097a116cSHeiko Carstens { 149097a116cSHeiko Carstens struct cpu_info *c = per_cpu_ptr(&cpu_info, n); 150097a116cSHeiko Carstens 151097a116cSHeiko Carstens seq_printf(m, "cpu MHz dynamic : %d\n", c->cpu_mhz_dynamic); 152097a116cSHeiko Carstens seq_printf(m, "cpu MHz static : %d\n", c->cpu_mhz_static); 153097a116cSHeiko Carstens } 154097a116cSHeiko Carstens 155219a21b3SHeiko Carstens /* 156219a21b3SHeiko Carstens * show_cpuinfo - Get information on one CPU for use by procfs. 157219a21b3SHeiko Carstens */ 158219a21b3SHeiko Carstens static int show_cpuinfo(struct seq_file *m, void *v) 159219a21b3SHeiko Carstens { 160219a21b3SHeiko Carstens unsigned long n = (unsigned long) v - 1; 161219a21b3SHeiko Carstens 162219a21b3SHeiko Carstens if (!n) 163219a21b3SHeiko Carstens show_cpu_summary(m, v); 164097a116cSHeiko Carstens if (!machine_has_cpu_mhz) 165097a116cSHeiko Carstens return 0; 166109ab954SHeiko Carstens seq_printf(m, "\ncpu number : %ld\n", n); 167097a116cSHeiko Carstens show_cpu_mhz(m, n); 168395d31d4SMartin Schwidefsky return 0; 169395d31d4SMartin Schwidefsky } 170395d31d4SMartin Schwidefsky 171281eaa8cSHeiko Carstens static inline void *c_update(loff_t *pos) 172281eaa8cSHeiko Carstens { 173281eaa8cSHeiko Carstens if (*pos) 174281eaa8cSHeiko Carstens *pos = cpumask_next(*pos - 1, cpu_online_mask); 175281eaa8cSHeiko Carstens return *pos < nr_cpu_ids ? (void *)*pos + 1 : NULL; 176281eaa8cSHeiko Carstens } 177281eaa8cSHeiko Carstens 178395d31d4SMartin Schwidefsky static void *c_start(struct seq_file *m, loff_t *pos) 179395d31d4SMartin Schwidefsky { 180281eaa8cSHeiko Carstens get_online_cpus(); 181281eaa8cSHeiko Carstens return c_update(pos); 182395d31d4SMartin Schwidefsky } 183395d31d4SMartin Schwidefsky 184395d31d4SMartin Schwidefsky static void *c_next(struct seq_file *m, void *v, loff_t *pos) 185395d31d4SMartin Schwidefsky { 186395d31d4SMartin Schwidefsky ++*pos; 187281eaa8cSHeiko Carstens return c_update(pos); 188395d31d4SMartin Schwidefsky } 189395d31d4SMartin Schwidefsky 190395d31d4SMartin Schwidefsky static void c_stop(struct seq_file *m, void *v) 191395d31d4SMartin Schwidefsky { 192281eaa8cSHeiko Carstens put_online_cpus(); 193395d31d4SMartin Schwidefsky } 194395d31d4SMartin Schwidefsky 195395d31d4SMartin Schwidefsky const struct seq_operations cpuinfo_op = { 196395d31d4SMartin Schwidefsky .start = c_start, 197395d31d4SMartin Schwidefsky .next = c_next, 198395d31d4SMartin Schwidefsky .stop = c_stop, 199395d31d4SMartin Schwidefsky .show = show_cpuinfo, 200395d31d4SMartin Schwidefsky }; 2016b73044bSMartin Schwidefsky 2026b73044bSMartin Schwidefsky int s390_isolate_bp(void) 2036b73044bSMartin Schwidefsky { 2046b73044bSMartin Schwidefsky if (!test_facility(82)) 2056b73044bSMartin Schwidefsky return -EOPNOTSUPP; 2066b73044bSMartin Schwidefsky set_thread_flag(TIF_ISOLATE_BP); 2076b73044bSMartin Schwidefsky return 0; 2086b73044bSMartin Schwidefsky } 2096b73044bSMartin Schwidefsky EXPORT_SYMBOL(s390_isolate_bp); 2106b73044bSMartin Schwidefsky 2116b73044bSMartin Schwidefsky int s390_isolate_bp_guest(void) 2126b73044bSMartin Schwidefsky { 2136b73044bSMartin Schwidefsky if (!test_facility(82)) 2146b73044bSMartin Schwidefsky return -EOPNOTSUPP; 2156b73044bSMartin Schwidefsky set_thread_flag(TIF_ISOLATE_BP_GUEST); 2166b73044bSMartin Schwidefsky return 0; 2176b73044bSMartin Schwidefsky } 2186b73044bSMartin Schwidefsky EXPORT_SYMBOL(s390_isolate_bp_guest); 219