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