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 164static int stsi_1_2_2(struct sysinfo_1_2_2 *info, char *page, int len) 165{ 166 struct sysinfo_1_2_2_extension *ext; 167 int i; 168 169 if (stsi(info, 1, 2, 2) == -ENOSYS) 170 return len; 171 ext = (struct sysinfo_1_2_2_extension *) 172 ((unsigned long) info + info->acc_offset); 173 174 len += sprintf(page + len, "\n"); 175 len += sprintf(page + len, "CPUs Total: %d\n", 176 info->cpus_total); 177 len += sprintf(page + len, "CPUs Configured: %d\n", 178 info->cpus_configured); 179 len += sprintf(page + len, "CPUs Standby: %d\n", 180 info->cpus_standby); 181 len += sprintf(page + len, "CPUs Reserved: %d\n", 182 info->cpus_reserved); 183 184 if (info->format == 1) { 185 /* 186 * Sigh 2. According to the specification the alternate 187 * capability field is a 32 bit floating point number 188 * if the higher order 8 bits are not zero. Printing 189 * a floating point number in the kernel is a no-no, 190 * always print the number as 32 bit unsigned integer. 191 * The user-space needs to know about the strange 192 * encoding of the alternate cpu capability. 193 */ 194 len += sprintf(page + len, "Capability: %u %u\n", 195 info->capability, ext->alt_capability); 196 for (i = 2; i <= info->cpus_total; i++) 197 len += sprintf(page + len, 198 "Adjustment %02d-way: %u %u\n", 199 i, info->adjustment[i-2], 200 ext->alt_adjustment[i-2]); 201 202 } else { 203 len += sprintf(page + len, "Capability: %u\n", 204 info->capability); 205 for (i = 2; i <= info->cpus_total; i++) 206 len += sprintf(page + len, 207 "Adjustment %02d-way: %u\n", 208 i, info->adjustment[i-2]); 209 } 210 211 if (info->secondary_capability != 0) 212 len += sprintf(page + len, "Secondary Capability: %d\n", 213 info->secondary_capability); 214 215 return len; 216} 217 218 219static int stsi_2_2_2(struct sysinfo_2_2_2 *info, char *page, int len) 220{ 221 if (stsi(info, 2, 2, 2) == -ENOSYS) 222 return len; 223 224 EBCASC (info->name, sizeof(info->name)); 225 226 len += sprintf(page + len, "\n"); 227 len += sprintf(page + len, "LPAR Number: %d\n", 228 info->lpar_number); 229 230 len += sprintf(page + len, "LPAR Characteristics: "); 231 if (info->characteristics & LPAR_CHAR_DEDICATED) 232 len += sprintf(page + len, "Dedicated "); 233 if (info->characteristics & LPAR_CHAR_SHARED) 234 len += sprintf(page + len, "Shared "); 235 if (info->characteristics & LPAR_CHAR_LIMITED) 236 len += sprintf(page + len, "Limited "); 237 len += sprintf(page + len, "\n"); 238 239 len += sprintf(page + len, "LPAR Name: %-8.8s\n", 240 info->name); 241 242 len += sprintf(page + len, "LPAR Adjustment: %d\n", 243 info->caf); 244 245 len += sprintf(page + len, "LPAR CPUs Total: %d\n", 246 info->cpus_total); 247 len += sprintf(page + len, "LPAR CPUs Configured: %d\n", 248 info->cpus_configured); 249 len += sprintf(page + len, "LPAR CPUs Standby: %d\n", 250 info->cpus_standby); 251 len += sprintf(page + len, "LPAR CPUs Reserved: %d\n", 252 info->cpus_reserved); 253 len += sprintf(page + len, "LPAR CPUs Dedicated: %d\n", 254 info->cpus_dedicated); 255 len += sprintf(page + len, "LPAR CPUs Shared: %d\n", 256 info->cpus_shared); 257 return len; 258} 259 260static int stsi_3_2_2(struct sysinfo_3_2_2 *info, char *page, int len) 261{ 262 int i; 263 264 if (stsi(info, 3, 2, 2) == -ENOSYS) 265 return len; 266 for (i = 0; i < info->count; i++) { 267 EBCASC (info->vm[i].name, sizeof(info->vm[i].name)); 268 EBCASC (info->vm[i].cpi, sizeof(info->vm[i].cpi)); 269 len += sprintf(page + len, "\n"); 270 len += sprintf(page + len, "VM%02d Name: %-8.8s\n", 271 i, info->vm[i].name); 272 len += sprintf(page + len, "VM%02d Control Program: %-16.16s\n", 273 i, info->vm[i].cpi); 274 275 len += sprintf(page + len, "VM%02d Adjustment: %d\n", 276 i, info->vm[i].caf); 277 278 len += sprintf(page + len, "VM%02d CPUs Total: %d\n", 279 i, info->vm[i].cpus_total); 280 len += sprintf(page + len, "VM%02d CPUs Configured: %d\n", 281 i, info->vm[i].cpus_configured); 282 len += sprintf(page + len, "VM%02d CPUs Standby: %d\n", 283 i, info->vm[i].cpus_standby); 284 len += sprintf(page + len, "VM%02d CPUs Reserved: %d\n", 285 i, info->vm[i].cpus_reserved); 286 } 287 return len; 288} 289 290 291static int proc_read_sysinfo(char *page, char **start, 292 off_t off, int count, 293 int *eof, void *data) 294{ 295 unsigned long info = get_zeroed_page (GFP_KERNEL); 296 int level, len; 297 298 if (!info) 299 return 0; 300 301 len = 0; 302 level = stsi_0(); 303 if (level >= 1) 304 len = stsi_1_1_1((struct sysinfo_1_1_1 *) info, page, len); 305 306 if (level >= 1) 307 len = stsi_1_2_2((struct sysinfo_1_2_2 *) info, page, len); 308 309 if (level >= 2) 310 len = stsi_2_2_2((struct sysinfo_2_2_2 *) info, page, len); 311 312 if (level >= 3) 313 len = stsi_3_2_2((struct sysinfo_3_2_2 *) info, page, len); 314 315 free_page (info); 316 return len; 317} 318 319static __init int create_proc_sysinfo(void) 320{ 321 create_proc_read_entry("sysinfo", 0444, NULL, 322 proc_read_sysinfo, NULL); 323 return 0; 324} 325 326__initcall(create_proc_sysinfo); 327 328int get_cpu_capability(unsigned int *capability) 329{ 330 struct sysinfo_1_2_2 *info; 331 int rc; 332 333 info = (void *) get_zeroed_page(GFP_KERNEL); 334 if (!info) 335 return -ENOMEM; 336 rc = stsi(info, 1, 2, 2); 337 if (rc == -ENOSYS) 338 goto out; 339 rc = 0; 340 *capability = info->capability; 341out: 342 free_page((unsigned long) info); 343 return rc; 344} 345 346/* 347 * CPU capability might have changed. Therefore recalculate loops_per_jiffy. 348 */ 349void s390_adjust_jiffies(void) 350{ 351 struct sysinfo_1_2_2 *info; 352 const unsigned int fmil = 0x4b189680; /* 1e7 as 32-bit float. */ 353 FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR); 354 FP_DECL_EX; 355 unsigned int capability; 356 357 info = (void *) get_zeroed_page(GFP_KERNEL); 358 if (!info) 359 return; 360 361 if (stsi(info, 1, 2, 2) != -ENOSYS) { 362 /* 363 * Major sigh. The cpu capability encoding is "special". 364 * If the first 9 bits of info->capability are 0 then it 365 * is a 32 bit unsigned integer in the range 0 .. 2^23. 366 * If the first 9 bits are != 0 then it is a 32 bit float. 367 * In addition a lower value indicates a proportionally 368 * higher cpu capacity. Bogomips are the other way round. 369 * To get to a halfway suitable number we divide 1e7 370 * by the cpu capability number. Yes, that means a floating 371 * point division .. math-emu here we come :-) 372 */ 373 FP_UNPACK_SP(SA, &fmil); 374 if ((info->capability >> 23) == 0) 375 FP_FROM_INT_S(SB, info->capability, 32, int); 376 else 377 FP_UNPACK_SP(SB, &info->capability); 378 FP_DIV_S(SR, SA, SB); 379 FP_TO_INT_S(capability, SR, 32, 0); 380 } else 381 /* 382 * Really old machine without stsi block for basic 383 * cpu information. Report 42.0 bogomips. 384 */ 385 capability = 42; 386 loops_per_jiffy = capability * (500000/HZ); 387 free_page((unsigned long) info); 388} 389 390/* 391 * calibrate the delay loop 392 */ 393void __init calibrate_delay(void) 394{ 395 s390_adjust_jiffies(); 396 /* Print the good old Bogomips line .. */ 397 printk(KERN_DEBUG "Calibrating delay loop (skipped)... " 398 "%lu.%02lu BogoMIPS preset\n", loops_per_jiffy/(500000/HZ), 399 (loops_per_jiffy/(5000/HZ)) % 100); 400} 401