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.
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>
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 * 10/26/2006 Russ Anderson updated processor features to rev 2.2 spec
21 #include <linux/types.h>
22 #include <linux/errno.h>
23 #include <linux/init.h>
24 #include <linux/proc_fs.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>
35 #include <asm/processor.h>
36 #include <linux/smp.h>
38 MODULE_AUTHOR("Stephane Eranian <eranian@hpl.hp.com>");
39 MODULE_DESCRIPTION("/proc interface to IA-64 PAL");
40 MODULE_LICENSE("GPL");
42 #define PALINFO_VERSION "0.5"
44 typedef int (*palinfo_func_t
)(char*);
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) */
54 * A bunch of string array to get pretty printing
57 static char *cache_types
[] = {
61 "Data/Instruction" /* unified */
64 static const char *cache_mattrib
[]={
71 static const char *cache_st_hints
[]={
75 "Non-temporal, all levels",
82 static const char *cache_ld_hints
[]={
84 "Non-temporal, level 1",
86 "Non-temporal, all levels",
93 static const char *rse_hints
[]={
97 "eager loads and stores"
100 #define RSE_HINTS_COUNT ARRAY_SIZE(rse_hints)
102 static const char *mem_attrib
[]={
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.
119 * - a pointer to a buffer to hold the string
122 * - a pointer to the end of the buffer
126 bitvector_process(char *p
, u64 vector
)
129 const char *units
[]={ "", "K", "M", "G", "T" };
131 for (i
=0, j
=0; i
< 64; i
++ , j
=i
/10) {
133 p
+= sprintf(p
, "%d%s ", 1 << (i
-j
*10), units
[j
]);
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.
146 * - a pointer to a buffer to hold the string
149 * - a pointer to the end of the buffer
153 bitregister_process(char *p
, u64
*reg_info
, int max
)
155 int i
, begin
, skip
= 0;
156 u64 value
= reg_info
[0];
158 value
>>= i
= begin
= ffs(value
) - 1;
160 for(; i
< max
; i
++ ) {
162 if (i
!= 0 && (i
%64) == 0) value
= *++reg_info
;
164 if ((value
& 0x1) == 0 && skip
== 0) {
166 p
+= sprintf(p
, "%d-%d ", begin
, i
-1);
168 p
+= sprintf(p
, "%d ", i
-1);
171 } else if ((value
& 0x1) && skip
== 1) {
179 p
+= sprintf(p
, "%d-127", begin
);
181 p
+= sprintf(p
, "127");
188 power_info(char *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
;
196 status
= ia64_pal_halt_info(halt_info
);
197 if (status
!= 0) return 0;
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");
211 p
+= sprintf(p
,"Power level %d: not implemented\n",i
);
218 cache_info(char *page
)
221 u64 i
, levels
, unique_caches
;
222 pal_cache_config_info_t cci
;
226 if ((status
= ia64_pal_cache_summary(&levels
, &unique_caches
)) != 0) {
227 printk(KERN_ERR
"ia64_pal_cache_summary=%ld\n", status
);
231 p
+= sprintf(p
, "Cache levels : %ld\nUnique caches : %ld\n\n", levels
, unique_caches
);
233 for (i
=0; i
< levels
; i
++) {
235 for (j
=2; j
>0 ; j
--) {
237 /* even without unification some level may not be present */
238 if ((status
=ia64_pal_cache_config_info(i
,j
, &cci
)) != 0) {
242 "%s Cache level %lu:\n"
243 "\tSize : %u bytes\n"
245 cache_types
[j
+cci
.pcci_unified
], i
+1,
246 cci
.pcci_cache_size
);
248 if (cci
.pcci_unified
) p
+= sprintf(p
, "Unified ");
250 p
+= sprintf(p
, "%s\n", cache_mattrib
[cci
.pcci_cache_attr
]);
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
);
258 p
+= sprintf(p
, "\tStore latency : N/A\n");
260 p
+= sprintf(p
, "\tStore latency : %d cycle(s)\n",
261 cci
.pcci_st_latency
);
264 "\tLoad latency : %d cycle(s)\n"
265 "\tStore hints : ", cci
.pcci_ld_latency
);
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;
272 p
+= sprintf(p
, "\n\tLoad hints : ");
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;
280 "\n\tAlias boundary : %d byte(s)\n"
283 1<<cci
.pcci_alias_boundary
, cci
.pcci_tag_lsb
,
286 /* when unified, data(j=2) is enough */
287 if (cci
.pcci_unified
) break;
298 u64 tr_pages
=0, vw_pages
=0, tc_pages
;
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
;
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
);
313 "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"
320 vm_info_1
.pal_vm_info_1_s
.phys_add_size
,
321 vm_info_2
.pal_vm_info_2_s
.impl_va_msb
+1,
322 vm_info_1
.pal_vm_info_1_s
.max_pkr
+1,
323 vm_info_1
.pal_vm_info_1_s
.key_size
,
324 vm_info_1
.pal_vm_info_1_s
.hash_tag_id
,
325 vm_info_2
.pal_vm_info_2_s
.rid_size
);
326 if (vm_info_2
.pal_vm_info_2_s
.max_purges
== PAL_MAX_PURGES
)
327 p
+= sprintf(p
, "unlimited\n");
329 p
+= sprintf(p
, "%d\n",
330 vm_info_2
.pal_vm_info_2_s
.max_purges
?
331 vm_info_2
.pal_vm_info_2_s
.max_purges
: 1);
334 if (ia64_pal_mem_attrib(&attrib
) == 0) {
335 p
+= sprintf(p
, "Supported memory attributes : ");
337 for (i
= 0; i
< 8; i
++) {
338 if (attrib
& (1 << i
)) {
339 p
+= sprintf(p
, "%s%s", sep
, mem_attrib
[i
]);
343 p
+= sprintf(p
, "\n");
346 if ((status
= ia64_pal_vm_page_size(&tr_pages
, &vw_pages
)) !=0) {
347 printk(KERN_ERR
"ia64_pal_vm_page_size=%ld\n", status
);
351 "\nTLB walker : %simplemented\n"
352 "Number of DTR : %d\n"
353 "Number of ITR : %d\n"
354 "TLB insertable page sizes : ",
355 vm_info_1
.pal_vm_info_1_s
.vw
? "" : "not ",
356 vm_info_1
.pal_vm_info_1_s
.max_dtr_entry
+1,
357 vm_info_1
.pal_vm_info_1_s
.max_itr_entry
+1);
360 p
= bitvector_process(p
, tr_pages
);
362 p
+= sprintf(p
, "\nTLB purgeable page sizes : ");
364 p
= bitvector_process(p
, vw_pages
);
366 if ((status
=ia64_get_ptce(&ptce
)) != 0) {
367 printk(KERN_ERR
"ia64_get_ptce=%ld\n", status
);
370 "\nPurge base address : 0x%016lx\n"
371 "Purge outer loop count : %d\n"
372 "Purge inner loop count : %d\n"
373 "Purge outer loop stride : %d\n"
374 "Purge inner loop stride : %d\n",
375 ptce
.base
, ptce
.count
[0], ptce
.count
[1],
376 ptce
.stride
[0], ptce
.stride
[1]);
380 "Unique TC(s) : %d\n",
381 vm_info_1
.pal_vm_info_1_s
.num_tc_levels
,
382 vm_info_1
.pal_vm_info_1_s
.max_unique_tcs
);
384 for(i
=0; i
< vm_info_1
.pal_vm_info_1_s
.num_tc_levels
; i
++) {
385 for (j
=2; j
>0 ; j
--) {
386 tc_pages
= 0; /* just in case */
389 /* even without unification, some levels may not be present */
390 if ((status
=ia64_pal_vm_info(i
,j
, &tc_info
, &tc_pages
)) != 0) {
395 "\n%s Translation Cache Level %d:\n"
397 "\tAssociativity : %d\n"
398 "\tNumber of entries : %d\n"
400 cache_types
[j
+tc_info
.tc_unified
], i
+1,
402 tc_info
.tc_associativity
,
403 tc_info
.tc_num_entries
);
406 p
+= sprintf(p
, "PreferredPageSizeOptimized ");
407 if (tc_info
.tc_unified
)
408 p
+= sprintf(p
, "Unified ");
409 if (tc_info
.tc_reduce_tr
)
410 p
+= sprintf(p
, "TCReduction");
412 p
+= sprintf(p
, "\n\tSupported page sizes: ");
414 p
= bitvector_process(p
, tc_pages
);
416 /* when unified date (j=2) is enough */
417 if (tc_info
.tc_unified
)
422 p
+= sprintf(p
, "\n");
429 register_info(char *page
)
439 "AR(s) with read side-effects",
441 "CR(s) with read side-effects",
444 for(info
=0; info
< 4; info
++) {
446 if (ia64_pal_register_info(info
, ®_info
[0], ®_info
[1]) != 0) return 0;
448 p
+= sprintf(p
, "%-32s : ", info_type
[info
]);
450 p
= bitregister_process(p
, reg_info
, 128);
452 p
+= sprintf(p
, "\n");
455 if (ia64_pal_rse_info(&phys_stacked
, &hints
) == 0) {
458 "RSE stacked physical registers : %ld\n"
459 "RSE load/store hints : %ld (%s)\n",
460 phys_stacked
, hints
.ph_data
,
461 hints
.ph_data
< RSE_HINTS_COUNT
? rse_hints
[hints
.ph_data
]: "(??)");
463 if (ia64_pal_debug_info(&iregs
, &dregs
))
467 "Instruction debug register pairs : %ld\n"
468 "Data debug register pairs : %ld\n", iregs
, dregs
);
473 static char *proc_features_0
[]={ /* Feature set 0 */
474 NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,
475 NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
, NULL
,NULL
,
476 NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,
477 NULL
,NULL
,NULL
,NULL
,NULL
, NULL
,NULL
,NULL
,NULL
,
478 "Unimplemented instruction address fault",
479 "INIT, PMI, and LINT pins",
480 "Simple unimplemented instr addresses",
481 "Variable P-state performance",
482 "Virtual machine features implemented",
483 "XIP,XPSR,XFS implemented",
484 "XR1-XR3 implemented",
485 "Disable dynamic predicate prediction",
486 "Disable processor physical number",
487 "Disable dynamic data cache prefetch",
488 "Disable dynamic inst cache prefetch",
489 "Disable dynamic branch prediction",
490 NULL
, NULL
, NULL
, NULL
,
492 "Enable MCA on Data Poisoning",
493 "Enable vmsw instruction",
494 "Enable extern environmental notification",
495 "Disable BINIT on processor time-out",
496 "Disable dynamic power management (DPM)",
499 "Enable CMCI promotion",
500 "Enable MCA to BINIT promotion",
501 "Enable MCA promotion",
502 "Enable BERR promotion"
505 static char *proc_features_16
[]={ /* Feature set 16 */
508 "Enable MCA on half-way timer",
511 "Enable Fast Deferral",
512 "Disable MCA on memory aliasing",
514 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
515 "DP system processor",
518 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
519 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
520 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
521 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
522 NULL
, NULL
, NULL
, NULL
, NULL
525 static char **proc_features
[]={
527 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
528 NULL
, NULL
, NULL
, NULL
,
530 NULL
, NULL
, NULL
, NULL
,
534 feature_set_info(char *page
, u64 avail
, u64 status
, u64 control
, u64 set
)
540 vf
= v
= proc_features
[set
];
541 for(i
=0; i
< 64; i
++, avail
>>=1, status
>>=1, control
>>=1) {
543 if (!(control
)) /* No remaining bits set */
545 if (!(avail
& 0x1)) /* Print only bits that are available */
550 p
+= sprintf(p
, "%-40s : %s %s\n", *v
,
551 avail
& 0x1 ? (status
& 0x1 ?
553 avail
& 0x1 ? (control
& 0x1 ?
554 "Ctrl" : "NoCtrl"): "");
556 p
+= sprintf(p
, "Feature set %2ld bit %2d\t\t\t"
559 avail
& 0x1 ? (status
& 0x1 ?
561 avail
& 0x1 ? (control
& 0x1 ?
562 "Ctrl" : "NoCtrl"): "");
569 processor_info(char *page
)
572 u64 avail
=1, status
=1, control
=1, feature_set
=0;
576 ret
= ia64_pal_proc_get_features(&avail
, &status
, &control
,
586 p
= feature_set_info(p
, avail
, status
, control
, feature_set
);
594 static const char *bus_features
[]={
595 NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,
596 NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
, NULL
,NULL
,
597 NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,NULL
,
599 "Request Bus Parking",
601 "Enable Half Transfer",
602 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
603 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
604 NULL
, NULL
, NULL
, NULL
,
605 "Enable Cache Line Repl. Shared",
606 "Enable Cache Line Repl. Exclusive",
607 "Disable Transaction Queuing",
608 "Disable Response Error Checking",
609 "Disable Bus Error Checking",
610 "Disable Bus Requester Internal Error Signalling",
611 "Disable Bus Requester Error Signalling",
612 "Disable Bus Initialization Event Checking",
613 "Disable Bus Initialization Event Signalling",
614 "Disable Bus Address Error Checking",
615 "Disable Bus Address Error Signalling",
616 "Disable Bus Data Error Checking"
624 const char **v
= bus_features
;
625 pal_bus_features_u_t av
, st
, ct
;
626 u64 avail
, status
, control
;
630 if ((ret
=ia64_pal_bus_get_features(&av
, &st
, &ct
)) != 0) return 0;
632 avail
= av
.pal_bus_features_val
;
633 status
= st
.pal_bus_features_val
;
634 control
= ct
.pal_bus_features_val
;
636 for(i
=0; i
< 64; i
++, v
++, avail
>>=1, status
>>=1, control
>>=1) {
637 if ( ! *v
) continue;
638 p
+= sprintf(p
, "%-48s : %s%s %s\n", *v
,
639 avail
& 0x1 ? "" : "NotImpl",
640 avail
& 0x1 ? (status
& 0x1 ? "On" : "Off"): "",
641 avail
& 0x1 ? (control
& 0x1 ? "Ctrl" : "NoCtrl"): "");
647 version_info(char *page
)
649 pal_version_u_t min_ver
, cur_ver
;
652 if (ia64_pal_version(&min_ver
, &cur_ver
) != 0)
656 "PAL_vendor : 0x%02x (min=0x%02x)\n"
657 "PAL_A : %02x.%02x (min=%02x.%02x)\n"
658 "PAL_B : %02x.%02x (min=%02x.%02x)\n",
659 cur_ver
.pal_version_s
.pv_pal_vendor
,
660 min_ver
.pal_version_s
.pv_pal_vendor
,
661 cur_ver
.pal_version_s
.pv_pal_a_model
,
662 cur_ver
.pal_version_s
.pv_pal_a_rev
,
663 min_ver
.pal_version_s
.pv_pal_a_model
,
664 min_ver
.pal_version_s
.pv_pal_a_rev
,
665 cur_ver
.pal_version_s
.pv_pal_b_model
,
666 cur_ver
.pal_version_s
.pv_pal_b_rev
,
667 min_ver
.pal_version_s
.pv_pal_b_model
,
668 min_ver
.pal_version_s
.pv_pal_b_rev
);
673 perfmon_info(char *page
)
677 pal_perf_mon_info_u_t pm_info
;
679 if (ia64_pal_perf_mon_info(pm_buffer
, &pm_info
) != 0) return 0;
682 "PMC/PMD pairs : %d\n"
683 "Counter width : %d bits\n"
684 "Cycle event number : %d\n"
685 "Retired event number : %d\n"
686 "Implemented PMC : ",
687 pm_info
.pal_perf_mon_info_s
.generic
, pm_info
.pal_perf_mon_info_s
.width
,
688 pm_info
.pal_perf_mon_info_s
.cycles
, pm_info
.pal_perf_mon_info_s
.retired
);
690 p
= bitregister_process(p
, pm_buffer
, 256);
691 p
+= sprintf(p
, "\nImplemented PMD : ");
692 p
= bitregister_process(p
, pm_buffer
+4, 256);
693 p
+= sprintf(p
, "\nCycles count capable : ");
694 p
= bitregister_process(p
, pm_buffer
+8, 256);
695 p
+= sprintf(p
, "\nRetired bundles count capable : ");
697 #ifdef CONFIG_ITANIUM
699 * PAL_PERF_MON_INFO reports that only PMC4 can be used to count CPU_CYCLES
700 * which is wrong, both PMC4 and PMD5 support it.
702 if (pm_buffer
[12] == 0x10) pm_buffer
[12]=0x30;
705 p
= bitregister_process(p
, pm_buffer
+12, 256);
707 p
+= sprintf(p
, "\n");
713 frequency_info(char *page
)
716 struct pal_freq_ratio proc
, itc
, bus
;
719 if (ia64_pal_freq_base(&base
) == -1)
720 p
+= sprintf(p
, "Output clock : not implemented\n");
722 p
+= sprintf(p
, "Output clock : %ld ticks/s\n", base
);
724 if (ia64_pal_freq_ratios(&proc
, &bus
, &itc
) != 0) return 0;
727 "Processor/Clock ratio : %d/%d\n"
728 "Bus/Clock ratio : %d/%d\n"
729 "ITC/Clock ratio : %d/%d\n",
730 proc
.num
, proc
.den
, bus
.num
, bus
.den
, itc
.num
, itc
.den
);
740 pal_tr_valid_u_t tr_valid
;
742 pal_vm_info_1_u_t vm_info_1
;
743 pal_vm_info_2_u_t vm_info_2
;
778 if ((status
= ia64_pal_vm_summary(&vm_info_1
, &vm_info_2
)) !=0) {
779 printk(KERN_ERR
"ia64_pal_vm_summary=%ld\n", status
);
782 max
[0] = vm_info_1
.pal_vm_info_1_s
.max_itr_entry
+1;
783 max
[1] = vm_info_1
.pal_vm_info_1_s
.max_dtr_entry
+1;
785 for (i
=0; i
< 2; i
++ ) {
786 for (j
=0; j
< max
[i
]; j
++) {
788 status
= ia64_pal_tr_read(j
, i
, tr_buffer
, &tr_valid
);
790 printk(KERN_ERR
"palinfo: pal call failed on tr[%lu:%lu]=%ld\n",
795 ifa_reg
= (struct ifa_reg
*)&tr_buffer
[2];
797 if (ifa_reg
->valid
== 0) continue;
799 gr_reg
= (struct gr_reg
*)tr_buffer
;
800 itir_reg
= (struct itir_reg
*)&tr_buffer
[1];
801 rid_reg
= (struct rid_reg
*)&tr_buffer
[3];
803 pgm
= -1 << (itir_reg
->ps
- 12);
805 "%cTR%lu: av=%d pv=%d dv=%d mv=%d\n"
810 tr_valid
.pal_tr_valid_s
.access_rights_valid
,
811 tr_valid
.pal_tr_valid_s
.priv_level_valid
,
812 tr_valid
.pal_tr_valid_s
.dirty_bit_valid
,
813 tr_valid
.pal_tr_valid_s
.mem_attr_valid
,
814 (gr_reg
->ppn
& pgm
)<< 12, (ifa_reg
->vpn
& pgm
)<< 12);
816 p
= bitvector_process(p
, 1<< itir_reg
->ps
);
825 gr_reg
->pl
, gr_reg
->ar
, rid_reg
->rid
, gr_reg
->p
, gr_reg
->ma
,
835 * List {name,function} pairs for every entry in /proc/palinfo/cpu*
837 static palinfo_entry_t palinfo_entries
[]={
838 { "version_info", version_info
, },
839 { "vm_info", vm_info
, },
840 { "cache_info", cache_info
, },
841 { "power_info", power_info
, },
842 { "register_info", register_info
, },
843 { "processor_info", processor_info
, },
844 { "perfmon_info", perfmon_info
, },
845 { "frequency_info", frequency_info
, },
846 { "bus_info", bus_info
},
847 { "tr_info", tr_info
, }
850 #define NR_PALINFO_ENTRIES (int) ARRAY_SIZE(palinfo_entries)
853 * this array is used to keep track of the proc entries we create. This is
854 * required in the module mode when we need to remove all entries. The procfs code
855 * does not do recursion of deletion
858 * - +1 accounts for the cpuN directory entry in /proc/pal
860 #define NR_PALINFO_PROC_ENTRIES (NR_CPUS*(NR_PALINFO_ENTRIES+1))
862 static struct proc_dir_entry
*palinfo_proc_entries
[NR_PALINFO_PROC_ENTRIES
];
863 static struct proc_dir_entry
*palinfo_dir
;
866 * This data structure is used to pass which cpu,function is being requested
867 * It must fit in a 64bit quantity to be passed to the proc callback routine
869 * In SMP mode, when we get a request for another CPU, we must call that
870 * other CPU using IPI and wait for the result before returning.
875 unsigned req_cpu
: 32; /* for which CPU this info is */
876 unsigned func_id
: 32; /* which function is requested */
880 #define req_cpu pal_func_cpu.req_cpu
881 #define func_id pal_func_cpu.func_id
886 * used to hold information about final function to call
889 palinfo_func_t func
; /* pointer to function to call */
890 char *page
; /* buffer to store results */
891 int ret
; /* return value from call */
892 } palinfo_smp_data_t
;
896 * this function does the actual final call and he called
897 * from the smp code, i.e., this is the palinfo callback routine
900 palinfo_smp_call(void *info
)
902 palinfo_smp_data_t
*data
= (palinfo_smp_data_t
*)info
;
904 printk(KERN_ERR
"palinfo: data pointer is NULL\n");
905 data
->ret
= 0; /* no output */
908 /* does this actual call */
909 data
->ret
= (*data
->func
)(data
->page
);
913 * function called to trigger the IPI, we need to access a remote CPU
915 * 0 : error or nothing to output
916 * otherwise how many bytes in the "page" buffer were written
919 int palinfo_handle_smp(pal_func_cpu_u_t
*f
, char *page
)
921 palinfo_smp_data_t ptr
;
924 ptr
.func
= palinfo_entries
[f
->func_id
].proc_read
;
926 ptr
.ret
= 0; /* just in case */
929 /* will send IPI to other CPU and wait for completion of remote call */
930 if ((ret
=smp_call_function_single(f
->req_cpu
, palinfo_smp_call
, &ptr
, 0, 1))) {
931 printk(KERN_ERR
"palinfo: remote CPU call from %d to %d on function %d: "
932 "error %d\n", smp_processor_id(), f
->req_cpu
, f
->func_id
, ret
);
937 #else /* ! CONFIG_SMP */
939 int palinfo_handle_smp(pal_func_cpu_u_t
*f
, char *page
)
941 printk(KERN_ERR
"palinfo: should not be called with non SMP kernel\n");
944 #endif /* CONFIG_SMP */
947 * Entry point routine: all calls go through this function
950 palinfo_read_entry(char *page
, char **start
, off_t off
, int count
, int *eof
, void *data
)
953 pal_func_cpu_u_t
*f
= (pal_func_cpu_u_t
*)&data
;
956 * in SMP mode, we may need to call another CPU to get correct
957 * information. PAL, by definition, is processor specific
959 if (f
->req_cpu
== get_cpu())
960 len
= (*palinfo_entries
[f
->func_id
].proc_read
)(page
);
962 len
= palinfo_handle_smp(f
, page
);
966 if (len
<= off
+count
) *eof
= 1;
971 if (len
>count
) len
= count
;
977 static void __cpuinit
978 create_palinfo_proc_entries(unsigned int cpu
)
980 # define CPUSTR "cpu%d"
983 struct proc_dir_entry
**pdir
;
984 struct proc_dir_entry
*cpu_dir
;
986 char cpustr
[sizeof(CPUSTR
)];
990 * we keep track of created entries in a depth-first order for
991 * cleanup purposes. Each entry is stored into palinfo_proc_entries
993 sprintf(cpustr
,CPUSTR
, cpu
);
995 cpu_dir
= proc_mkdir(cpustr
, palinfo_dir
);
1000 * Compute the location to store per cpu entries
1001 * We dont store the top level entry in this list, but
1002 * remove it finally after removing all cpu entries.
1004 pdir
= &palinfo_proc_entries
[cpu
*(NR_PALINFO_ENTRIES
+1)];
1006 for (j
=0; j
< NR_PALINFO_ENTRIES
; j
++) {
1008 *pdir
= create_proc_read_entry(
1009 palinfo_entries
[j
].name
, 0, cpu_dir
,
1010 palinfo_read_entry
, (void *)f
.value
);
1012 (*pdir
)->owner
= THIS_MODULE
;
1018 remove_palinfo_proc_entries(unsigned int hcpu
)
1021 struct proc_dir_entry
*cpu_dir
, **pdir
;
1023 pdir
= &palinfo_proc_entries
[hcpu
*(NR_PALINFO_ENTRIES
+1)];
1026 for (j
=0; j
< (NR_PALINFO_ENTRIES
); j
++) {
1028 remove_proc_entry ((*pdir
)->name
, cpu_dir
);
1034 remove_proc_entry(cpu_dir
->name
, palinfo_dir
);
1038 static int __cpuinit
palinfo_cpu_callback(struct notifier_block
*nfb
,
1039 unsigned long action
, void *hcpu
)
1041 unsigned int hotcpu
= (unsigned long)hcpu
;
1045 case CPU_ONLINE_FROZEN
:
1046 create_palinfo_proc_entries(hotcpu
);
1049 case CPU_DEAD_FROZEN
:
1050 remove_palinfo_proc_entries(hotcpu
);
1056 static struct notifier_block palinfo_cpu_notifier __cpuinitdata
=
1058 .notifier_call
= palinfo_cpu_callback
,
1067 printk(KERN_INFO
"PAL Information Facility v%s\n", PALINFO_VERSION
);
1068 palinfo_dir
= proc_mkdir("pal", NULL
);
1070 /* Create palinfo dirs in /proc for all online cpus */
1071 for_each_online_cpu(i
) {
1072 create_palinfo_proc_entries(i
);
1075 /* Register for future delivery via notify registration */
1076 register_hotcpu_notifier(&palinfo_cpu_notifier
);
1086 /* remove all nodes: depth first pass. Could optimize this */
1087 for_each_online_cpu(i
) {
1088 remove_palinfo_proc_entries(i
);
1092 * Remove the top level entry finally
1094 remove_proc_entry(palinfo_dir
->name
, NULL
);
1097 * Unregister from cpu notifier callbacks
1099 unregister_hotcpu_notifier(&palinfo_cpu_notifier
);
1102 module_init(palinfo_init
);
1103 module_exit(palinfo_exit
);