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