blob: ca1884ed5a06a169ce97f3ca7f30ea8716a2e60b [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * palinfo.c
3 *
4 * Prints processor specific information reported by PAL.
5 * This code is based on specification of PAL as of the
6 * Intel IA-64 Architecture Software Developer's Manual v1.0.
7 *
8 *
9 * Copyright (C) 2000-2001, 2003 Hewlett-Packard Co
10 * Stephane Eranian <eranian@hpl.hp.com>
11 * Copyright (C) 2004 Intel Corporation
12 * Ashok Raj <ashok.raj@intel.com>
13 *
14 * 05/26/2000 S.Eranian initial release
15 * 08/21/2000 S.Eranian updated to July 2000 PAL specs
16 * 02/05/2001 S.Eranian fixed module support
17 * 10/23/2001 S.Eranian updated pal_perf_mon_info bug fixes
18 * 03/24/2004 Ashok Raj updated to work with CPU Hotplug
Russ Anderson895309f2006-12-07 11:06:35 -080019 * 10/26/2006 Russ Anderson updated processor features to rev 2.2 spec
Linus Torvalds1da177e2005-04-16 15:20:36 -070020 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070021#include <linux/types.h>
22#include <linux/errno.h>
23#include <linux/init.h>
24#include <linux/proc_fs.h>
25#include <linux/mm.h>
26#include <linux/module.h>
27#include <linux/efi.h>
28#include <linux/notifier.h>
29#include <linux/cpu.h>
30#include <linux/cpumask.h>
31
32#include <asm/pal.h>
33#include <asm/sal.h>
34#include <asm/page.h>
35#include <asm/processor.h>
36#include <linux/smp.h>
37
38MODULE_AUTHOR("Stephane Eranian <eranian@hpl.hp.com>");
39MODULE_DESCRIPTION("/proc interface to IA-64 PAL");
40MODULE_LICENSE("GPL");
41
42#define PALINFO_VERSION "0.5"
43
44typedef int (*palinfo_func_t)(char*);
45
46typedef struct {
47 const char *name; /* name of the proc entry */
48 palinfo_func_t proc_read; /* function to call for reading */
49 struct proc_dir_entry *entry; /* registered entry (removal) */
50} palinfo_entry_t;
51
52
53/*
54 * A bunch of string array to get pretty printing
55 */
56
57static char *cache_types[] = {
58 "", /* not used */
59 "Instruction",
60 "Data",
61 "Data/Instruction" /* unified */
62};
63
64static const char *cache_mattrib[]={
65 "WriteThrough",
66 "WriteBack",
67 "", /* reserved */
68 "" /* reserved */
69};
70
71static const char *cache_st_hints[]={
72 "Temporal, level 1",
73 "Reserved",
74 "Reserved",
75 "Non-temporal, all levels",
76 "Reserved",
77 "Reserved",
78 "Reserved",
79 "Reserved"
80};
81
82static const char *cache_ld_hints[]={
83 "Temporal, level 1",
84 "Non-temporal, level 1",
85 "Reserved",
86 "Non-temporal, all levels",
87 "Reserved",
88 "Reserved",
89 "Reserved",
90 "Reserved"
91};
92
93static const char *rse_hints[]={
94 "enforced lazy",
95 "eager stores",
96 "eager loads",
97 "eager loads and stores"
98};
99
100#define RSE_HINTS_COUNT ARRAY_SIZE(rse_hints)
101
102static const char *mem_attrib[]={
103 "WB", /* 000 */
104 "SW", /* 001 */
105 "010", /* 010 */
106 "011", /* 011 */
107 "UC", /* 100 */
108 "UCE", /* 101 */
109 "WC", /* 110 */
110 "NaTPage" /* 111 */
111};
112
113/*
114 * Take a 64bit vector and produces a string such that
115 * if bit n is set then 2^n in clear text is generated. The adjustment
116 * to the right unit is also done.
117 *
118 * Input:
119 * - a pointer to a buffer to hold the string
120 * - a 64-bit vector
121 * Ouput:
122 * - a pointer to the end of the buffer
123 *
124 */
125static char *
126bitvector_process(char *p, u64 vector)
127{
128 int i,j;
129 const char *units[]={ "", "K", "M", "G", "T" };
130
131 for (i=0, j=0; i < 64; i++ , j=i/10) {
132 if (vector & 0x1) {
133 p += sprintf(p, "%d%s ", 1 << (i-j*10), units[j]);
134 }
135 vector >>= 1;
136 }
137 return p;
138}
139
140/*
141 * Take a 64bit vector and produces a string such that
142 * if bit n is set then register n is present. The function
143 * takes into account consecutive registers and prints out ranges.
144 *
145 * Input:
146 * - a pointer to a buffer to hold the string
147 * - a 64-bit vector
148 * Ouput:
149 * - a pointer to the end of the buffer
150 *
151 */
152static char *
153bitregister_process(char *p, u64 *reg_info, int max)
154{
155 int i, begin, skip = 0;
156 u64 value = reg_info[0];
157
158 value >>= i = begin = ffs(value) - 1;
159
160 for(; i < max; i++ ) {
161
162 if (i != 0 && (i%64) == 0) value = *++reg_info;
163
164 if ((value & 0x1) == 0 && skip == 0) {
165 if (begin <= i - 2)
166 p += sprintf(p, "%d-%d ", begin, i-1);
167 else
168 p += sprintf(p, "%d ", i-1);
169 skip = 1;
170 begin = -1;
171 } else if ((value & 0x1) && skip == 1) {
172 skip = 0;
173 begin = i;
174 }
175 value >>=1;
176 }
177 if (begin > -1) {
178 if (begin < 127)
179 p += sprintf(p, "%d-127", begin);
180 else
181 p += sprintf(p, "127");
182 }
183
184 return p;
185}
186
187static int
188power_info(char *page)
189{
190 s64 status;
191 char *p = page;
192 u64 halt_info_buffer[8];
193 pal_power_mgmt_info_u_t *halt_info =(pal_power_mgmt_info_u_t *)halt_info_buffer;
194 int i;
195
196 status = ia64_pal_halt_info(halt_info);
197 if (status != 0) return 0;
198
199 for (i=0; i < 8 ; i++ ) {
200 if (halt_info[i].pal_power_mgmt_info_s.im == 1) {
201 p += sprintf(p, "Power level %d:\n"
202 "\tentry_latency : %d cycles\n"
203 "\texit_latency : %d cycles\n"
204 "\tpower consumption : %d mW\n"
205 "\tCache+TLB coherency : %s\n", i,
206 halt_info[i].pal_power_mgmt_info_s.entry_latency,
207 halt_info[i].pal_power_mgmt_info_s.exit_latency,
208 halt_info[i].pal_power_mgmt_info_s.power_consumption,
209 halt_info[i].pal_power_mgmt_info_s.co ? "Yes" : "No");
210 } else {
211 p += sprintf(p,"Power level %d: not implemented\n",i);
212 }
213 }
214 return p - page;
215}
216
217static int
218cache_info(char *page)
219{
220 char *p = page;
221 u64 i, levels, unique_caches;
222 pal_cache_config_info_t cci;
223 int j, k;
224 s64 status;
225
226 if ((status = ia64_pal_cache_summary(&levels, &unique_caches)) != 0) {
227 printk(KERN_ERR "ia64_pal_cache_summary=%ld\n", status);
228 return 0;
229 }
230
231 p += sprintf(p, "Cache levels : %ld\nUnique caches : %ld\n\n", levels, unique_caches);
232
233 for (i=0; i < levels; i++) {
234
235 for (j=2; j >0 ; j--) {
236
237 /* even without unification some level may not be present */
238 if ((status=ia64_pal_cache_config_info(i,j, &cci)) != 0) {
239 continue;
240 }
241 p += sprintf(p,
242 "%s Cache level %lu:\n"
Tony Luck2ab93912006-03-31 10:28:29 -0800243 "\tSize : %u bytes\n"
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 "\tAttributes : ",
245 cache_types[j+cci.pcci_unified], i+1,
246 cci.pcci_cache_size);
247
248 if (cci.pcci_unified) p += sprintf(p, "Unified ");
249
250 p += sprintf(p, "%s\n", cache_mattrib[cci.pcci_cache_attr]);
251
252 p += sprintf(p,
253 "\tAssociativity : %d\n"
254 "\tLine size : %d bytes\n"
255 "\tStride : %d bytes\n",
256 cci.pcci_assoc, 1<<cci.pcci_line_size, 1<<cci.pcci_stride);
257 if (j == 1)
258 p += sprintf(p, "\tStore latency : N/A\n");
259 else
260 p += sprintf(p, "\tStore latency : %d cycle(s)\n",
261 cci.pcci_st_latency);
262
263 p += sprintf(p,
264 "\tLoad latency : %d cycle(s)\n"
265 "\tStore hints : ", cci.pcci_ld_latency);
266
267 for(k=0; k < 8; k++ ) {
268 if ( cci.pcci_st_hints & 0x1)
269 p += sprintf(p, "[%s]", cache_st_hints[k]);
270 cci.pcci_st_hints >>=1;
271 }
272 p += sprintf(p, "\n\tLoad hints : ");
273
274 for(k=0; k < 8; k++ ) {
275 if (cci.pcci_ld_hints & 0x1)
276 p += sprintf(p, "[%s]", cache_ld_hints[k]);
277 cci.pcci_ld_hints >>=1;
278 }
279 p += sprintf(p,
280 "\n\tAlias boundary : %d byte(s)\n"
281 "\tTag LSB : %d\n"
282 "\tTag MSB : %d\n",
283 1<<cci.pcci_alias_boundary, cci.pcci_tag_lsb,
284 cci.pcci_tag_msb);
285
286 /* when unified, data(j=2) is enough */
287 if (cci.pcci_unified) break;
288 }
289 }
290 return p - page;
291}
292
293
294static int
295vm_info(char *page)
296{
297 char *p = page;
298 u64 tr_pages =0, vw_pages=0, tc_pages;
299 u64 attrib;
300 pal_vm_info_1_u_t vm_info_1;
301 pal_vm_info_2_u_t vm_info_2;
302 pal_tc_info_u_t tc_info;
303 ia64_ptce_info_t ptce;
304 const char *sep;
305 int i, j;
306 s64 status;
307
308 if ((status = ia64_pal_vm_summary(&vm_info_1, &vm_info_2)) !=0) {
309 printk(KERN_ERR "ia64_pal_vm_summary=%ld\n", status);
Peter Chubb714d2dc2005-08-25 17:39:00 -0700310 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311
Peter Chubb714d2dc2005-08-25 17:39:00 -0700312 p += sprintf(p,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 "Physical Address Space : %d bits\n"
314 "Virtual Address Space : %d bits\n"
315 "Protection Key Registers(PKR) : %d\n"
316 "Implemented bits in PKR.key : %d\n"
317 "Hash Tag ID : 0x%x\n"
318 "Size of RR.rid : %d\n",
319 vm_info_1.pal_vm_info_1_s.phys_add_size,
Peter Chubb714d2dc2005-08-25 17:39:00 -0700320 vm_info_2.pal_vm_info_2_s.impl_va_msb+1,
321 vm_info_1.pal_vm_info_1_s.max_pkr+1,
322 vm_info_1.pal_vm_info_1_s.key_size,
323 vm_info_1.pal_vm_info_1_s.hash_tag_id,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 vm_info_2.pal_vm_info_2_s.rid_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 }
Peter Chubb714d2dc2005-08-25 17:39:00 -0700326
327 if (ia64_pal_mem_attrib(&attrib) == 0) {
328 p += sprintf(p, "Supported memory attributes : ");
329 sep = "";
330 for (i = 0; i < 8; i++) {
331 if (attrib & (1 << i)) {
332 p += sprintf(p, "%s%s", sep, mem_attrib[i]);
333 sep = ", ";
334 }
335 }
336 p += sprintf(p, "\n");
337 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338
339 if ((status = ia64_pal_vm_page_size(&tr_pages, &vw_pages)) !=0) {
340 printk(KERN_ERR "ia64_pal_vm_page_size=%ld\n", status);
Peter Chubb714d2dc2005-08-25 17:39:00 -0700341 } else {
342
343 p += sprintf(p,
344 "\nTLB walker : %simplemented\n"
345 "Number of DTR : %d\n"
346 "Number of ITR : %d\n"
347 "TLB insertable page sizes : ",
348 vm_info_1.pal_vm_info_1_s.vw ? "" : "not ",
349 vm_info_1.pal_vm_info_1_s.max_dtr_entry+1,
350 vm_info_1.pal_vm_info_1_s.max_itr_entry+1);
351
352
353 p = bitvector_process(p, tr_pages);
354
355 p += sprintf(p, "\nTLB purgeable page sizes : ");
356
357 p = bitvector_process(p, vw_pages);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 if ((status=ia64_get_ptce(&ptce)) != 0) {
360 printk(KERN_ERR "ia64_get_ptce=%ld\n", status);
Peter Chubb714d2dc2005-08-25 17:39:00 -0700361 } else {
362 p += sprintf(p,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 "\nPurge base address : 0x%016lx\n"
364 "Purge outer loop count : %d\n"
365 "Purge inner loop count : %d\n"
366 "Purge outer loop stride : %d\n"
367 "Purge inner loop stride : %d\n",
Peter Chubb714d2dc2005-08-25 17:39:00 -0700368 ptce.base, ptce.count[0], ptce.count[1],
369 ptce.stride[0], ptce.stride[1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370
Peter Chubb714d2dc2005-08-25 17:39:00 -0700371 p += sprintf(p,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 "TC Levels : %d\n"
373 "Unique TC(s) : %d\n",
374 vm_info_1.pal_vm_info_1_s.num_tc_levels,
375 vm_info_1.pal_vm_info_1_s.max_unique_tcs);
376
Peter Chubb714d2dc2005-08-25 17:39:00 -0700377 for(i=0; i < vm_info_1.pal_vm_info_1_s.num_tc_levels; i++) {
378 for (j=2; j>0 ; j--) {
379 tc_pages = 0; /* just in case */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380
381
Peter Chubb714d2dc2005-08-25 17:39:00 -0700382 /* even without unification, some levels may not be present */
383 if ((status=ia64_pal_vm_info(i,j, &tc_info, &tc_pages)) != 0) {
384 continue;
385 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386
Peter Chubb714d2dc2005-08-25 17:39:00 -0700387 p += sprintf(p,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 "\n%s Translation Cache Level %d:\n"
389 "\tHash sets : %d\n"
390 "\tAssociativity : %d\n"
391 "\tNumber of entries : %d\n"
392 "\tFlags : ",
Peter Chubb714d2dc2005-08-25 17:39:00 -0700393 cache_types[j+tc_info.tc_unified], i+1,
394 tc_info.tc_num_sets,
395 tc_info.tc_associativity,
396 tc_info.tc_num_entries);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397
Peter Chubb714d2dc2005-08-25 17:39:00 -0700398 if (tc_info.tc_pf)
399 p += sprintf(p, "PreferredPageSizeOptimized ");
400 if (tc_info.tc_unified)
401 p += sprintf(p, "Unified ");
402 if (tc_info.tc_reduce_tr)
403 p += sprintf(p, "TCReduction");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404
Peter Chubb714d2dc2005-08-25 17:39:00 -0700405 p += sprintf(p, "\n\tSupported page sizes: ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406
Peter Chubb714d2dc2005-08-25 17:39:00 -0700407 p = bitvector_process(p, tc_pages);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408
Peter Chubb714d2dc2005-08-25 17:39:00 -0700409 /* when unified date (j=2) is enough */
410 if (tc_info.tc_unified)
411 break;
412 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 }
414 }
415 p += sprintf(p, "\n");
416
417 return p - page;
418}
419
420
421static int
422register_info(char *page)
423{
424 char *p = page;
425 u64 reg_info[2];
426 u64 info;
427 u64 phys_stacked;
428 pal_hints_u_t hints;
429 u64 iregs, dregs;
430 char *info_type[]={
431 "Implemented AR(s)",
432 "AR(s) with read side-effects",
433 "Implemented CR(s)",
434 "CR(s) with read side-effects",
435 };
436
437 for(info=0; info < 4; info++) {
438
439 if (ia64_pal_register_info(info, &reg_info[0], &reg_info[1]) != 0) return 0;
440
441 p += sprintf(p, "%-32s : ", info_type[info]);
442
443 p = bitregister_process(p, reg_info, 128);
444
445 p += sprintf(p, "\n");
446 }
447
Peter Chubb714d2dc2005-08-25 17:39:00 -0700448 if (ia64_pal_rse_info(&phys_stacked, &hints) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449
450 p += sprintf(p,
451 "RSE stacked physical registers : %ld\n"
452 "RSE load/store hints : %ld (%s)\n",
453 phys_stacked, hints.ph_data,
454 hints.ph_data < RSE_HINTS_COUNT ? rse_hints[hints.ph_data]: "(??)");
Peter Chubb714d2dc2005-08-25 17:39:00 -0700455 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456 if (ia64_pal_debug_info(&iregs, &dregs))
457 return 0;
458
459 p += sprintf(p,
460 "Instruction debug register pairs : %ld\n"
461 "Data debug register pairs : %ld\n", iregs, dregs);
462
463 return p - page;
464}
465
466static const char *proc_features[]={
467 NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
468 NULL,NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,
469 NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
470 NULL,NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,
Russ Anderson895309f2006-12-07 11:06:35 -0800471 "Unimplemented instruction address fault",
472 "INIT, PMI, and LINT pins",
473 "Simple unimplemented instr addresses",
474 "Variable P-state performance",
475 "Virtual machine features implemented",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476 "XIP,XPSR,XFS implemented",
477 "XR1-XR3 implemented",
478 "Disable dynamic predicate prediction",
479 "Disable processor physical number",
480 "Disable dynamic data cache prefetch",
481 "Disable dynamic inst cache prefetch",
482 "Disable dynamic branch prediction",
Russ Anderson895309f2006-12-07 11:06:35 -0800483 NULL, NULL, NULL, NULL,
484 "Disable P-states",
485 "Enable MCA on Data Poisoning",
486 "Enable vmsw instruction",
487 "Enable extern environmental notification",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 "Disable BINIT on processor time-out",
489 "Disable dynamic power management (DPM)",
490 "Disable coherency",
491 "Disable cache",
492 "Enable CMCI promotion",
493 "Enable MCA to BINIT promotion",
494 "Enable MCA promotion",
495 "Enable BERR promotion"
496};
497
498
499static int
500processor_info(char *page)
501{
502 char *p = page;
503 const char **v = proc_features;
504 u64 avail=1, status=1, control=1;
505 int i;
506 s64 ret;
507
508 if ((ret=ia64_pal_proc_get_features(&avail, &status, &control)) != 0) return 0;
509
510 for(i=0; i < 64; i++, v++,avail >>=1, status >>=1, control >>=1) {
511 if ( ! *v ) continue;
512 p += sprintf(p, "%-40s : %s%s %s\n", *v,
513 avail & 0x1 ? "" : "NotImpl",
514 avail & 0x1 ? (status & 0x1 ? "On" : "Off"): "",
515 avail & 0x1 ? (control & 0x1 ? "Ctrl" : "NoCtrl"): "");
516 }
517 return p - page;
518}
519
520static const char *bus_features[]={
521 NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
522 NULL,NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,
523 NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
524 NULL,NULL,
525 "Request Bus Parking",
526 "Bus Lock Mask",
527 "Enable Half Transfer",
528 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
529 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
530 NULL, NULL, NULL, NULL,
531 "Enable Cache Line Repl. Shared",
532 "Enable Cache Line Repl. Exclusive",
533 "Disable Transaction Queuing",
534 "Disable Response Error Checking",
535 "Disable Bus Error Checking",
536 "Disable Bus Requester Internal Error Signalling",
537 "Disable Bus Requester Error Signalling",
538 "Disable Bus Initialization Event Checking",
539 "Disable Bus Initialization Event Signalling",
540 "Disable Bus Address Error Checking",
541 "Disable Bus Address Error Signalling",
542 "Disable Bus Data Error Checking"
543};
544
545
546static int
547bus_info(char *page)
548{
549 char *p = page;
550 const char **v = bus_features;
551 pal_bus_features_u_t av, st, ct;
552 u64 avail, status, control;
553 int i;
554 s64 ret;
555
556 if ((ret=ia64_pal_bus_get_features(&av, &st, &ct)) != 0) return 0;
557
558 avail = av.pal_bus_features_val;
559 status = st.pal_bus_features_val;
560 control = ct.pal_bus_features_val;
561
562 for(i=0; i < 64; i++, v++, avail >>=1, status >>=1, control >>=1) {
563 if ( ! *v ) continue;
564 p += sprintf(p, "%-48s : %s%s %s\n", *v,
565 avail & 0x1 ? "" : "NotImpl",
566 avail & 0x1 ? (status & 0x1 ? "On" : "Off"): "",
567 avail & 0x1 ? (control & 0x1 ? "Ctrl" : "NoCtrl"): "");
568 }
569 return p - page;
570}
571
572static int
573version_info(char *page)
574{
575 pal_version_u_t min_ver, cur_ver;
576 char *p = page;
577
Matthew Wilcox1bf1eba2006-06-23 13:15:55 -0600578 if (ia64_pal_version(&min_ver, &cur_ver) != 0)
579 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580
581 p += sprintf(p,
582 "PAL_vendor : 0x%02x (min=0x%02x)\n"
Matthew Wilcox1bf1eba2006-06-23 13:15:55 -0600583 "PAL_A : %02x.%02x (min=%02x.%02x)\n"
584 "PAL_B : %02x.%02x (min=%02x.%02x)\n",
585 cur_ver.pal_version_s.pv_pal_vendor,
586 min_ver.pal_version_s.pv_pal_vendor,
587 cur_ver.pal_version_s.pv_pal_a_model,
588 cur_ver.pal_version_s.pv_pal_a_rev,
589 min_ver.pal_version_s.pv_pal_a_model,
590 min_ver.pal_version_s.pv_pal_a_rev,
591 cur_ver.pal_version_s.pv_pal_b_model,
592 cur_ver.pal_version_s.pv_pal_b_rev,
593 min_ver.pal_version_s.pv_pal_b_model,
594 min_ver.pal_version_s.pv_pal_b_rev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 return p - page;
596}
597
598static int
599perfmon_info(char *page)
600{
601 char *p = page;
602 u64 pm_buffer[16];
603 pal_perf_mon_info_u_t pm_info;
604
605 if (ia64_pal_perf_mon_info(pm_buffer, &pm_info) != 0) return 0;
606
607 p += sprintf(p,
608 "PMC/PMD pairs : %d\n"
609 "Counter width : %d bits\n"
610 "Cycle event number : %d\n"
611 "Retired event number : %d\n"
612 "Implemented PMC : ",
613 pm_info.pal_perf_mon_info_s.generic, pm_info.pal_perf_mon_info_s.width,
614 pm_info.pal_perf_mon_info_s.cycles, pm_info.pal_perf_mon_info_s.retired);
615
616 p = bitregister_process(p, pm_buffer, 256);
617 p += sprintf(p, "\nImplemented PMD : ");
618 p = bitregister_process(p, pm_buffer+4, 256);
619 p += sprintf(p, "\nCycles count capable : ");
620 p = bitregister_process(p, pm_buffer+8, 256);
621 p += sprintf(p, "\nRetired bundles count capable : ");
622
623#ifdef CONFIG_ITANIUM
624 /*
625 * PAL_PERF_MON_INFO reports that only PMC4 can be used to count CPU_CYCLES
626 * which is wrong, both PMC4 and PMD5 support it.
627 */
628 if (pm_buffer[12] == 0x10) pm_buffer[12]=0x30;
629#endif
630
631 p = bitregister_process(p, pm_buffer+12, 256);
632
633 p += sprintf(p, "\n");
634
635 return p - page;
636}
637
638static int
639frequency_info(char *page)
640{
641 char *p = page;
642 struct pal_freq_ratio proc, itc, bus;
643 u64 base;
644
645 if (ia64_pal_freq_base(&base) == -1)
646 p += sprintf(p, "Output clock : not implemented\n");
647 else
648 p += sprintf(p, "Output clock : %ld ticks/s\n", base);
649
650 if (ia64_pal_freq_ratios(&proc, &bus, &itc) != 0) return 0;
651
652 p += sprintf(p,
Tony Luck2ab93912006-03-31 10:28:29 -0800653 "Processor/Clock ratio : %d/%d\n"
654 "Bus/Clock ratio : %d/%d\n"
655 "ITC/Clock ratio : %d/%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656 proc.num, proc.den, bus.num, bus.den, itc.num, itc.den);
657
658 return p - page;
659}
660
661static int
662tr_info(char *page)
663{
664 char *p = page;
665 s64 status;
666 pal_tr_valid_u_t tr_valid;
667 u64 tr_buffer[4];
668 pal_vm_info_1_u_t vm_info_1;
669 pal_vm_info_2_u_t vm_info_2;
670 u64 i, j;
671 u64 max[3], pgm;
672 struct ifa_reg {
673 u64 valid:1;
674 u64 ig:11;
675 u64 vpn:52;
676 } *ifa_reg;
677 struct itir_reg {
678 u64 rv1:2;
679 u64 ps:6;
680 u64 key:24;
681 u64 rv2:32;
682 } *itir_reg;
683 struct gr_reg {
684 u64 p:1;
685 u64 rv1:1;
686 u64 ma:3;
687 u64 a:1;
688 u64 d:1;
689 u64 pl:2;
690 u64 ar:3;
691 u64 ppn:38;
692 u64 rv2:2;
693 u64 ed:1;
694 u64 ig:11;
695 } *gr_reg;
696 struct rid_reg {
697 u64 ig1:1;
698 u64 rv1:1;
699 u64 ig2:6;
700 u64 rid:24;
701 u64 rv2:32;
702 } *rid_reg;
703
704 if ((status = ia64_pal_vm_summary(&vm_info_1, &vm_info_2)) !=0) {
705 printk(KERN_ERR "ia64_pal_vm_summary=%ld\n", status);
706 return 0;
707 }
708 max[0] = vm_info_1.pal_vm_info_1_s.max_itr_entry+1;
709 max[1] = vm_info_1.pal_vm_info_1_s.max_dtr_entry+1;
710
711 for (i=0; i < 2; i++ ) {
712 for (j=0; j < max[i]; j++) {
713
714 status = ia64_pal_tr_read(j, i, tr_buffer, &tr_valid);
715 if (status != 0) {
716 printk(KERN_ERR "palinfo: pal call failed on tr[%lu:%lu]=%ld\n",
717 i, j, status);
718 continue;
719 }
720
721 ifa_reg = (struct ifa_reg *)&tr_buffer[2];
722
723 if (ifa_reg->valid == 0) continue;
724
725 gr_reg = (struct gr_reg *)tr_buffer;
726 itir_reg = (struct itir_reg *)&tr_buffer[1];
727 rid_reg = (struct rid_reg *)&tr_buffer[3];
728
729 pgm = -1 << (itir_reg->ps - 12);
730 p += sprintf(p,
731 "%cTR%lu: av=%d pv=%d dv=%d mv=%d\n"
732 "\tppn : 0x%lx\n"
733 "\tvpn : 0x%lx\n"
734 "\tps : ",
735 "ID"[i], j,
736 tr_valid.pal_tr_valid_s.access_rights_valid,
737 tr_valid.pal_tr_valid_s.priv_level_valid,
738 tr_valid.pal_tr_valid_s.dirty_bit_valid,
739 tr_valid.pal_tr_valid_s.mem_attr_valid,
740 (gr_reg->ppn & pgm)<< 12, (ifa_reg->vpn & pgm)<< 12);
741
742 p = bitvector_process(p, 1<< itir_reg->ps);
743
744 p += sprintf(p,
745 "\n\tpl : %d\n"
746 "\tar : %d\n"
747 "\trid : %x\n"
748 "\tp : %d\n"
749 "\tma : %d\n"
750 "\td : %d\n",
751 gr_reg->pl, gr_reg->ar, rid_reg->rid, gr_reg->p, gr_reg->ma,
752 gr_reg->d);
753 }
754 }
755 return p - page;
756}
757
758
759
760/*
761 * List {name,function} pairs for every entry in /proc/palinfo/cpu*
762 */
763static palinfo_entry_t palinfo_entries[]={
764 { "version_info", version_info, },
765 { "vm_info", vm_info, },
766 { "cache_info", cache_info, },
767 { "power_info", power_info, },
768 { "register_info", register_info, },
769 { "processor_info", processor_info, },
770 { "perfmon_info", perfmon_info, },
771 { "frequency_info", frequency_info, },
772 { "bus_info", bus_info },
773 { "tr_info", tr_info, }
774};
775
776#define NR_PALINFO_ENTRIES (int) ARRAY_SIZE(palinfo_entries)
777
778/*
779 * this array is used to keep track of the proc entries we create. This is
780 * required in the module mode when we need to remove all entries. The procfs code
781 * does not do recursion of deletion
782 *
783 * Notes:
784 * - +1 accounts for the cpuN directory entry in /proc/pal
785 */
786#define NR_PALINFO_PROC_ENTRIES (NR_CPUS*(NR_PALINFO_ENTRIES+1))
787
788static struct proc_dir_entry *palinfo_proc_entries[NR_PALINFO_PROC_ENTRIES];
789static struct proc_dir_entry *palinfo_dir;
790
791/*
792 * This data structure is used to pass which cpu,function is being requested
793 * It must fit in a 64bit quantity to be passed to the proc callback routine
794 *
795 * In SMP mode, when we get a request for another CPU, we must call that
796 * other CPU using IPI and wait for the result before returning.
797 */
798typedef union {
799 u64 value;
800 struct {
801 unsigned req_cpu: 32; /* for which CPU this info is */
802 unsigned func_id: 32; /* which function is requested */
803 } pal_func_cpu;
804} pal_func_cpu_u_t;
805
806#define req_cpu pal_func_cpu.req_cpu
807#define func_id pal_func_cpu.func_id
808
809#ifdef CONFIG_SMP
810
811/*
812 * used to hold information about final function to call
813 */
814typedef struct {
815 palinfo_func_t func; /* pointer to function to call */
816 char *page; /* buffer to store results */
817 int ret; /* return value from call */
818} palinfo_smp_data_t;
819
820
821/*
822 * this function does the actual final call and he called
823 * from the smp code, i.e., this is the palinfo callback routine
824 */
825static void
826palinfo_smp_call(void *info)
827{
828 palinfo_smp_data_t *data = (palinfo_smp_data_t *)info;
829 if (data == NULL) {
830 printk(KERN_ERR "palinfo: data pointer is NULL\n");
831 data->ret = 0; /* no output */
832 return;
833 }
834 /* does this actual call */
835 data->ret = (*data->func)(data->page);
836}
837
838/*
839 * function called to trigger the IPI, we need to access a remote CPU
840 * Return:
841 * 0 : error or nothing to output
842 * otherwise how many bytes in the "page" buffer were written
843 */
844static
845int palinfo_handle_smp(pal_func_cpu_u_t *f, char *page)
846{
847 palinfo_smp_data_t ptr;
848 int ret;
849
850 ptr.func = palinfo_entries[f->func_id].proc_read;
851 ptr.page = page;
852 ptr.ret = 0; /* just in case */
853
854
855 /* will send IPI to other CPU and wait for completion of remote call */
856 if ((ret=smp_call_function_single(f->req_cpu, palinfo_smp_call, &ptr, 0, 1))) {
857 printk(KERN_ERR "palinfo: remote CPU call from %d to %d on function %d: "
858 "error %d\n", smp_processor_id(), f->req_cpu, f->func_id, ret);
859 return 0;
860 }
861 return ptr.ret;
862}
863#else /* ! CONFIG_SMP */
864static
865int palinfo_handle_smp(pal_func_cpu_u_t *f, char *page)
866{
867 printk(KERN_ERR "palinfo: should not be called with non SMP kernel\n");
868 return 0;
869}
870#endif /* CONFIG_SMP */
871
872/*
873 * Entry point routine: all calls go through this function
874 */
875static int
876palinfo_read_entry(char *page, char **start, off_t off, int count, int *eof, void *data)
877{
878 int len=0;
879 pal_func_cpu_u_t *f = (pal_func_cpu_u_t *)&data;
880
881 /*
882 * in SMP mode, we may need to call another CPU to get correct
883 * information. PAL, by definition, is processor specific
884 */
885 if (f->req_cpu == get_cpu())
886 len = (*palinfo_entries[f->func_id].proc_read)(page);
887 else
888 len = palinfo_handle_smp(f, page);
889
890 put_cpu();
891
892 if (len <= off+count) *eof = 1;
893
894 *start = page + off;
895 len -= off;
896
897 if (len>count) len = count;
898 if (len<0) len = 0;
899
900 return len;
901}
902
903static void
904create_palinfo_proc_entries(unsigned int cpu)
905{
906# define CPUSTR "cpu%d"
907
908 pal_func_cpu_u_t f;
909 struct proc_dir_entry **pdir;
910 struct proc_dir_entry *cpu_dir;
911 int j;
912 char cpustr[sizeof(CPUSTR)];
913
914
915 /*
916 * we keep track of created entries in a depth-first order for
917 * cleanup purposes. Each entry is stored into palinfo_proc_entries
918 */
919 sprintf(cpustr,CPUSTR, cpu);
920
921 cpu_dir = proc_mkdir(cpustr, palinfo_dir);
922
923 f.req_cpu = cpu;
924
925 /*
926 * Compute the location to store per cpu entries
927 * We dont store the top level entry in this list, but
928 * remove it finally after removing all cpu entries.
929 */
930 pdir = &palinfo_proc_entries[cpu*(NR_PALINFO_ENTRIES+1)];
931 *pdir++ = cpu_dir;
932 for (j=0; j < NR_PALINFO_ENTRIES; j++) {
933 f.func_id = j;
934 *pdir = create_proc_read_entry(
935 palinfo_entries[j].name, 0, cpu_dir,
936 palinfo_read_entry, (void *)f.value);
937 if (*pdir)
938 (*pdir)->owner = THIS_MODULE;
939 pdir++;
940 }
941}
942
943static void
944remove_palinfo_proc_entries(unsigned int hcpu)
945{
946 int j;
947 struct proc_dir_entry *cpu_dir, **pdir;
948
949 pdir = &palinfo_proc_entries[hcpu*(NR_PALINFO_ENTRIES+1)];
950 cpu_dir = *pdir;
951 *pdir++=NULL;
952 for (j=0; j < (NR_PALINFO_ENTRIES); j++) {
953 if ((*pdir)) {
954 remove_proc_entry ((*pdir)->name, cpu_dir);
955 *pdir ++= NULL;
956 }
957 }
958
959 if (cpu_dir) {
960 remove_proc_entry(cpu_dir->name, palinfo_dir);
961 }
962}
963
Chandra Seetharamanbe6b5a32006-07-30 03:03:37 -0700964#ifdef CONFIG_HOTPLUG_CPU
965static int palinfo_cpu_callback(struct notifier_block *nfb,
966 unsigned long action, void *hcpu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967{
968 unsigned int hotcpu = (unsigned long)hcpu;
969
970 switch (action) {
971 case CPU_ONLINE:
972 create_palinfo_proc_entries(hotcpu);
973 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 case CPU_DEAD:
975 remove_palinfo_proc_entries(hotcpu);
976 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977 }
978 return NOTIFY_OK;
979}
980
Chandra Seetharamanbe6b5a32006-07-30 03:03:37 -0700981static struct notifier_block palinfo_cpu_notifier =
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982{
983 .notifier_call = palinfo_cpu_callback,
984 .priority = 0,
985};
Chandra Seetharamanbe6b5a32006-07-30 03:03:37 -0700986#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987
988static int __init
989palinfo_init(void)
990{
991 int i = 0;
992
993 printk(KERN_INFO "PAL Information Facility v%s\n", PALINFO_VERSION);
994 palinfo_dir = proc_mkdir("pal", NULL);
995
996 /* Create palinfo dirs in /proc for all online cpus */
997 for_each_online_cpu(i) {
998 create_palinfo_proc_entries(i);
999 }
1000
1001 /* Register for future delivery via notify registration */
Tony Luckf1206642006-06-28 09:55:13 -07001002 register_hotcpu_notifier(&palinfo_cpu_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003
1004 return 0;
1005}
1006
1007static void __exit
1008palinfo_exit(void)
1009{
1010 int i = 0;
1011
1012 /* remove all nodes: depth first pass. Could optimize this */
1013 for_each_online_cpu(i) {
1014 remove_palinfo_proc_entries(i);
1015 }
1016
1017 /*
1018 * Remove the top level entry finally
1019 */
1020 remove_proc_entry(palinfo_dir->name, NULL);
1021
1022 /*
1023 * Unregister from cpu notifier callbacks
1024 */
Chandra Seetharamanbe6b5a32006-07-30 03:03:37 -07001025 unregister_hotcpu_notifier(&palinfo_cpu_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026}
1027
1028module_init(palinfo_init);
1029module_exit(palinfo_exit);