1395d31d4SMartin Schwidefsky /* 2395d31d4SMartin Schwidefsky * Copyright IBM Corp. 2008 3395d31d4SMartin Schwidefsky * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com) 4395d31d4SMartin Schwidefsky */ 5395d31d4SMartin Schwidefsky 6395d31d4SMartin Schwidefsky #define KMSG_COMPONENT "cpu" 7395d31d4SMartin Schwidefsky #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 8395d31d4SMartin Schwidefsky 9ca21872eSHeiko Carstens #include <linux/cpufeature.h> 10395d31d4SMartin Schwidefsky #include <linux/kernel.h> 11395d31d4SMartin Schwidefsky #include <linux/init.h> 12395d31d4SMartin Schwidefsky #include <linux/seq_file.h> 13395d31d4SMartin Schwidefsky #include <linux/delay.h> 1419726cecSHeiko Carstens #include <linux/cpu.h> 151ec2772eSMartin Schwidefsky #include <asm/diag.h> 16097a116cSHeiko Carstens #include <asm/facility.h> 17395d31d4SMartin Schwidefsky #include <asm/elf.h> 18395d31d4SMartin Schwidefsky #include <asm/lowcore.h> 19395d31d4SMartin Schwidefsky #include <asm/param.h> 204d92f502SHeiko Carstens #include <asm/smp.h> 21395d31d4SMartin Schwidefsky 22097a116cSHeiko Carstens struct cpu_info { 23097a116cSHeiko Carstens unsigned int cpu_mhz_dynamic; 24097a116cSHeiko Carstens unsigned int cpu_mhz_static; 25097a116cSHeiko Carstens struct cpuid cpu_id; 26097a116cSHeiko Carstens }; 27097a116cSHeiko Carstens 28097a116cSHeiko Carstens static DEFINE_PER_CPU(struct cpu_info, cpu_info); 29097a116cSHeiko Carstens 30097a116cSHeiko Carstens static bool machine_has_cpu_mhz; 31097a116cSHeiko Carstens 32097a116cSHeiko Carstens void __init cpu_detect_mhz_feature(void) 33097a116cSHeiko Carstens { 34097a116cSHeiko Carstens if (test_facility(34) && __ecag(ECAG_CPU_ATTRIBUTE, 0) != -1UL) 35097a116cSHeiko Carstens machine_has_cpu_mhz = 1; 36097a116cSHeiko Carstens } 37097a116cSHeiko Carstens 38097a116cSHeiko Carstens static void update_cpu_mhz(void *arg) 39097a116cSHeiko Carstens { 40097a116cSHeiko Carstens unsigned long mhz; 41097a116cSHeiko Carstens struct cpu_info *c; 42097a116cSHeiko Carstens 43097a116cSHeiko Carstens mhz = __ecag(ECAG_CPU_ATTRIBUTE, 0); 44097a116cSHeiko Carstens c = this_cpu_ptr(&cpu_info); 45097a116cSHeiko Carstens c->cpu_mhz_dynamic = mhz >> 32; 46097a116cSHeiko Carstens c->cpu_mhz_static = mhz & 0xffffffff; 47097a116cSHeiko Carstens } 48097a116cSHeiko Carstens 49097a116cSHeiko Carstens void s390_update_cpu_mhz(void) 50097a116cSHeiko Carstens { 51097a116cSHeiko Carstens s390_adjust_jiffies(); 52097a116cSHeiko Carstens if (machine_has_cpu_mhz) 53097a116cSHeiko Carstens on_each_cpu(update_cpu_mhz, NULL, 0); 54097a116cSHeiko Carstens } 5594038a99SMartin Schwidefsky 5679ab11cdSChristian Borntraeger void notrace cpu_relax_yield(void) 574d92f502SHeiko Carstens { 581ec2772eSMartin Schwidefsky if (!smp_cpu_mtid && MACHINE_HAS_DIAG44) { 591ec2772eSMartin Schwidefsky diag_stat_inc(DIAG_STAT_X044); 604d92f502SHeiko Carstens asm volatile("diag 0,0,0x44"); 611ec2772eSMartin Schwidefsky } 624d92f502SHeiko Carstens barrier(); 634d92f502SHeiko Carstens } 6479ab11cdSChristian Borntraeger EXPORT_SYMBOL(cpu_relax_yield); 654d92f502SHeiko Carstens 6694038a99SMartin Schwidefsky /* 6794038a99SMartin Schwidefsky * cpu_init - initializes state that is per-CPU. 6894038a99SMartin Schwidefsky */ 69e2741f17SPaul Gortmaker void cpu_init(void) 7094038a99SMartin Schwidefsky { 71097a116cSHeiko Carstens struct cpuid *id = this_cpu_ptr(&cpu_info.cpu_id); 7294038a99SMartin Schwidefsky 7394038a99SMartin Schwidefsky get_cpu_id(id); 74097a116cSHeiko Carstens if (machine_has_cpu_mhz) 75097a116cSHeiko Carstens update_cpu_mhz(NULL); 7694038a99SMartin Schwidefsky atomic_inc(&init_mm.mm_count); 7794038a99SMartin Schwidefsky current->active_mm = &init_mm; 7894038a99SMartin Schwidefsky BUG_ON(current->mm); 7994038a99SMartin Schwidefsky enter_lazy_tlb(&init_mm, current); 8094038a99SMartin Schwidefsky } 8194038a99SMartin Schwidefsky 8294038a99SMartin Schwidefsky /* 838f00b3e2SHendrik Brueckner * cpu_have_feature - Test CPU features on module initialization 848f00b3e2SHendrik Brueckner */ 858f00b3e2SHendrik Brueckner int cpu_have_feature(unsigned int num) 868f00b3e2SHendrik Brueckner { 878f00b3e2SHendrik Brueckner return elf_hwcap & (1UL << num); 888f00b3e2SHendrik Brueckner } 898f00b3e2SHendrik Brueckner EXPORT_SYMBOL(cpu_have_feature); 908f00b3e2SHendrik Brueckner 91219a21b3SHeiko Carstens static void show_cpu_summary(struct seq_file *m, void *v) 92395d31d4SMartin Schwidefsky { 93fbf3c542SHeiko Carstens static const char *hwcap_str[] = { 94395d31d4SMartin Schwidefsky "esan3", "zarch", "stfle", "msa", "ldisp", "eimm", "dfp", 9580703617SMartin Schwidefsky "edat", "etf3eh", "highgprs", "te", "vx" 96395d31d4SMartin Schwidefsky }; 977f16d7e7SDavid Hildenbrand static const char * const int_hwcap_str[] = { 987f16d7e7SDavid Hildenbrand "sie" 997f16d7e7SDavid Hildenbrand }; 100219a21b3SHeiko Carstens int i, cpu; 101395d31d4SMartin Schwidefsky 102395d31d4SMartin Schwidefsky seq_printf(m, "vendor_id : IBM/S390\n" 103395d31d4SMartin Schwidefsky "# processors : %i\n" 104395d31d4SMartin Schwidefsky "bogomips per cpu: %lu.%02lu\n", 105395d31d4SMartin Schwidefsky num_online_cpus(), loops_per_jiffy/(500000/HZ), 106395d31d4SMartin Schwidefsky (loops_per_jiffy/(5000/HZ))%100); 10710f4954aSHeiko Carstens seq_printf(m, "max thread id : %d\n", smp_cpu_mtid); 108395d31d4SMartin Schwidefsky seq_puts(m, "features\t: "); 109fbf3c542SHeiko Carstens for (i = 0; i < ARRAY_SIZE(hwcap_str); i++) 110395d31d4SMartin Schwidefsky if (hwcap_str[i] && (elf_hwcap & (1UL << i))) 111395d31d4SMartin Schwidefsky seq_printf(m, "%s ", hwcap_str[i]); 1127f16d7e7SDavid Hildenbrand for (i = 0; i < ARRAY_SIZE(int_hwcap_str); i++) 1137f16d7e7SDavid Hildenbrand if (int_hwcap_str[i] && (int_hwcap & (1UL << i))) 1147f16d7e7SDavid Hildenbrand seq_printf(m, "%s ", int_hwcap_str[i]); 115395d31d4SMartin Schwidefsky seq_puts(m, "\n"); 1166668022cSHeiko Carstens show_cacheinfo(m); 117219a21b3SHeiko Carstens for_each_online_cpu(cpu) { 118097a116cSHeiko Carstens struct cpuid *id = &per_cpu(cpu_info.cpu_id, cpu); 119219a21b3SHeiko Carstens 120219a21b3SHeiko Carstens seq_printf(m, "processor %d: " 121395d31d4SMartin Schwidefsky "version = %02X, " 122395d31d4SMartin Schwidefsky "identification = %06X, " 123395d31d4SMartin Schwidefsky "machine = %04X\n", 124219a21b3SHeiko Carstens cpu, id->version, id->ident, id->machine); 125395d31d4SMartin Schwidefsky } 126219a21b3SHeiko Carstens } 127219a21b3SHeiko Carstens 128097a116cSHeiko Carstens static void show_cpu_mhz(struct seq_file *m, unsigned long n) 129097a116cSHeiko Carstens { 130097a116cSHeiko Carstens struct cpu_info *c = per_cpu_ptr(&cpu_info, n); 131097a116cSHeiko Carstens 132097a116cSHeiko Carstens seq_printf(m, "cpu MHz dynamic : %d\n", c->cpu_mhz_dynamic); 133097a116cSHeiko Carstens seq_printf(m, "cpu MHz static : %d\n", c->cpu_mhz_static); 134097a116cSHeiko Carstens } 135097a116cSHeiko Carstens 136219a21b3SHeiko Carstens /* 137219a21b3SHeiko Carstens * show_cpuinfo - Get information on one CPU for use by procfs. 138219a21b3SHeiko Carstens */ 139219a21b3SHeiko Carstens static int show_cpuinfo(struct seq_file *m, void *v) 140219a21b3SHeiko Carstens { 141219a21b3SHeiko Carstens unsigned long n = (unsigned long) v - 1; 142219a21b3SHeiko Carstens 143219a21b3SHeiko Carstens if (!n) 144219a21b3SHeiko Carstens show_cpu_summary(m, v); 145097a116cSHeiko Carstens if (!machine_has_cpu_mhz) 146097a116cSHeiko Carstens return 0; 147109ab954SHeiko Carstens seq_printf(m, "\ncpu number : %ld\n", n); 148097a116cSHeiko Carstens show_cpu_mhz(m, n); 149395d31d4SMartin Schwidefsky return 0; 150395d31d4SMartin Schwidefsky } 151395d31d4SMartin Schwidefsky 152281eaa8cSHeiko Carstens static inline void *c_update(loff_t *pos) 153281eaa8cSHeiko Carstens { 154281eaa8cSHeiko Carstens if (*pos) 155281eaa8cSHeiko Carstens *pos = cpumask_next(*pos - 1, cpu_online_mask); 156281eaa8cSHeiko Carstens return *pos < nr_cpu_ids ? (void *)*pos + 1 : NULL; 157281eaa8cSHeiko Carstens } 158281eaa8cSHeiko Carstens 159395d31d4SMartin Schwidefsky static void *c_start(struct seq_file *m, loff_t *pos) 160395d31d4SMartin Schwidefsky { 161281eaa8cSHeiko Carstens get_online_cpus(); 162281eaa8cSHeiko Carstens return c_update(pos); 163395d31d4SMartin Schwidefsky } 164395d31d4SMartin Schwidefsky 165395d31d4SMartin Schwidefsky static void *c_next(struct seq_file *m, void *v, loff_t *pos) 166395d31d4SMartin Schwidefsky { 167395d31d4SMartin Schwidefsky ++*pos; 168281eaa8cSHeiko Carstens return c_update(pos); 169395d31d4SMartin Schwidefsky } 170395d31d4SMartin Schwidefsky 171395d31d4SMartin Schwidefsky static void c_stop(struct seq_file *m, void *v) 172395d31d4SMartin Schwidefsky { 173281eaa8cSHeiko Carstens put_online_cpus(); 174395d31d4SMartin Schwidefsky } 175395d31d4SMartin Schwidefsky 176395d31d4SMartin Schwidefsky const struct seq_operations cpuinfo_op = { 177395d31d4SMartin Schwidefsky .start = c_start, 178395d31d4SMartin Schwidefsky .next = c_next, 179395d31d4SMartin Schwidefsky .stop = c_stop, 180395d31d4SMartin Schwidefsky .show = show_cpuinfo, 181395d31d4SMartin Schwidefsky }; 182