aboutsummaryrefslogtreecommitdiffstats
path: root/arch/ia64/kernel/palinfo.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/ia64/kernel/palinfo.c
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'arch/ia64/kernel/palinfo.c')
-rw-r--r--arch/ia64/kernel/palinfo.c1023
1 files changed, 1023 insertions, 0 deletions
diff --git a/arch/ia64/kernel/palinfo.c b/arch/ia64/kernel/palinfo.c
new file mode 100644
index 000000000000..25e7c8344564
--- /dev/null
+++ b/arch/ia64/kernel/palinfo.c
@@ -0,0 +1,1023 @@
1/*
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
19 */
20#include <linux/config.h>
21#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"
243 "\tSize : %lu bytes\n"
244 "\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);
310 return 0;
311 }
312
313
314 p += sprintf(p,
315 "Physical Address Space : %d bits\n"
316 "Virtual Address Space : %d bits\n"
317 "Protection Key Registers(PKR) : %d\n"
318 "Implemented bits in PKR.key : %d\n"
319 "Hash Tag ID : 0x%x\n"
320 "Size of RR.rid : %d\n",
321 vm_info_1.pal_vm_info_1_s.phys_add_size,
322 vm_info_2.pal_vm_info_2_s.impl_va_msb+1, vm_info_1.pal_vm_info_1_s.max_pkr+1,
323 vm_info_1.pal_vm_info_1_s.key_size, vm_info_1.pal_vm_info_1_s.hash_tag_id,
324 vm_info_2.pal_vm_info_2_s.rid_size);
325
326 if (ia64_pal_mem_attrib(&attrib) != 0)
327 return 0;
328
329 p += sprintf(p, "Supported memory attributes : ");
330 sep = "";
331 for (i = 0; i < 8; i++) {
332 if (attrib & (1 << i)) {
333 p += sprintf(p, "%s%s", sep, mem_attrib[i]);
334 sep = ", ";
335 }
336 }
337 p += sprintf(p, "\n");
338
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);
341 return 0;
342 }
343
344 p += sprintf(p,
345 "\nTLB walker : %simplemented\n"
346 "Number of DTR : %d\n"
347 "Number of ITR : %d\n"
348 "TLB insertable page sizes : ",
349 vm_info_1.pal_vm_info_1_s.vw ? "" : "not ",
350 vm_info_1.pal_vm_info_1_s.max_dtr_entry+1,
351 vm_info_1.pal_vm_info_1_s.max_itr_entry+1);
352
353
354 p = bitvector_process(p, tr_pages);
355
356 p += sprintf(p, "\nTLB purgeable page sizes : ");
357
358 p = bitvector_process(p, vw_pages);
359
360 if ((status=ia64_get_ptce(&ptce)) != 0) {
361 printk(KERN_ERR "ia64_get_ptce=%ld\n", status);
362 return 0;
363 }
364
365 p += sprintf(p,
366 "\nPurge base address : 0x%016lx\n"
367 "Purge outer loop count : %d\n"
368 "Purge inner loop count : %d\n"
369 "Purge outer loop stride : %d\n"
370 "Purge inner loop stride : %d\n",
371 ptce.base, ptce.count[0], ptce.count[1], ptce.stride[0], ptce.stride[1]);
372
373 p += sprintf(p,
374 "TC Levels : %d\n"
375 "Unique TC(s) : %d\n",
376 vm_info_1.pal_vm_info_1_s.num_tc_levels,
377 vm_info_1.pal_vm_info_1_s.max_unique_tcs);
378
379 for(i=0; i < vm_info_1.pal_vm_info_1_s.num_tc_levels; i++) {
380 for (j=2; j>0 ; j--) {
381 tc_pages = 0; /* just in case */
382
383
384 /* even without unification, some levels may not be present */
385 if ((status=ia64_pal_vm_info(i,j, &tc_info, &tc_pages)) != 0) {
386 continue;
387 }
388
389 p += sprintf(p,
390 "\n%s Translation Cache Level %d:\n"
391 "\tHash sets : %d\n"
392 "\tAssociativity : %d\n"
393 "\tNumber of entries : %d\n"
394 "\tFlags : ",
395 cache_types[j+tc_info.tc_unified], i+1, tc_info.tc_num_sets,
396 tc_info.tc_associativity, tc_info.tc_num_entries);
397
398 if (tc_info.tc_pf) p += sprintf(p, "PreferredPageSizeOptimized ");
399 if (tc_info.tc_unified) p += sprintf(p, "Unified ");
400 if (tc_info.tc_reduce_tr) p += sprintf(p, "TCReduction");
401
402 p += sprintf(p, "\n\tSupported page sizes: ");
403
404 p = bitvector_process(p, tc_pages);
405
406 /* when unified date (j=2) is enough */
407 if (tc_info.tc_unified) break;
408 }
409 }
410 p += sprintf(p, "\n");
411
412 return p - page;
413}
414
415
416static int
417register_info(char *page)
418{
419 char *p = page;
420 u64 reg_info[2];
421 u64 info;
422 u64 phys_stacked;
423 pal_hints_u_t hints;
424 u64 iregs, dregs;
425 char *info_type[]={
426 "Implemented AR(s)",
427 "AR(s) with read side-effects",
428 "Implemented CR(s)",
429 "CR(s) with read side-effects",
430 };
431
432 for(info=0; info < 4; info++) {
433
434 if (ia64_pal_register_info(info, &reg_info[0], &reg_info[1]) != 0) return 0;
435
436 p += sprintf(p, "%-32s : ", info_type[info]);
437
438 p = bitregister_process(p, reg_info, 128);
439
440 p += sprintf(p, "\n");
441 }
442
443 if (ia64_pal_rse_info(&phys_stacked, &hints) != 0) return 0;
444
445 p += sprintf(p,
446 "RSE stacked physical registers : %ld\n"
447 "RSE load/store hints : %ld (%s)\n",
448 phys_stacked, hints.ph_data,
449 hints.ph_data < RSE_HINTS_COUNT ? rse_hints[hints.ph_data]: "(??)");
450
451 if (ia64_pal_debug_info(&iregs, &dregs))
452 return 0;
453
454 p += sprintf(p,
455 "Instruction debug register pairs : %ld\n"
456 "Data debug register pairs : %ld\n", iregs, dregs);
457
458 return p - page;
459}
460
461static const char *proc_features[]={
462 NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
463 NULL,NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,
464 NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
465 NULL,NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,
466 NULL,NULL,NULL,NULL,NULL,
467 "XIP,XPSR,XFS implemented",
468 "XR1-XR3 implemented",
469 "Disable dynamic predicate prediction",
470 "Disable processor physical number",
471 "Disable dynamic data cache prefetch",
472 "Disable dynamic inst cache prefetch",
473 "Disable dynamic branch prediction",
474 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
475 "Disable BINIT on processor time-out",
476 "Disable dynamic power management (DPM)",
477 "Disable coherency",
478 "Disable cache",
479 "Enable CMCI promotion",
480 "Enable MCA to BINIT promotion",
481 "Enable MCA promotion",
482 "Enable BERR promotion"
483};
484
485
486static int
487processor_info(char *page)
488{
489 char *p = page;
490 const char **v = proc_features;
491 u64 avail=1, status=1, control=1;
492 int i;
493 s64 ret;
494
495 if ((ret=ia64_pal_proc_get_features(&avail, &status, &control)) != 0) return 0;
496
497 for(i=0; i < 64; i++, v++,avail >>=1, status >>=1, control >>=1) {
498 if ( ! *v ) continue;
499 p += sprintf(p, "%-40s : %s%s %s\n", *v,
500 avail & 0x1 ? "" : "NotImpl",
501 avail & 0x1 ? (status & 0x1 ? "On" : "Off"): "",
502 avail & 0x1 ? (control & 0x1 ? "Ctrl" : "NoCtrl"): "");
503 }
504 return p - page;
505}
506
507static const char *bus_features[]={
508 NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
509 NULL,NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,
510 NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
511 NULL,NULL,
512 "Request Bus Parking",
513 "Bus Lock Mask",
514 "Enable Half Transfer",
515 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
516 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
517 NULL, NULL, NULL, NULL,
518 "Enable Cache Line Repl. Shared",
519 "Enable Cache Line Repl. Exclusive",
520 "Disable Transaction Queuing",
521 "Disable Response Error Checking",
522 "Disable Bus Error Checking",
523 "Disable Bus Requester Internal Error Signalling",
524 "Disable Bus Requester Error Signalling",
525 "Disable Bus Initialization Event Checking",
526 "Disable Bus Initialization Event Signalling",
527 "Disable Bus Address Error Checking",
528 "Disable Bus Address Error Signalling",
529 "Disable Bus Data Error Checking"
530};
531
532
533static int
534bus_info(char *page)
535{
536 char *p = page;
537 const char **v = bus_features;
538 pal_bus_features_u_t av, st, ct;
539 u64 avail, status, control;
540 int i;
541 s64 ret;
542
543 if ((ret=ia64_pal_bus_get_features(&av, &st, &ct)) != 0) return 0;
544
545 avail = av.pal_bus_features_val;
546 status = st.pal_bus_features_val;
547 control = ct.pal_bus_features_val;
548
549 for(i=0; i < 64; i++, v++, avail >>=1, status >>=1, control >>=1) {
550 if ( ! *v ) continue;
551 p += sprintf(p, "%-48s : %s%s %s\n", *v,
552 avail & 0x1 ? "" : "NotImpl",
553 avail & 0x1 ? (status & 0x1 ? "On" : "Off"): "",
554 avail & 0x1 ? (control & 0x1 ? "Ctrl" : "NoCtrl"): "");
555 }
556 return p - page;
557}
558
559static int
560version_info(char *page)
561{
562 pal_version_u_t min_ver, cur_ver;
563 char *p = page;
564
565 /* The PAL_VERSION call is advertised as being able to support
566 * both physical and virtual mode calls. This seems to be a documentation
567 * bug rather than firmware bug. In fact, it does only support physical mode.
568 * So now the code reflects this fact and the pal_version() has been updated
569 * accordingly.
570 */
571 if (ia64_pal_version(&min_ver, &cur_ver) != 0) return 0;
572
573 p += sprintf(p,
574 "PAL_vendor : 0x%02x (min=0x%02x)\n"
575 "PAL_A : %x.%x.%x (min=%x.%x.%x)\n"
576 "PAL_B : %x.%x.%x (min=%x.%x.%x)\n",
577 cur_ver.pal_version_s.pv_pal_vendor, min_ver.pal_version_s.pv_pal_vendor,
578
579 cur_ver.pal_version_s.pv_pal_a_model>>4,
580 cur_ver.pal_version_s.pv_pal_a_model&0xf, cur_ver.pal_version_s.pv_pal_a_rev,
581 min_ver.pal_version_s.pv_pal_a_model>>4,
582 min_ver.pal_version_s.pv_pal_a_model&0xf, min_ver.pal_version_s.pv_pal_a_rev,
583
584 cur_ver.pal_version_s.pv_pal_b_model>>4,
585 cur_ver.pal_version_s.pv_pal_b_model&0xf, cur_ver.pal_version_s.pv_pal_b_rev,
586 min_ver.pal_version_s.pv_pal_b_model>>4,
587 min_ver.pal_version_s.pv_pal_b_model&0xf, min_ver.pal_version_s.pv_pal_b_rev);
588 return p - page;
589}
590
591static int
592perfmon_info(char *page)
593{
594 char *p = page;
595 u64 pm_buffer[16];
596 pal_perf_mon_info_u_t pm_info;
597
598 if (ia64_pal_perf_mon_info(pm_buffer, &pm_info) != 0) return 0;
599
600 p += sprintf(p,
601 "PMC/PMD pairs : %d\n"
602 "Counter width : %d bits\n"
603 "Cycle event number : %d\n"
604 "Retired event number : %d\n"
605 "Implemented PMC : ",
606 pm_info.pal_perf_mon_info_s.generic, pm_info.pal_perf_mon_info_s.width,
607 pm_info.pal_perf_mon_info_s.cycles, pm_info.pal_perf_mon_info_s.retired);
608
609 p = bitregister_process(p, pm_buffer, 256);
610 p += sprintf(p, "\nImplemented PMD : ");
611 p = bitregister_process(p, pm_buffer+4, 256);
612 p += sprintf(p, "\nCycles count capable : ");
613 p = bitregister_process(p, pm_buffer+8, 256);
614 p += sprintf(p, "\nRetired bundles count capable : ");
615
616#ifdef CONFIG_ITANIUM
617 /*
618 * PAL_PERF_MON_INFO reports that only PMC4 can be used to count CPU_CYCLES
619 * which is wrong, both PMC4 and PMD5 support it.
620 */
621 if (pm_buffer[12] == 0x10) pm_buffer[12]=0x30;
622#endif
623
624 p = bitregister_process(p, pm_buffer+12, 256);
625
626 p += sprintf(p, "\n");
627
628 return p - page;
629}
630
631static int
632frequency_info(char *page)
633{
634 char *p = page;
635 struct pal_freq_ratio proc, itc, bus;
636 u64 base;
637
638 if (ia64_pal_freq_base(&base) == -1)
639 p += sprintf(p, "Output clock : not implemented\n");
640 else
641 p += sprintf(p, "Output clock : %ld ticks/s\n", base);
642
643 if (ia64_pal_freq_ratios(&proc, &bus, &itc) != 0) return 0;
644
645 p += sprintf(p,
646 "Processor/Clock ratio : %ld/%ld\n"
647 "Bus/Clock ratio : %ld/%ld\n"
648 "ITC/Clock ratio : %ld/%ld\n",
649 proc.num, proc.den, bus.num, bus.den, itc.num, itc.den);
650
651 return p - page;
652}
653
654static int
655tr_info(char *page)
656{
657 char *p = page;
658 s64 status;
659 pal_tr_valid_u_t tr_valid;
660 u64 tr_buffer[4];
661 pal_vm_info_1_u_t vm_info_1;
662 pal_vm_info_2_u_t vm_info_2;
663 u64 i, j;
664 u64 max[3], pgm;
665 struct ifa_reg {
666 u64 valid:1;
667 u64 ig:11;
668 u64 vpn:52;
669 } *ifa_reg;
670 struct itir_reg {
671 u64 rv1:2;
672 u64 ps:6;
673 u64 key:24;
674 u64 rv2:32;
675 } *itir_reg;
676 struct gr_reg {
677 u64 p:1;
678 u64 rv1:1;
679 u64 ma:3;
680 u64 a:1;
681 u64 d:1;
682 u64 pl:2;
683 u64 ar:3;
684 u64 ppn:38;
685 u64 rv2:2;
686 u64 ed:1;
687 u64 ig:11;
688 } *gr_reg;
689 struct rid_reg {
690 u64 ig1:1;
691 u64 rv1:1;
692 u64 ig2:6;
693 u64 rid:24;
694 u64 rv2:32;
695 } *rid_reg;
696
697 if ((status = ia64_pal_vm_summary(&vm_info_1, &vm_info_2)) !=0) {
698 printk(KERN_ERR "ia64_pal_vm_summary=%ld\n", status);
699 return 0;
700 }
701 max[0] = vm_info_1.pal_vm_info_1_s.max_itr_entry+1;
702 max[1] = vm_info_1.pal_vm_info_1_s.max_dtr_entry+1;
703
704 for (i=0; i < 2; i++ ) {
705 for (j=0; j < max[i]; j++) {
706
707 status = ia64_pal_tr_read(j, i, tr_buffer, &tr_valid);
708 if (status != 0) {
709 printk(KERN_ERR "palinfo: pal call failed on tr[%lu:%lu]=%ld\n",
710 i, j, status);
711 continue;
712 }
713
714 ifa_reg = (struct ifa_reg *)&tr_buffer[2];
715
716 if (ifa_reg->valid == 0) continue;
717
718 gr_reg = (struct gr_reg *)tr_buffer;
719 itir_reg = (struct itir_reg *)&tr_buffer[1];
720 rid_reg = (struct rid_reg *)&tr_buffer[3];
721
722 pgm = -1 << (itir_reg->ps - 12);
723 p += sprintf(p,
724 "%cTR%lu: av=%d pv=%d dv=%d mv=%d\n"
725 "\tppn : 0x%lx\n"
726 "\tvpn : 0x%lx\n"
727 "\tps : ",
728 "ID"[i], j,
729 tr_valid.pal_tr_valid_s.access_rights_valid,
730 tr_valid.pal_tr_valid_s.priv_level_valid,
731 tr_valid.pal_tr_valid_s.dirty_bit_valid,
732 tr_valid.pal_tr_valid_s.mem_attr_valid,
733 (gr_reg->ppn & pgm)<< 12, (ifa_reg->vpn & pgm)<< 12);
734
735 p = bitvector_process(p, 1<< itir_reg->ps);
736
737 p += sprintf(p,
738 "\n\tpl : %d\n"
739 "\tar : %d\n"
740 "\trid : %x\n"
741 "\tp : %d\n"
742 "\tma : %d\n"
743 "\td : %d\n",
744 gr_reg->pl, gr_reg->ar, rid_reg->rid, gr_reg->p, gr_reg->ma,
745 gr_reg->d);
746 }
747 }
748 return p - page;
749}
750
751
752
753/*
754 * List {name,function} pairs for every entry in /proc/palinfo/cpu*
755 */
756static palinfo_entry_t palinfo_entries[]={
757 { "version_info", version_info, },
758 { "vm_info", vm_info, },
759 { "cache_info", cache_info, },
760 { "power_info", power_info, },
761 { "register_info", register_info, },
762 { "processor_info", processor_info, },
763 { "perfmon_info", perfmon_info, },
764 { "frequency_info", frequency_info, },
765 { "bus_info", bus_info },
766 { "tr_info", tr_info, }
767};
768
769#define NR_PALINFO_ENTRIES (int) ARRAY_SIZE(palinfo_entries)
770
771/*
772 * this array is used to keep track of the proc entries we create. This is
773 * required in the module mode when we need to remove all entries. The procfs code
774 * does not do recursion of deletion
775 *
776 * Notes:
777 * - +1 accounts for the cpuN directory entry in /proc/pal
778 */
779#define NR_PALINFO_PROC_ENTRIES (NR_CPUS*(NR_PALINFO_ENTRIES+1))
780
781static struct proc_dir_entry *palinfo_proc_entries[NR_PALINFO_PROC_ENTRIES];
782static struct proc_dir_entry *palinfo_dir;
783
784/*
785 * This data structure is used to pass which cpu,function is being requested
786 * It must fit in a 64bit quantity to be passed to the proc callback routine
787 *
788 * In SMP mode, when we get a request for another CPU, we must call that
789 * other CPU using IPI and wait for the result before returning.
790 */
791typedef union {
792 u64 value;
793 struct {
794 unsigned req_cpu: 32; /* for which CPU this info is */
795 unsigned func_id: 32; /* which function is requested */
796 } pal_func_cpu;
797} pal_func_cpu_u_t;
798
799#define req_cpu pal_func_cpu.req_cpu
800#define func_id pal_func_cpu.func_id
801
802#ifdef CONFIG_SMP
803
804/*
805 * used to hold information about final function to call
806 */
807typedef struct {
808 palinfo_func_t func; /* pointer to function to call */
809 char *page; /* buffer to store results */
810 int ret; /* return value from call */
811} palinfo_smp_data_t;
812
813
814/*
815 * this function does the actual final call and he called
816 * from the smp code, i.e., this is the palinfo callback routine
817 */
818static void
819palinfo_smp_call(void *info)
820{
821 palinfo_smp_data_t *data = (palinfo_smp_data_t *)info;
822 if (data == NULL) {
823 printk(KERN_ERR "palinfo: data pointer is NULL\n");
824 data->ret = 0; /* no output */
825 return;
826 }
827 /* does this actual call */
828 data->ret = (*data->func)(data->page);
829}
830
831/*
832 * function called to trigger the IPI, we need to access a remote CPU
833 * Return:
834 * 0 : error or nothing to output
835 * otherwise how many bytes in the "page" buffer were written
836 */
837static
838int palinfo_handle_smp(pal_func_cpu_u_t *f, char *page)
839{
840 palinfo_smp_data_t ptr;
841 int ret;
842
843 ptr.func = palinfo_entries[f->func_id].proc_read;
844 ptr.page = page;
845 ptr.ret = 0; /* just in case */
846
847
848 /* will send IPI to other CPU and wait for completion of remote call */
849 if ((ret=smp_call_function_single(f->req_cpu, palinfo_smp_call, &ptr, 0, 1))) {
850 printk(KERN_ERR "palinfo: remote CPU call from %d to %d on function %d: "
851 "error %d\n", smp_processor_id(), f->req_cpu, f->func_id, ret);
852 return 0;
853 }
854 return ptr.ret;
855}
856#else /* ! CONFIG_SMP */
857static
858int palinfo_handle_smp(pal_func_cpu_u_t *f, char *page)
859{
860 printk(KERN_ERR "palinfo: should not be called with non SMP kernel\n");
861 return 0;
862}
863#endif /* CONFIG_SMP */
864
865/*
866 * Entry point routine: all calls go through this function
867 */
868static int
869palinfo_read_entry(char *page, char **start, off_t off, int count, int *eof, void *data)
870{
871 int len=0;
872 pal_func_cpu_u_t *f = (pal_func_cpu_u_t *)&data;
873
874 /*
875 * in SMP mode, we may need to call another CPU to get correct
876 * information. PAL, by definition, is processor specific
877 */
878 if (f->req_cpu == get_cpu())
879 len = (*palinfo_entries[f->func_id].proc_read)(page);
880 else
881 len = palinfo_handle_smp(f, page);
882
883 put_cpu();
884
885 if (len <= off+count) *eof = 1;
886
887 *start = page + off;
888 len -= off;
889
890 if (len>count) len = count;
891 if (len<0) len = 0;
892
893 return len;
894}
895
896static void
897create_palinfo_proc_entries(unsigned int cpu)
898{
899# define CPUSTR "cpu%d"
900
901 pal_func_cpu_u_t f;
902 struct proc_dir_entry **pdir;
903 struct proc_dir_entry *cpu_dir;
904 int j;
905 char cpustr[sizeof(CPUSTR)];
906
907
908 /*
909 * we keep track of created entries in a depth-first order for
910 * cleanup purposes. Each entry is stored into palinfo_proc_entries
911 */
912 sprintf(cpustr,CPUSTR, cpu);
913
914 cpu_dir = proc_mkdir(cpustr, palinfo_dir);
915
916 f.req_cpu = cpu;
917
918 /*
919 * Compute the location to store per cpu entries
920 * We dont store the top level entry in this list, but
921 * remove it finally after removing all cpu entries.
922 */
923 pdir = &palinfo_proc_entries[cpu*(NR_PALINFO_ENTRIES+1)];
924 *pdir++ = cpu_dir;
925 for (j=0; j < NR_PALINFO_ENTRIES; j++) {
926 f.func_id = j;
927 *pdir = create_proc_read_entry(
928 palinfo_entries[j].name, 0, cpu_dir,
929 palinfo_read_entry, (void *)f.value);
930 if (*pdir)
931 (*pdir)->owner = THIS_MODULE;
932 pdir++;
933 }
934}
935
936static void
937remove_palinfo_proc_entries(unsigned int hcpu)
938{
939 int j;
940 struct proc_dir_entry *cpu_dir, **pdir;
941
942 pdir = &palinfo_proc_entries[hcpu*(NR_PALINFO_ENTRIES+1)];
943 cpu_dir = *pdir;
944 *pdir++=NULL;
945 for (j=0; j < (NR_PALINFO_ENTRIES); j++) {
946 if ((*pdir)) {
947 remove_proc_entry ((*pdir)->name, cpu_dir);
948 *pdir ++= NULL;
949 }
950 }
951
952 if (cpu_dir) {
953 remove_proc_entry(cpu_dir->name, palinfo_dir);
954 }
955}
956
957static int __devinit palinfo_cpu_callback(struct notifier_block *nfb,
958 unsigned long action,
959 void *hcpu)
960{
961 unsigned int hotcpu = (unsigned long)hcpu;
962
963 switch (action) {
964 case CPU_ONLINE:
965 create_palinfo_proc_entries(hotcpu);
966 break;
967#ifdef CONFIG_HOTPLUG_CPU
968 case CPU_DEAD:
969 remove_palinfo_proc_entries(hotcpu);
970 break;
971#endif
972 }
973 return NOTIFY_OK;
974}
975
976static struct notifier_block palinfo_cpu_notifier =
977{
978 .notifier_call = palinfo_cpu_callback,
979 .priority = 0,
980};
981
982static int __init
983palinfo_init(void)
984{
985 int i = 0;
986
987 printk(KERN_INFO "PAL Information Facility v%s\n", PALINFO_VERSION);
988 palinfo_dir = proc_mkdir("pal", NULL);
989
990 /* Create palinfo dirs in /proc for all online cpus */
991 for_each_online_cpu(i) {
992 create_palinfo_proc_entries(i);
993 }
994
995 /* Register for future delivery via notify registration */
996 register_cpu_notifier(&palinfo_cpu_notifier);
997
998 return 0;
999}
1000
1001static void __exit
1002palinfo_exit(void)
1003{
1004 int i = 0;
1005
1006 /* remove all nodes: depth first pass. Could optimize this */
1007 for_each_online_cpu(i) {
1008 remove_palinfo_proc_entries(i);
1009 }
1010
1011 /*
1012 * Remove the top level entry finally
1013 */
1014 remove_proc_entry(palinfo_dir->name, NULL);
1015
1016 /*
1017 * Unregister from cpu notifier callbacks
1018 */
1019 unregister_cpu_notifier(&palinfo_cpu_notifier);
1020}
1021
1022module_init(palinfo_init);
1023module_exit(palinfo_exit);