xref: /openbmc/linux/arch/s390/kernel/processor.c (revision a8fd6168)
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