blob: 43fffa3b099dda5bf3b6cbc43e88b235e338c8b8 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * drivers/s390/sysinfo.c
3 *
4 * Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
5 * Author(s): Ulrich Weigand (Ulrich.Weigand@de.ibm.com)
6 */
7
Linus Torvalds1da177e2005-04-16 15:20:36 -07008#include <linux/kernel.h>
9#include <linux/mm.h>
10#include <linux/proc_fs.h>
11#include <linux/init.h>
Martin Schwidefsky31ee4b22007-02-05 21:18:31 +010012#include <linux/delay.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070013#include <asm/ebcdic.h>
Christian Borntraegeraa24f7f2008-04-17 07:46:09 +020014#include <asm/sysinfo.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070015
Martin Schwidefsky31ee4b22007-02-05 21:18:31 +010016/* Sigh, math-emu. Don't ask. */
17#include <asm/sfp-util.h>
18#include <math-emu/soft-fp.h>
19#include <math-emu/single.h>
20
Martin Schwidefsky0fee6442006-09-20 15:59:10 +020021static inline int stsi_0(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -070022{
23 int rc = stsi (NULL, 0, 0, 0);
Martin Schwidefsky0fee6442006-09-20 15:59:10 +020024 return rc == -ENOSYS ? rc : (((unsigned int) rc) >> 28);
Linus Torvalds1da177e2005-04-16 15:20:36 -070025}
26
Martin Schwidefsky0fee6442006-09-20 15:59:10 +020027static int stsi_1_1_1(struct sysinfo_1_1_1 *info, char *page, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -070028{
Martin Schwidefsky0fee6442006-09-20 15:59:10 +020029 if (stsi(info, 1, 1, 1) == -ENOSYS)
30 return len;
31
32 EBCASC(info->manufacturer, sizeof(info->manufacturer));
33 EBCASC(info->type, sizeof(info->type));
34 EBCASC(info->model, sizeof(info->model));
35 EBCASC(info->sequence, sizeof(info->sequence));
36 EBCASC(info->plant, sizeof(info->plant));
37 EBCASC(info->model_capacity, sizeof(info->model_capacity));
38 len += sprintf(page + len, "Manufacturer: %-16.16s\n",
39 info->manufacturer);
40 len += sprintf(page + len, "Type: %-4.4s\n",
41 info->type);
42 if (info->model[0] != '\0')
43 /*
44 * Sigh: the model field has been renamed with System z9
45 * to model_capacity and a new model field has been added
46 * after the plant field. To avoid confusing older programs
47 * the "Model:" prints "model_capacity model" or just
48 * "model_capacity" if the model string is empty .
49 */
50 len += sprintf(page + len,
51 "Model: %-16.16s %-16.16s\n",
52 info->model_capacity, info->model);
53 else
54 len += sprintf(page + len, "Model: %-16.16s\n",
55 info->model_capacity);
56 len += sprintf(page + len, "Sequence Code: %-16.16s\n",
57 info->sequence);
58 len += sprintf(page + len, "Plant: %-4.4s\n",
59 info->plant);
60 len += sprintf(page + len, "Model Capacity: %-16.16s\n",
61 info->model_capacity);
62 return len;
63}
64
65#if 0 /* Currently unused */
66static int stsi_1_2_1(struct sysinfo_1_2_1 *info, char *page, int len)
67{
68 if (stsi(info, 1, 2, 1) == -ENOSYS)
69 return len;
70
71 len += sprintf(page + len, "\n");
72 EBCASC(info->sequence, sizeof(info->sequence));
73 EBCASC(info->plant, sizeof(info->plant));
74 len += sprintf(page + len, "Sequence Code of CPU: %-16.16s\n",
75 info->sequence);
76 len += sprintf(page + len, "Plant of CPU: %-16.16s\n",
77 info->plant);
78 return len;
79}
80#endif
81
82static int stsi_1_2_2(struct sysinfo_1_2_2 *info, char *page, int len)
83{
84 struct sysinfo_1_2_2_extension *ext;
85 int i;
86
87 if (stsi(info, 1, 2, 2) == -ENOSYS)
88 return len;
89 ext = (struct sysinfo_1_2_2_extension *)
90 ((unsigned long) info + info->acc_offset);
91
92 len += sprintf(page + len, "\n");
93 len += sprintf(page + len, "CPUs Total: %d\n",
94 info->cpus_total);
95 len += sprintf(page + len, "CPUs Configured: %d\n",
96 info->cpus_configured);
97 len += sprintf(page + len, "CPUs Standby: %d\n",
98 info->cpus_standby);
99 len += sprintf(page + len, "CPUs Reserved: %d\n",
100 info->cpus_reserved);
101
102 if (info->format == 1) {
103 /*
104 * Sigh 2. According to the specification the alternate
105 * capability field is a 32 bit floating point number
106 * if the higher order 8 bits are not zero. Printing
107 * a floating point number in the kernel is a no-no,
108 * always print the number as 32 bit unsigned integer.
Martin Schwidefsky31ee4b22007-02-05 21:18:31 +0100109 * The user-space needs to know about the strange
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200110 * encoding of the alternate cpu capability.
111 */
112 len += sprintf(page + len, "Capability: %u %u\n",
113 info->capability, ext->alt_capability);
114 for (i = 2; i <= info->cpus_total; i++)
115 len += sprintf(page + len,
116 "Adjustment %02d-way: %u %u\n",
117 i, info->adjustment[i-2],
118 ext->alt_adjustment[i-2]);
119
120 } else {
121 len += sprintf(page + len, "Capability: %u\n",
122 info->capability);
123 for (i = 2; i <= info->cpus_total; i++)
124 len += sprintf(page + len,
125 "Adjustment %02d-way: %u\n",
126 i, info->adjustment[i-2]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 }
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200128
129 if (info->secondary_capability != 0)
130 len += sprintf(page + len, "Secondary Capability: %d\n",
131 info->secondary_capability);
132
133 return len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134}
135
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200136#if 0 /* Currently unused */
137static int stsi_2_2_1(struct sysinfo_2_2_1 *info, char *page, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138{
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200139 if (stsi(info, 2, 2, 1) == -ENOSYS)
140 return len;
141
142 len += sprintf(page + len, "\n");
143 EBCASC (info->sequence, sizeof(info->sequence));
144 EBCASC (info->plant, sizeof(info->plant));
145 len += sprintf(page + len, "Sequence Code of logical CPU: %-16.16s\n",
146 info->sequence);
147 len += sprintf(page + len, "Plant of logical CPU: %-16.16s\n",
148 info->plant);
149 return len;
150}
151#endif
152
153static int stsi_2_2_2(struct sysinfo_2_2_2 *info, char *page, int len)
154{
155 if (stsi(info, 2, 2, 2) == -ENOSYS)
156 return len;
157
158 EBCASC (info->name, sizeof(info->name));
159
160 len += sprintf(page + len, "\n");
161 len += sprintf(page + len, "LPAR Number: %d\n",
162 info->lpar_number);
163
164 len += sprintf(page + len, "LPAR Characteristics: ");
165 if (info->characteristics & LPAR_CHAR_DEDICATED)
166 len += sprintf(page + len, "Dedicated ");
167 if (info->characteristics & LPAR_CHAR_SHARED)
168 len += sprintf(page + len, "Shared ");
169 if (info->characteristics & LPAR_CHAR_LIMITED)
170 len += sprintf(page + len, "Limited ");
171 len += sprintf(page + len, "\n");
172
173 len += sprintf(page + len, "LPAR Name: %-8.8s\n",
174 info->name);
175
176 len += sprintf(page + len, "LPAR Adjustment: %d\n",
177 info->caf);
178
179 len += sprintf(page + len, "LPAR CPUs Total: %d\n",
180 info->cpus_total);
181 len += sprintf(page + len, "LPAR CPUs Configured: %d\n",
182 info->cpus_configured);
183 len += sprintf(page + len, "LPAR CPUs Standby: %d\n",
184 info->cpus_standby);
185 len += sprintf(page + len, "LPAR CPUs Reserved: %d\n",
186 info->cpus_reserved);
187 len += sprintf(page + len, "LPAR CPUs Dedicated: %d\n",
188 info->cpus_dedicated);
189 len += sprintf(page + len, "LPAR CPUs Shared: %d\n",
190 info->cpus_shared);
191 return len;
192}
193
194static int stsi_3_2_2(struct sysinfo_3_2_2 *info, char *page, int len)
195{
196 int i;
197
198 if (stsi(info, 3, 2, 2) == -ENOSYS)
199 return len;
200 for (i = 0; i < info->count; i++) {
201 EBCASC (info->vm[i].name, sizeof(info->vm[i].name));
202 EBCASC (info->vm[i].cpi, sizeof(info->vm[i].cpi));
203 len += sprintf(page + len, "\n");
204 len += sprintf(page + len, "VM%02d Name: %-8.8s\n",
205 i, info->vm[i].name);
206 len += sprintf(page + len, "VM%02d Control Program: %-16.16s\n",
207 i, info->vm[i].cpi);
208
209 len += sprintf(page + len, "VM%02d Adjustment: %d\n",
210 i, info->vm[i].caf);
211
212 len += sprintf(page + len, "VM%02d CPUs Total: %d\n",
213 i, info->vm[i].cpus_total);
214 len += sprintf(page + len, "VM%02d CPUs Configured: %d\n",
215 i, info->vm[i].cpus_configured);
216 len += sprintf(page + len, "VM%02d CPUs Standby: %d\n",
217 i, info->vm[i].cpus_standby);
218 len += sprintf(page + len, "VM%02d CPUs Reserved: %d\n",
219 i, info->vm[i].cpus_reserved);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 }
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200221 return len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222}
223
224
225static int proc_read_sysinfo(char *page, char **start,
226 off_t off, int count,
227 int *eof, void *data)
228{
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200229 unsigned long info = get_zeroed_page (GFP_KERNEL);
230 int level, len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231
232 if (!info)
233 return 0;
234
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200235 len = 0;
236 level = stsi_0();
237 if (level >= 1)
238 len = stsi_1_1_1((struct sysinfo_1_1_1 *) info, page, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200240 if (level >= 1)
241 len = stsi_1_2_2((struct sysinfo_1_2_2 *) info, page, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200243 if (level >= 2)
244 len = stsi_2_2_2((struct sysinfo_2_2_2 *) info, page, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200246 if (level >= 3)
247 len = stsi_3_2_2((struct sysinfo_3_2_2 *) info, page, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200249 free_page (info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250 return len;
251}
252
253static __init int create_proc_sysinfo(void)
254{
Martin Schwidefsky0fee6442006-09-20 15:59:10 +0200255 create_proc_read_entry("sysinfo", 0444, NULL,
256 proc_read_sysinfo, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257 return 0;
258}
259
260__initcall(create_proc_sysinfo);
261
Heiko Carstens2fc2d1e2007-04-27 16:01:56 +0200262int get_cpu_capability(unsigned int *capability)
263{
264 struct sysinfo_1_2_2 *info;
265 int rc;
266
267 info = (void *) get_zeroed_page(GFP_KERNEL);
268 if (!info)
269 return -ENOMEM;
270 rc = stsi(info, 1, 2, 2);
271 if (rc == -ENOSYS)
272 goto out;
273 rc = 0;
274 *capability = info->capability;
275out:
276 free_page((unsigned long) info);
277 return rc;
278}
279
Martin Schwidefsky31ee4b22007-02-05 21:18:31 +0100280/*
281 * CPU capability might have changed. Therefore recalculate loops_per_jiffy.
282 */
283void s390_adjust_jiffies(void)
284{
285 struct sysinfo_1_2_2 *info;
286 const unsigned int fmil = 0x4b189680; /* 1e7 as 32-bit float. */
287 FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
288 FP_DECL_EX;
289 unsigned int capability;
290
291 info = (void *) get_zeroed_page(GFP_KERNEL);
292 if (!info)
293 return;
294
295 if (stsi(info, 1, 2, 2) != -ENOSYS) {
296 /*
297 * Major sigh. The cpu capability encoding is "special".
298 * If the first 9 bits of info->capability are 0 then it
299 * is a 32 bit unsigned integer in the range 0 .. 2^23.
300 * If the first 9 bits are != 0 then it is a 32 bit float.
301 * In addition a lower value indicates a proportionally
302 * higher cpu capacity. Bogomips are the other way round.
303 * To get to a halfway suitable number we divide 1e7
304 * by the cpu capability number. Yes, that means a floating
305 * point division .. math-emu here we come :-)
306 */
307 FP_UNPACK_SP(SA, &fmil);
308 if ((info->capability >> 23) == 0)
309 FP_FROM_INT_S(SB, info->capability, 32, int);
310 else
311 FP_UNPACK_SP(SB, &info->capability);
312 FP_DIV_S(SR, SA, SB);
313 FP_TO_INT_S(capability, SR, 32, 0);
314 } else
315 /*
316 * Really old machine without stsi block for basic
317 * cpu information. Report 42.0 bogomips.
318 */
319 capability = 42;
320 loops_per_jiffy = capability * (500000/HZ);
321 free_page((unsigned long) info);
322}
323
324/*
325 * calibrate the delay loop
326 */
Adrian Bunk6c81c322008-02-06 01:37:51 -0800327void __cpuinit calibrate_delay(void)
Martin Schwidefsky31ee4b22007-02-05 21:18:31 +0100328{
329 s390_adjust_jiffies();
330 /* Print the good old Bogomips line .. */
331 printk(KERN_DEBUG "Calibrating delay loop (skipped)... "
332 "%lu.%02lu BogoMIPS preset\n", loops_per_jiffy/(500000/HZ),
333 (loops_per_jiffy/(5000/HZ)) % 100);
334}