at v2.6.23 432 lines 12 kB view raw
1/* 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 8#include <linux/kernel.h> 9#include <linux/mm.h> 10#include <linux/proc_fs.h> 11#include <linux/init.h> 12#include <linux/delay.h> 13#include <asm/ebcdic.h> 14 15/* Sigh, math-emu. Don't ask. */ 16#include <asm/sfp-util.h> 17#include <math-emu/soft-fp.h> 18#include <math-emu/single.h> 19 20struct sysinfo_1_1_1 { 21 char reserved_0[32]; 22 char manufacturer[16]; 23 char type[4]; 24 char reserved_1[12]; 25 char model_capacity[16]; 26 char sequence[16]; 27 char plant[4]; 28 char model[16]; 29}; 30 31struct sysinfo_1_2_1 { 32 char reserved_0[80]; 33 char sequence[16]; 34 char plant[4]; 35 char reserved_1[2]; 36 unsigned short cpu_address; 37}; 38 39struct sysinfo_1_2_2 { 40 char format; 41 char reserved_0[1]; 42 unsigned short acc_offset; 43 char reserved_1[24]; 44 unsigned int secondary_capability; 45 unsigned int capability; 46 unsigned short cpus_total; 47 unsigned short cpus_configured; 48 unsigned short cpus_standby; 49 unsigned short cpus_reserved; 50 unsigned short adjustment[0]; 51}; 52 53struct sysinfo_1_2_2_extension { 54 unsigned int alt_capability; 55 unsigned short alt_adjustment[0]; 56}; 57 58struct sysinfo_2_2_1 { 59 char reserved_0[80]; 60 char sequence[16]; 61 char plant[4]; 62 unsigned short cpu_id; 63 unsigned short cpu_address; 64}; 65 66struct sysinfo_2_2_2 { 67 char reserved_0[32]; 68 unsigned short lpar_number; 69 char reserved_1; 70 unsigned char characteristics; 71 unsigned short cpus_total; 72 unsigned short cpus_configured; 73 unsigned short cpus_standby; 74 unsigned short cpus_reserved; 75 char name[8]; 76 unsigned int caf; 77 char reserved_2[16]; 78 unsigned short cpus_dedicated; 79 unsigned short cpus_shared; 80}; 81 82#define LPAR_CHAR_DEDICATED (1 << 7) 83#define LPAR_CHAR_SHARED (1 << 6) 84#define LPAR_CHAR_LIMITED (1 << 5) 85 86struct sysinfo_3_2_2 { 87 char reserved_0[31]; 88 unsigned char count; 89 struct { 90 char reserved_0[4]; 91 unsigned short cpus_total; 92 unsigned short cpus_configured; 93 unsigned short cpus_standby; 94 unsigned short cpus_reserved; 95 char name[8]; 96 unsigned int caf; 97 char cpi[16]; 98 char reserved_1[24]; 99 100 } vm[8]; 101}; 102 103static inline int stsi(void *sysinfo, int fc, int sel1, int sel2) 104{ 105 register int r0 asm("0") = (fc << 28) | sel1; 106 register int r1 asm("1") = sel2; 107 108 asm volatile( 109 " stsi 0(%2)\n" 110 "0: jz 2f\n" 111 "1: lhi %0,%3\n" 112 "2:\n" 113 EX_TABLE(0b,1b) 114 : "+d" (r0) : "d" (r1), "a" (sysinfo), "K" (-ENOSYS) 115 : "cc", "memory" ); 116 return r0; 117} 118 119static inline int stsi_0(void) 120{ 121 int rc = stsi (NULL, 0, 0, 0); 122 return rc == -ENOSYS ? rc : (((unsigned int) rc) >> 28); 123} 124 125static int stsi_1_1_1(struct sysinfo_1_1_1 *info, char *page, int len) 126{ 127 if (stsi(info, 1, 1, 1) == -ENOSYS) 128 return len; 129 130 EBCASC(info->manufacturer, sizeof(info->manufacturer)); 131 EBCASC(info->type, sizeof(info->type)); 132 EBCASC(info->model, sizeof(info->model)); 133 EBCASC(info->sequence, sizeof(info->sequence)); 134 EBCASC(info->plant, sizeof(info->plant)); 135 EBCASC(info->model_capacity, sizeof(info->model_capacity)); 136 len += sprintf(page + len, "Manufacturer: %-16.16s\n", 137 info->manufacturer); 138 len += sprintf(page + len, "Type: %-4.4s\n", 139 info->type); 140 if (info->model[0] != '\0') 141 /* 142 * Sigh: the model field has been renamed with System z9 143 * to model_capacity and a new model field has been added 144 * after the plant field. To avoid confusing older programs 145 * the "Model:" prints "model_capacity model" or just 146 * "model_capacity" if the model string is empty . 147 */ 148 len += sprintf(page + len, 149 "Model: %-16.16s %-16.16s\n", 150 info->model_capacity, info->model); 151 else 152 len += sprintf(page + len, "Model: %-16.16s\n", 153 info->model_capacity); 154 len += sprintf(page + len, "Sequence Code: %-16.16s\n", 155 info->sequence); 156 len += sprintf(page + len, "Plant: %-4.4s\n", 157 info->plant); 158 len += sprintf(page + len, "Model Capacity: %-16.16s\n", 159 info->model_capacity); 160 return len; 161} 162 163#if 0 /* Currently unused */ 164static int stsi_1_2_1(struct sysinfo_1_2_1 *info, char *page, int len) 165{ 166 if (stsi(info, 1, 2, 1) == -ENOSYS) 167 return len; 168 169 len += sprintf(page + len, "\n"); 170 EBCASC(info->sequence, sizeof(info->sequence)); 171 EBCASC(info->plant, sizeof(info->plant)); 172 len += sprintf(page + len, "Sequence Code of CPU: %-16.16s\n", 173 info->sequence); 174 len += sprintf(page + len, "Plant of CPU: %-16.16s\n", 175 info->plant); 176 return len; 177} 178#endif 179 180static int stsi_1_2_2(struct sysinfo_1_2_2 *info, char *page, int len) 181{ 182 struct sysinfo_1_2_2_extension *ext; 183 int i; 184 185 if (stsi(info, 1, 2, 2) == -ENOSYS) 186 return len; 187 ext = (struct sysinfo_1_2_2_extension *) 188 ((unsigned long) info + info->acc_offset); 189 190 len += sprintf(page + len, "\n"); 191 len += sprintf(page + len, "CPUs Total: %d\n", 192 info->cpus_total); 193 len += sprintf(page + len, "CPUs Configured: %d\n", 194 info->cpus_configured); 195 len += sprintf(page + len, "CPUs Standby: %d\n", 196 info->cpus_standby); 197 len += sprintf(page + len, "CPUs Reserved: %d\n", 198 info->cpus_reserved); 199 200 if (info->format == 1) { 201 /* 202 * Sigh 2. According to the specification the alternate 203 * capability field is a 32 bit floating point number 204 * if the higher order 8 bits are not zero. Printing 205 * a floating point number in the kernel is a no-no, 206 * always print the number as 32 bit unsigned integer. 207 * The user-space needs to know about the strange 208 * encoding of the alternate cpu capability. 209 */ 210 len += sprintf(page + len, "Capability: %u %u\n", 211 info->capability, ext->alt_capability); 212 for (i = 2; i <= info->cpus_total; i++) 213 len += sprintf(page + len, 214 "Adjustment %02d-way: %u %u\n", 215 i, info->adjustment[i-2], 216 ext->alt_adjustment[i-2]); 217 218 } else { 219 len += sprintf(page + len, "Capability: %u\n", 220 info->capability); 221 for (i = 2; i <= info->cpus_total; i++) 222 len += sprintf(page + len, 223 "Adjustment %02d-way: %u\n", 224 i, info->adjustment[i-2]); 225 } 226 227 if (info->secondary_capability != 0) 228 len += sprintf(page + len, "Secondary Capability: %d\n", 229 info->secondary_capability); 230 231 return len; 232} 233 234#if 0 /* Currently unused */ 235static int stsi_2_2_1(struct sysinfo_2_2_1 *info, char *page, int len) 236{ 237 if (stsi(info, 2, 2, 1) == -ENOSYS) 238 return len; 239 240 len += sprintf(page + len, "\n"); 241 EBCASC (info->sequence, sizeof(info->sequence)); 242 EBCASC (info->plant, sizeof(info->plant)); 243 len += sprintf(page + len, "Sequence Code of logical CPU: %-16.16s\n", 244 info->sequence); 245 len += sprintf(page + len, "Plant of logical CPU: %-16.16s\n", 246 info->plant); 247 return len; 248} 249#endif 250 251static int stsi_2_2_2(struct sysinfo_2_2_2 *info, char *page, int len) 252{ 253 if (stsi(info, 2, 2, 2) == -ENOSYS) 254 return len; 255 256 EBCASC (info->name, sizeof(info->name)); 257 258 len += sprintf(page + len, "\n"); 259 len += sprintf(page + len, "LPAR Number: %d\n", 260 info->lpar_number); 261 262 len += sprintf(page + len, "LPAR Characteristics: "); 263 if (info->characteristics & LPAR_CHAR_DEDICATED) 264 len += sprintf(page + len, "Dedicated "); 265 if (info->characteristics & LPAR_CHAR_SHARED) 266 len += sprintf(page + len, "Shared "); 267 if (info->characteristics & LPAR_CHAR_LIMITED) 268 len += sprintf(page + len, "Limited "); 269 len += sprintf(page + len, "\n"); 270 271 len += sprintf(page + len, "LPAR Name: %-8.8s\n", 272 info->name); 273 274 len += sprintf(page + len, "LPAR Adjustment: %d\n", 275 info->caf); 276 277 len += sprintf(page + len, "LPAR CPUs Total: %d\n", 278 info->cpus_total); 279 len += sprintf(page + len, "LPAR CPUs Configured: %d\n", 280 info->cpus_configured); 281 len += sprintf(page + len, "LPAR CPUs Standby: %d\n", 282 info->cpus_standby); 283 len += sprintf(page + len, "LPAR CPUs Reserved: %d\n", 284 info->cpus_reserved); 285 len += sprintf(page + len, "LPAR CPUs Dedicated: %d\n", 286 info->cpus_dedicated); 287 len += sprintf(page + len, "LPAR CPUs Shared: %d\n", 288 info->cpus_shared); 289 return len; 290} 291 292static int stsi_3_2_2(struct sysinfo_3_2_2 *info, char *page, int len) 293{ 294 int i; 295 296 if (stsi(info, 3, 2, 2) == -ENOSYS) 297 return len; 298 for (i = 0; i < info->count; i++) { 299 EBCASC (info->vm[i].name, sizeof(info->vm[i].name)); 300 EBCASC (info->vm[i].cpi, sizeof(info->vm[i].cpi)); 301 len += sprintf(page + len, "\n"); 302 len += sprintf(page + len, "VM%02d Name: %-8.8s\n", 303 i, info->vm[i].name); 304 len += sprintf(page + len, "VM%02d Control Program: %-16.16s\n", 305 i, info->vm[i].cpi); 306 307 len += sprintf(page + len, "VM%02d Adjustment: %d\n", 308 i, info->vm[i].caf); 309 310 len += sprintf(page + len, "VM%02d CPUs Total: %d\n", 311 i, info->vm[i].cpus_total); 312 len += sprintf(page + len, "VM%02d CPUs Configured: %d\n", 313 i, info->vm[i].cpus_configured); 314 len += sprintf(page + len, "VM%02d CPUs Standby: %d\n", 315 i, info->vm[i].cpus_standby); 316 len += sprintf(page + len, "VM%02d CPUs Reserved: %d\n", 317 i, info->vm[i].cpus_reserved); 318 } 319 return len; 320} 321 322 323static int proc_read_sysinfo(char *page, char **start, 324 off_t off, int count, 325 int *eof, void *data) 326{ 327 unsigned long info = get_zeroed_page (GFP_KERNEL); 328 int level, len; 329 330 if (!info) 331 return 0; 332 333 len = 0; 334 level = stsi_0(); 335 if (level >= 1) 336 len = stsi_1_1_1((struct sysinfo_1_1_1 *) info, page, len); 337 338 if (level >= 1) 339 len = stsi_1_2_2((struct sysinfo_1_2_2 *) info, page, len); 340 341 if (level >= 2) 342 len = stsi_2_2_2((struct sysinfo_2_2_2 *) info, page, len); 343 344 if (level >= 3) 345 len = stsi_3_2_2((struct sysinfo_3_2_2 *) info, page, len); 346 347 free_page (info); 348 return len; 349} 350 351static __init int create_proc_sysinfo(void) 352{ 353 create_proc_read_entry("sysinfo", 0444, NULL, 354 proc_read_sysinfo, NULL); 355 return 0; 356} 357 358__initcall(create_proc_sysinfo); 359 360int get_cpu_capability(unsigned int *capability) 361{ 362 struct sysinfo_1_2_2 *info; 363 int rc; 364 365 info = (void *) get_zeroed_page(GFP_KERNEL); 366 if (!info) 367 return -ENOMEM; 368 rc = stsi(info, 1, 2, 2); 369 if (rc == -ENOSYS) 370 goto out; 371 rc = 0; 372 *capability = info->capability; 373out: 374 free_page((unsigned long) info); 375 return rc; 376} 377 378/* 379 * CPU capability might have changed. Therefore recalculate loops_per_jiffy. 380 */ 381void s390_adjust_jiffies(void) 382{ 383 struct sysinfo_1_2_2 *info; 384 const unsigned int fmil = 0x4b189680; /* 1e7 as 32-bit float. */ 385 FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR); 386 FP_DECL_EX; 387 unsigned int capability; 388 389 info = (void *) get_zeroed_page(GFP_KERNEL); 390 if (!info) 391 return; 392 393 if (stsi(info, 1, 2, 2) != -ENOSYS) { 394 /* 395 * Major sigh. The cpu capability encoding is "special". 396 * If the first 9 bits of info->capability are 0 then it 397 * is a 32 bit unsigned integer in the range 0 .. 2^23. 398 * If the first 9 bits are != 0 then it is a 32 bit float. 399 * In addition a lower value indicates a proportionally 400 * higher cpu capacity. Bogomips are the other way round. 401 * To get to a halfway suitable number we divide 1e7 402 * by the cpu capability number. Yes, that means a floating 403 * point division .. math-emu here we come :-) 404 */ 405 FP_UNPACK_SP(SA, &fmil); 406 if ((info->capability >> 23) == 0) 407 FP_FROM_INT_S(SB, info->capability, 32, int); 408 else 409 FP_UNPACK_SP(SB, &info->capability); 410 FP_DIV_S(SR, SA, SB); 411 FP_TO_INT_S(capability, SR, 32, 0); 412 } else 413 /* 414 * Really old machine without stsi block for basic 415 * cpu information. Report 42.0 bogomips. 416 */ 417 capability = 42; 418 loops_per_jiffy = capability * (500000/HZ); 419 free_page((unsigned long) info); 420} 421 422/* 423 * calibrate the delay loop 424 */ 425void __init calibrate_delay(void) 426{ 427 s390_adjust_jiffies(); 428 /* Print the good old Bogomips line .. */ 429 printk(KERN_DEBUG "Calibrating delay loop (skipped)... " 430 "%lu.%02lu BogoMIPS preset\n", loops_per_jiffy/(500000/HZ), 431 (loops_per_jiffy/(5000/HZ)) % 100); 432}