blob: b6e597860888dd554686682d5481a8163fc7c81b [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 Anderson895309ff2006-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>
David Howellse781c3d2013-04-11 01:28:40 +010025#include <linux/seq_file.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070026#include <linux/mm.h>
27#include <linux/module.h>
28#include <linux/efi.h>
29#include <linux/notifier.h>
30#include <linux/cpu.h>
31#include <linux/cpumask.h>
32
33#include <asm/pal.h>
34#include <asm/sal.h>
35#include <asm/page.h>
36#include <asm/processor.h>
37#include <linux/smp.h>
38
39MODULE_AUTHOR("Stephane Eranian <eranian@hpl.hp.com>");
40MODULE_DESCRIPTION("/proc interface to IA-64 PAL");
41MODULE_LICENSE("GPL");
42
43#define PALINFO_VERSION "0.5"
44
David Howellse781c3d2013-04-11 01:28:40 +010045typedef int (*palinfo_func_t)(struct seq_file *);
Linus Torvalds1da177e2005-04-16 15:20:36 -070046
47typedef struct {
48 const char *name; /* name of the proc entry */
49 palinfo_func_t proc_read; /* function to call for reading */
50 struct proc_dir_entry *entry; /* registered entry (removal) */
51} palinfo_entry_t;
52
53
54/*
55 * A bunch of string array to get pretty printing
56 */
57
David Howellse781c3d2013-04-11 01:28:40 +010058static const char *cache_types[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070059 "", /* not used */
60 "Instruction",
61 "Data",
62 "Data/Instruction" /* unified */
63};
64
65static const char *cache_mattrib[]={
66 "WriteThrough",
67 "WriteBack",
68 "", /* reserved */
69 "" /* reserved */
70};
71
72static const char *cache_st_hints[]={
73 "Temporal, level 1",
74 "Reserved",
75 "Reserved",
76 "Non-temporal, all levels",
77 "Reserved",
78 "Reserved",
79 "Reserved",
80 "Reserved"
81};
82
83static const char *cache_ld_hints[]={
84 "Temporal, level 1",
85 "Non-temporal, level 1",
86 "Reserved",
87 "Non-temporal, all levels",
88 "Reserved",
89 "Reserved",
90 "Reserved",
91 "Reserved"
92};
93
94static const char *rse_hints[]={
95 "enforced lazy",
96 "eager stores",
97 "eager loads",
98 "eager loads and stores"
99};
100
101#define RSE_HINTS_COUNT ARRAY_SIZE(rse_hints)
102
103static const char *mem_attrib[]={
104 "WB", /* 000 */
105 "SW", /* 001 */
106 "010", /* 010 */
107 "011", /* 011 */
108 "UC", /* 100 */
109 "UCE", /* 101 */
110 "WC", /* 110 */
111 "NaTPage" /* 111 */
112};
113
114/*
115 * Take a 64bit vector and produces a string such that
116 * if bit n is set then 2^n in clear text is generated. The adjustment
117 * to the right unit is also done.
118 *
119 * Input:
120 * - a pointer to a buffer to hold the string
121 * - a 64-bit vector
122 * Ouput:
123 * - a pointer to the end of the buffer
124 *
125 */
David Howellse781c3d2013-04-11 01:28:40 +0100126static void bitvector_process(struct seq_file *m, u64 vector)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127{
128 int i,j;
David Howellse781c3d2013-04-11 01:28:40 +0100129 static const char *units[]={ "", "K", "M", "G", "T" };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130
131 for (i=0, j=0; i < 64; i++ , j=i/10) {
David Howellse781c3d2013-04-11 01:28:40 +0100132 if (vector & 0x1)
133 seq_printf(m, "%d%s ", 1 << (i-j*10), units[j]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 vector >>= 1;
135 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136}
137
138/*
139 * Take a 64bit vector and produces a string such that
140 * if bit n is set then register n is present. The function
141 * takes into account consecutive registers and prints out ranges.
142 *
143 * Input:
144 * - a pointer to a buffer to hold the string
145 * - a 64-bit vector
146 * Ouput:
147 * - a pointer to the end of the buffer
148 *
149 */
David Howellse781c3d2013-04-11 01:28:40 +0100150static void bitregister_process(struct seq_file *m, u64 *reg_info, int max)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151{
152 int i, begin, skip = 0;
153 u64 value = reg_info[0];
154
155 value >>= i = begin = ffs(value) - 1;
156
157 for(; i < max; i++ ) {
158
159 if (i != 0 && (i%64) == 0) value = *++reg_info;
160
161 if ((value & 0x1) == 0 && skip == 0) {
162 if (begin <= i - 2)
David Howellse781c3d2013-04-11 01:28:40 +0100163 seq_printf(m, "%d-%d ", begin, i-1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 else
David Howellse781c3d2013-04-11 01:28:40 +0100165 seq_printf(m, "%d ", i-1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 skip = 1;
167 begin = -1;
168 } else if ((value & 0x1) && skip == 1) {
169 skip = 0;
170 begin = i;
171 }
172 value >>=1;
173 }
174 if (begin > -1) {
175 if (begin < 127)
David Howellse781c3d2013-04-11 01:28:40 +0100176 seq_printf(m, "%d-127", begin);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 else
David Howellse781c3d2013-04-11 01:28:40 +0100178 seq_puts(m, "127");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180}
181
David Howellse781c3d2013-04-11 01:28:40 +0100182static int power_info(struct seq_file *m)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183{
184 s64 status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 u64 halt_info_buffer[8];
186 pal_power_mgmt_info_u_t *halt_info =(pal_power_mgmt_info_u_t *)halt_info_buffer;
187 int i;
188
189 status = ia64_pal_halt_info(halt_info);
190 if (status != 0) return 0;
191
192 for (i=0; i < 8 ; i++ ) {
193 if (halt_info[i].pal_power_mgmt_info_s.im == 1) {
David Howellse781c3d2013-04-11 01:28:40 +0100194 seq_printf(m,
195 "Power level %d:\n"
196 "\tentry_latency : %d cycles\n"
197 "\texit_latency : %d cycles\n"
198 "\tpower consumption : %d mW\n"
199 "\tCache+TLB coherency : %s\n", i,
200 halt_info[i].pal_power_mgmt_info_s.entry_latency,
201 halt_info[i].pal_power_mgmt_info_s.exit_latency,
202 halt_info[i].pal_power_mgmt_info_s.power_consumption,
203 halt_info[i].pal_power_mgmt_info_s.co ? "Yes" : "No");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 } else {
David Howellse781c3d2013-04-11 01:28:40 +0100205 seq_printf(m,"Power level %d: not implemented\n", i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206 }
207 }
David Howellse781c3d2013-04-11 01:28:40 +0100208 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209}
210
David Howellse781c3d2013-04-11 01:28:40 +0100211static int cache_info(struct seq_file *m)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212{
Matthew Wilcoxe088a4a2009-05-22 13:49:49 -0700213 unsigned long i, levels, unique_caches;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 pal_cache_config_info_t cci;
215 int j, k;
Matthew Wilcoxe088a4a2009-05-22 13:49:49 -0700216 long status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217
218 if ((status = ia64_pal_cache_summary(&levels, &unique_caches)) != 0) {
219 printk(KERN_ERR "ia64_pal_cache_summary=%ld\n", status);
220 return 0;
221 }
222
David Howellse781c3d2013-04-11 01:28:40 +0100223 seq_printf(m, "Cache levels : %ld\nUnique caches : %ld\n\n",
224 levels, unique_caches);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225
226 for (i=0; i < levels; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 for (j=2; j >0 ; j--) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228 /* even without unification some level may not be present */
David Howellse781c3d2013-04-11 01:28:40 +0100229 if ((status=ia64_pal_cache_config_info(i,j, &cci)) != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231
David Howellse781c3d2013-04-11 01:28:40 +0100232 seq_printf(m,
233 "%s Cache level %lu:\n"
234 "\tSize : %u bytes\n"
235 "\tAttributes : ",
236 cache_types[j+cci.pcci_unified], i+1,
237 cci.pcci_cache_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238
David Howellse781c3d2013-04-11 01:28:40 +0100239 if (cci.pcci_unified)
240 seq_puts(m, "Unified ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241
David Howellse781c3d2013-04-11 01:28:40 +0100242 seq_printf(m, "%s\n", cache_mattrib[cci.pcci_cache_attr]);
243
244 seq_printf(m,
245 "\tAssociativity : %d\n"
246 "\tLine size : %d bytes\n"
247 "\tStride : %d bytes\n",
248 cci.pcci_assoc,
249 1<<cci.pcci_line_size,
250 1<<cci.pcci_stride);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 if (j == 1)
David Howellse781c3d2013-04-11 01:28:40 +0100252 seq_puts(m, "\tStore latency : N/A\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253 else
David Howellse781c3d2013-04-11 01:28:40 +0100254 seq_printf(m, "\tStore latency : %d cycle(s)\n",
255 cci.pcci_st_latency);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256
David Howellse781c3d2013-04-11 01:28:40 +0100257 seq_printf(m,
258 "\tLoad latency : %d cycle(s)\n"
259 "\tStore hints : ", cci.pcci_ld_latency);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260
261 for(k=0; k < 8; k++ ) {
262 if ( cci.pcci_st_hints & 0x1)
David Howellse781c3d2013-04-11 01:28:40 +0100263 seq_printf(m, "[%s]", cache_st_hints[k]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 cci.pcci_st_hints >>=1;
265 }
David Howellse781c3d2013-04-11 01:28:40 +0100266 seq_puts(m, "\n\tLoad hints : ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267
268 for(k=0; k < 8; k++ ) {
269 if (cci.pcci_ld_hints & 0x1)
David Howellse781c3d2013-04-11 01:28:40 +0100270 seq_printf(m, "[%s]", cache_ld_hints[k]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271 cci.pcci_ld_hints >>=1;
272 }
David Howellse781c3d2013-04-11 01:28:40 +0100273 seq_printf(m,
274 "\n\tAlias boundary : %d byte(s)\n"
275 "\tTag LSB : %d\n"
276 "\tTag MSB : %d\n",
277 1<<cci.pcci_alias_boundary, cci.pcci_tag_lsb,
278 cci.pcci_tag_msb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279
280 /* when unified, data(j=2) is enough */
David Howellse781c3d2013-04-11 01:28:40 +0100281 if (cci.pcci_unified)
282 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283 }
284 }
David Howellse781c3d2013-04-11 01:28:40 +0100285 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286}
287
288
David Howellse781c3d2013-04-11 01:28:40 +0100289static int vm_info(struct seq_file *m)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 u64 tr_pages =0, vw_pages=0, tc_pages;
292 u64 attrib;
293 pal_vm_info_1_u_t vm_info_1;
294 pal_vm_info_2_u_t vm_info_2;
295 pal_tc_info_u_t tc_info;
296 ia64_ptce_info_t ptce;
297 const char *sep;
298 int i, j;
Matthew Wilcoxe088a4a2009-05-22 13:49:49 -0700299 long status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300
301 if ((status = ia64_pal_vm_summary(&vm_info_1, &vm_info_2)) !=0) {
302 printk(KERN_ERR "ia64_pal_vm_summary=%ld\n", status);
Peter Chubb714d2dc2005-08-25 17:39:00 -0700303 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304
David Howellse781c3d2013-04-11 01:28:40 +0100305 seq_printf(m,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 "Physical Address Space : %d bits\n"
307 "Virtual Address Space : %d bits\n"
308 "Protection Key Registers(PKR) : %d\n"
309 "Implemented bits in PKR.key : %d\n"
310 "Hash Tag ID : 0x%x\n"
Russ Anderson5b4d5682006-11-06 16:45:18 -0600311 "Size of RR.rid : %d\n"
312 "Max Purges : ",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 vm_info_1.pal_vm_info_1_s.phys_add_size,
Peter Chubb714d2dc2005-08-25 17:39:00 -0700314 vm_info_2.pal_vm_info_2_s.impl_va_msb+1,
315 vm_info_1.pal_vm_info_1_s.max_pkr+1,
316 vm_info_1.pal_vm_info_1_s.key_size,
317 vm_info_1.pal_vm_info_1_s.hash_tag_id,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 vm_info_2.pal_vm_info_2_s.rid_size);
Russ Anderson5b4d5682006-11-06 16:45:18 -0600319 if (vm_info_2.pal_vm_info_2_s.max_purges == PAL_MAX_PURGES)
David Howellse781c3d2013-04-11 01:28:40 +0100320 seq_puts(m, "unlimited\n");
Russ Anderson5b4d5682006-11-06 16:45:18 -0600321 else
David Howellse781c3d2013-04-11 01:28:40 +0100322 seq_printf(m, "%d\n",
Russ Anderson5b4d5682006-11-06 16:45:18 -0600323 vm_info_2.pal_vm_info_2_s.max_purges ?
324 vm_info_2.pal_vm_info_2_s.max_purges : 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 }
Peter Chubb714d2dc2005-08-25 17:39:00 -0700326
327 if (ia64_pal_mem_attrib(&attrib) == 0) {
David Howellse781c3d2013-04-11 01:28:40 +0100328 seq_puts(m, "Supported memory attributes : ");
Peter Chubb714d2dc2005-08-25 17:39:00 -0700329 sep = "";
330 for (i = 0; i < 8; i++) {
331 if (attrib & (1 << i)) {
David Howellse781c3d2013-04-11 01:28:40 +0100332 seq_printf(m, "%s%s", sep, mem_attrib[i]);
Peter Chubb714d2dc2005-08-25 17:39:00 -0700333 sep = ", ";
334 }
335 }
David Howellse781c3d2013-04-11 01:28:40 +0100336 seq_putc(m, '\n');
Peter Chubb714d2dc2005-08-25 17:39:00 -0700337 }
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
David Howellse781c3d2013-04-11 01:28:40 +0100343 seq_printf(m,
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);
Peter Chubb714d2dc2005-08-25 17:39:00 -0700351
David Howellse781c3d2013-04-11 01:28:40 +0100352 bitvector_process(m, tr_pages);
Peter Chubb714d2dc2005-08-25 17:39:00 -0700353
David Howellse781c3d2013-04-11 01:28:40 +0100354 seq_puts(m, "\nTLB purgeable page sizes : ");
Peter Chubb714d2dc2005-08-25 17:39:00 -0700355
David Howellse781c3d2013-04-11 01:28:40 +0100356 bitvector_process(m, vw_pages);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 }
David Howellse781c3d2013-04-11 01:28:40 +0100358
359 if ((status = ia64_get_ptce(&ptce)) != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 printk(KERN_ERR "ia64_get_ptce=%ld\n", status);
Peter Chubb714d2dc2005-08-25 17:39:00 -0700361 } else {
David Howellse781c3d2013-04-11 01:28:40 +0100362 seq_printf(m,
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
David Howellse781c3d2013-04-11 01:28:40 +0100371 seq_printf(m,
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
Peter Chubb714d2dc2005-08-25 17:39:00 -0700381 /* even without unification, some levels may not be present */
David Howellse781c3d2013-04-11 01:28:40 +0100382 if ((status=ia64_pal_vm_info(i,j, &tc_info, &tc_pages)) != 0)
Peter Chubb714d2dc2005-08-25 17:39:00 -0700383 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384
David Howellse781c3d2013-04-11 01:28:40 +0100385 seq_printf(m,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 "\n%s Translation Cache Level %d:\n"
387 "\tHash sets : %d\n"
388 "\tAssociativity : %d\n"
389 "\tNumber of entries : %d\n"
390 "\tFlags : ",
Peter Chubb714d2dc2005-08-25 17:39:00 -0700391 cache_types[j+tc_info.tc_unified], i+1,
392 tc_info.tc_num_sets,
393 tc_info.tc_associativity,
394 tc_info.tc_num_entries);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395
Peter Chubb714d2dc2005-08-25 17:39:00 -0700396 if (tc_info.tc_pf)
David Howellse781c3d2013-04-11 01:28:40 +0100397 seq_puts(m, "PreferredPageSizeOptimized ");
Peter Chubb714d2dc2005-08-25 17:39:00 -0700398 if (tc_info.tc_unified)
David Howellse781c3d2013-04-11 01:28:40 +0100399 seq_puts(m, "Unified ");
Peter Chubb714d2dc2005-08-25 17:39:00 -0700400 if (tc_info.tc_reduce_tr)
David Howellse781c3d2013-04-11 01:28:40 +0100401 seq_puts(m, "TCReduction");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402
David Howellse781c3d2013-04-11 01:28:40 +0100403 seq_puts(m, "\n\tSupported page sizes: ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404
David Howellse781c3d2013-04-11 01:28:40 +0100405 bitvector_process(m, tc_pages);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406
Peter Chubb714d2dc2005-08-25 17:39:00 -0700407 /* when unified date (j=2) is enough */
408 if (tc_info.tc_unified)
409 break;
410 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 }
412 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413
David Howellse781c3d2013-04-11 01:28:40 +0100414 seq_putc(m, '\n');
415 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416}
417
418
David Howellse781c3d2013-04-11 01:28:40 +0100419static int register_info(struct seq_file *m)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421 u64 reg_info[2];
422 u64 info;
Matthew Wilcoxe088a4a2009-05-22 13:49:49 -0700423 unsigned long phys_stacked;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424 pal_hints_u_t hints;
Matthew Wilcoxe088a4a2009-05-22 13:49:49 -0700425 unsigned long iregs, dregs;
Joe Perchesc2164882010-09-13 21:23:48 -0700426 static const char * const info_type[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 "Implemented AR(s)",
428 "AR(s) with read side-effects",
429 "Implemented CR(s)",
430 "CR(s) with read side-effects",
431 };
432
433 for(info=0; info < 4; info++) {
David Howellse781c3d2013-04-11 01:28:40 +0100434 if (ia64_pal_register_info(info, &reg_info[0], &reg_info[1]) != 0)
435 return 0;
436 seq_printf(m, "%-32s : ", info_type[info]);
437 bitregister_process(m, reg_info, 128);
438 seq_putc(m, '\n');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 }
440
David Howellse781c3d2013-04-11 01:28:40 +0100441 if (ia64_pal_rse_info(&phys_stacked, &hints) == 0)
442 seq_printf(m,
443 "RSE stacked physical registers : %ld\n"
444 "RSE load/store hints : %ld (%s)\n",
445 phys_stacked, hints.ph_data,
446 hints.ph_data < RSE_HINTS_COUNT ? rse_hints[hints.ph_data]: "(??)");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448 if (ia64_pal_debug_info(&iregs, &dregs))
449 return 0;
450
David Howellse781c3d2013-04-11 01:28:40 +0100451 seq_printf(m,
452 "Instruction debug register pairs : %ld\n"
453 "Data debug register pairs : %ld\n", iregs, dregs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454
David Howellse781c3d2013-04-11 01:28:40 +0100455 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456}
457
David Howellse781c3d2013-04-11 01:28:40 +0100458static const char *const proc_features_0[]={ /* Feature set 0 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
460 NULL,NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,
461 NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
462 NULL,NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,
Russ Anderson895309ff2006-12-07 11:06:35 -0800463 "Unimplemented instruction address fault",
464 "INIT, PMI, and LINT pins",
465 "Simple unimplemented instr addresses",
466 "Variable P-state performance",
467 "Virtual machine features implemented",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 "XIP,XPSR,XFS implemented",
469 "XR1-XR3 implemented",
470 "Disable dynamic predicate prediction",
471 "Disable processor physical number",
472 "Disable dynamic data cache prefetch",
473 "Disable dynamic inst cache prefetch",
474 "Disable dynamic branch prediction",
Russ Anderson895309ff2006-12-07 11:06:35 -0800475 NULL, NULL, NULL, NULL,
476 "Disable P-states",
477 "Enable MCA on Data Poisoning",
478 "Enable vmsw instruction",
479 "Enable extern environmental notification",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 "Disable BINIT on processor time-out",
481 "Disable dynamic power management (DPM)",
482 "Disable coherency",
483 "Disable cache",
484 "Enable CMCI promotion",
485 "Enable MCA to BINIT promotion",
486 "Enable MCA promotion",
487 "Enable BERR promotion"
488};
489
David Howellse781c3d2013-04-11 01:28:40 +0100490static const char *const proc_features_16[]={ /* Feature set 16 */
Russ Andersonb8de4712007-10-16 17:02:38 -0500491 "Disable ETM",
492 "Enable ETM",
493 "Enable MCA on half-way timer",
494 "Enable snoop WC",
495 NULL,
496 "Enable Fast Deferral",
497 "Disable MCA on memory aliasing",
498 "Enable RSB",
499 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
500 "DP system processor",
501 "Low Voltage",
502 "HT supported",
503 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
504 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
505 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
506 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
507 NULL, NULL, NULL, NULL, NULL
508};
509
David Howellse781c3d2013-04-11 01:28:40 +0100510static const char *const *const proc_features[]={
Russ Andersonb8de4712007-10-16 17:02:38 -0500511 proc_features_0,
512 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
513 NULL, NULL, NULL, NULL,
514 proc_features_16,
515 NULL, NULL, NULL, NULL,
516};
517
David Howellse781c3d2013-04-11 01:28:40 +0100518static void feature_set_info(struct seq_file *m, u64 avail, u64 status, u64 control,
519 unsigned long set)
Russ Andersonb8de4712007-10-16 17:02:38 -0500520{
David Howellse781c3d2013-04-11 01:28:40 +0100521 const char *const *vf, *const *v;
Russ Andersonb8de4712007-10-16 17:02:38 -0500522 int i;
523
524 vf = v = proc_features[set];
525 for(i=0; i < 64; i++, avail >>=1, status >>=1, control >>=1) {
526
527 if (!(control)) /* No remaining bits set */
528 break;
529 if (!(avail & 0x1)) /* Print only bits that are available */
530 continue;
531 if (vf)
532 v = vf + i;
533 if ( v && *v ) {
David Howellse781c3d2013-04-11 01:28:40 +0100534 seq_printf(m, "%-40s : %s %s\n", *v,
Russ Andersonb8de4712007-10-16 17:02:38 -0500535 avail & 0x1 ? (status & 0x1 ?
David Howellse781c3d2013-04-11 01:28:40 +0100536 "On " : "Off"): "",
Russ Andersonb8de4712007-10-16 17:02:38 -0500537 avail & 0x1 ? (control & 0x1 ?
538 "Ctrl" : "NoCtrl"): "");
539 } else {
David Howellse781c3d2013-04-11 01:28:40 +0100540 seq_printf(m, "Feature set %2ld bit %2d\t\t\t"
Russ Andersonb8de4712007-10-16 17:02:38 -0500541 " : %s %s\n",
542 set, i,
543 avail & 0x1 ? (status & 0x1 ?
544 "On " : "Off"): "",
545 avail & 0x1 ? (control & 0x1 ?
546 "Ctrl" : "NoCtrl"): "");
547 }
548 }
Russ Andersonb8de4712007-10-16 17:02:38 -0500549}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550
David Howellse781c3d2013-04-11 01:28:40 +0100551static int processor_info(struct seq_file *m)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552{
Russ Andersonb8de4712007-10-16 17:02:38 -0500553 u64 avail=1, status=1, control=1, feature_set=0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 s64 ret;
555
Russ Andersonb8de4712007-10-16 17:02:38 -0500556 do {
557 ret = ia64_pal_proc_get_features(&avail, &status, &control,
558 feature_set);
David Howellse781c3d2013-04-11 01:28:40 +0100559 if (ret < 0)
560 return 0;
561
Russ Andersonb8de4712007-10-16 17:02:38 -0500562 if (ret == 1) {
563 feature_set++;
564 continue;
565 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566
David Howellse781c3d2013-04-11 01:28:40 +0100567 feature_set_info(m, avail, status, control, feature_set);
Russ Andersonb8de4712007-10-16 17:02:38 -0500568 feature_set++;
569 } while(1);
570
David Howellse781c3d2013-04-11 01:28:40 +0100571 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572}
573
David Howellse781c3d2013-04-11 01:28:40 +0100574static const char *const bus_features[]={
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
576 NULL,NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,
577 NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
578 NULL,NULL,
579 "Request Bus Parking",
580 "Bus Lock Mask",
581 "Enable Half Transfer",
582 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
583 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
584 NULL, NULL, NULL, NULL,
585 "Enable Cache Line Repl. Shared",
586 "Enable Cache Line Repl. Exclusive",
587 "Disable Transaction Queuing",
588 "Disable Response Error Checking",
589 "Disable Bus Error Checking",
590 "Disable Bus Requester Internal Error Signalling",
591 "Disable Bus Requester Error Signalling",
592 "Disable Bus Initialization Event Checking",
593 "Disable Bus Initialization Event Signalling",
594 "Disable Bus Address Error Checking",
595 "Disable Bus Address Error Signalling",
596 "Disable Bus Data Error Checking"
597};
598
599
David Howellse781c3d2013-04-11 01:28:40 +0100600static int bus_info(struct seq_file *m)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601{
David Howellse781c3d2013-04-11 01:28:40 +0100602 const char *const *v = bus_features;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 pal_bus_features_u_t av, st, ct;
604 u64 avail, status, control;
605 int i;
606 s64 ret;
607
David Howellse781c3d2013-04-11 01:28:40 +0100608 if ((ret=ia64_pal_bus_get_features(&av, &st, &ct)) != 0)
609 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610
611 avail = av.pal_bus_features_val;
612 status = st.pal_bus_features_val;
613 control = ct.pal_bus_features_val;
614
615 for(i=0; i < 64; i++, v++, avail >>=1, status >>=1, control >>=1) {
David Howellse781c3d2013-04-11 01:28:40 +0100616 if ( ! *v )
617 continue;
618 seq_printf(m, "%-48s : %s%s %s\n", *v,
619 avail & 0x1 ? "" : "NotImpl",
620 avail & 0x1 ? (status & 0x1 ? "On" : "Off"): "",
621 avail & 0x1 ? (control & 0x1 ? "Ctrl" : "NoCtrl"): "");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 }
David Howellse781c3d2013-04-11 01:28:40 +0100623 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624}
625
David Howellse781c3d2013-04-11 01:28:40 +0100626static int version_info(struct seq_file *m)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627{
628 pal_version_u_t min_ver, cur_ver;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629
Matthew Wilcox1bf1eba2006-06-23 13:15:55 -0600630 if (ia64_pal_version(&min_ver, &cur_ver) != 0)
631 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632
David Howellse781c3d2013-04-11 01:28:40 +0100633 seq_printf(m,
634 "PAL_vendor : 0x%02x (min=0x%02x)\n"
635 "PAL_A : %02x.%02x (min=%02x.%02x)\n"
636 "PAL_B : %02x.%02x (min=%02x.%02x)\n",
637 cur_ver.pal_version_s.pv_pal_vendor,
638 min_ver.pal_version_s.pv_pal_vendor,
639 cur_ver.pal_version_s.pv_pal_a_model,
640 cur_ver.pal_version_s.pv_pal_a_rev,
641 min_ver.pal_version_s.pv_pal_a_model,
642 min_ver.pal_version_s.pv_pal_a_rev,
643 cur_ver.pal_version_s.pv_pal_b_model,
644 cur_ver.pal_version_s.pv_pal_b_rev,
645 min_ver.pal_version_s.pv_pal_b_model,
646 min_ver.pal_version_s.pv_pal_b_rev);
647 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648}
649
David Howellse781c3d2013-04-11 01:28:40 +0100650static int perfmon_info(struct seq_file *m)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652 u64 pm_buffer[16];
653 pal_perf_mon_info_u_t pm_info;
654
David Howellse781c3d2013-04-11 01:28:40 +0100655 if (ia64_pal_perf_mon_info(pm_buffer, &pm_info) != 0)
656 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657
David Howellse781c3d2013-04-11 01:28:40 +0100658 seq_printf(m,
659 "PMC/PMD pairs : %d\n"
660 "Counter width : %d bits\n"
661 "Cycle event number : %d\n"
662 "Retired event number : %d\n"
663 "Implemented PMC : ",
664 pm_info.pal_perf_mon_info_s.generic,
665 pm_info.pal_perf_mon_info_s.width,
666 pm_info.pal_perf_mon_info_s.cycles,
667 pm_info.pal_perf_mon_info_s.retired);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668
David Howellse781c3d2013-04-11 01:28:40 +0100669 bitregister_process(m, pm_buffer, 256);
670 seq_puts(m, "\nImplemented PMD : ");
671 bitregister_process(m, pm_buffer+4, 256);
672 seq_puts(m, "\nCycles count capable : ");
673 bitregister_process(m, pm_buffer+8, 256);
674 seq_puts(m, "\nRetired bundles count capable : ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675
676#ifdef CONFIG_ITANIUM
677 /*
678 * PAL_PERF_MON_INFO reports that only PMC4 can be used to count CPU_CYCLES
679 * which is wrong, both PMC4 and PMD5 support it.
680 */
David Howellse781c3d2013-04-11 01:28:40 +0100681 if (pm_buffer[12] == 0x10)
682 pm_buffer[12]=0x30;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683#endif
684
David Howellse781c3d2013-04-11 01:28:40 +0100685 bitregister_process(m, pm_buffer+12, 256);
686 seq_putc(m, '\n');
687 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688}
689
David Howellse781c3d2013-04-11 01:28:40 +0100690static int frequency_info(struct seq_file *m)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692 struct pal_freq_ratio proc, itc, bus;
Matthew Wilcoxe088a4a2009-05-22 13:49:49 -0700693 unsigned long base;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694
695 if (ia64_pal_freq_base(&base) == -1)
David Howellse781c3d2013-04-11 01:28:40 +0100696 seq_puts(m, "Output clock : not implemented\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697 else
David Howellse781c3d2013-04-11 01:28:40 +0100698 seq_printf(m, "Output clock : %ld ticks/s\n", base);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699
700 if (ia64_pal_freq_ratios(&proc, &bus, &itc) != 0) return 0;
701
David Howellse781c3d2013-04-11 01:28:40 +0100702 seq_printf(m,
Tony Luck2ab93912006-03-31 10:28:29 -0800703 "Processor/Clock ratio : %d/%d\n"
704 "Bus/Clock ratio : %d/%d\n"
705 "ITC/Clock ratio : %d/%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 proc.num, proc.den, bus.num, bus.den, itc.num, itc.den);
David Howellse781c3d2013-04-11 01:28:40 +0100707 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708}
709
David Howellse781c3d2013-04-11 01:28:40 +0100710static int tr_info(struct seq_file *m)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711{
Matthew Wilcoxe088a4a2009-05-22 13:49:49 -0700712 long status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 pal_tr_valid_u_t tr_valid;
714 u64 tr_buffer[4];
715 pal_vm_info_1_u_t vm_info_1;
716 pal_vm_info_2_u_t vm_info_2;
Matthew Wilcoxe088a4a2009-05-22 13:49:49 -0700717 unsigned long i, j;
718 unsigned long max[3], pgm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719 struct ifa_reg {
Matthew Wilcoxe088a4a2009-05-22 13:49:49 -0700720 unsigned long valid:1;
721 unsigned long ig:11;
722 unsigned long vpn:52;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 } *ifa_reg;
724 struct itir_reg {
Matthew Wilcoxe088a4a2009-05-22 13:49:49 -0700725 unsigned long rv1:2;
726 unsigned long ps:6;
727 unsigned long key:24;
728 unsigned long rv2:32;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 } *itir_reg;
730 struct gr_reg {
Matthew Wilcoxe088a4a2009-05-22 13:49:49 -0700731 unsigned long p:1;
732 unsigned long rv1:1;
733 unsigned long ma:3;
734 unsigned long a:1;
735 unsigned long d:1;
736 unsigned long pl:2;
737 unsigned long ar:3;
738 unsigned long ppn:38;
739 unsigned long rv2:2;
740 unsigned long ed:1;
741 unsigned long ig:11;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742 } *gr_reg;
743 struct rid_reg {
Matthew Wilcoxe088a4a2009-05-22 13:49:49 -0700744 unsigned long ig1:1;
745 unsigned long rv1:1;
746 unsigned long ig2:6;
747 unsigned long rid:24;
748 unsigned long rv2:32;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 } *rid_reg;
750
751 if ((status = ia64_pal_vm_summary(&vm_info_1, &vm_info_2)) !=0) {
752 printk(KERN_ERR "ia64_pal_vm_summary=%ld\n", status);
753 return 0;
754 }
755 max[0] = vm_info_1.pal_vm_info_1_s.max_itr_entry+1;
756 max[1] = vm_info_1.pal_vm_info_1_s.max_dtr_entry+1;
757
758 for (i=0; i < 2; i++ ) {
759 for (j=0; j < max[i]; j++) {
760
761 status = ia64_pal_tr_read(j, i, tr_buffer, &tr_valid);
762 if (status != 0) {
763 printk(KERN_ERR "palinfo: pal call failed on tr[%lu:%lu]=%ld\n",
764 i, j, status);
765 continue;
766 }
767
768 ifa_reg = (struct ifa_reg *)&tr_buffer[2];
769
David Howellse781c3d2013-04-11 01:28:40 +0100770 if (ifa_reg->valid == 0)
771 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772
773 gr_reg = (struct gr_reg *)tr_buffer;
774 itir_reg = (struct itir_reg *)&tr_buffer[1];
775 rid_reg = (struct rid_reg *)&tr_buffer[3];
776
777 pgm = -1 << (itir_reg->ps - 12);
David Howellse781c3d2013-04-11 01:28:40 +0100778 seq_printf(m,
779 "%cTR%lu: av=%d pv=%d dv=%d mv=%d\n"
780 "\tppn : 0x%lx\n"
781 "\tvpn : 0x%lx\n"
782 "\tps : ",
783 "ID"[i], j,
784 tr_valid.pal_tr_valid_s.access_rights_valid,
785 tr_valid.pal_tr_valid_s.priv_level_valid,
786 tr_valid.pal_tr_valid_s.dirty_bit_valid,
787 tr_valid.pal_tr_valid_s.mem_attr_valid,
788 (gr_reg->ppn & pgm)<< 12, (ifa_reg->vpn & pgm)<< 12);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789
David Howellse781c3d2013-04-11 01:28:40 +0100790 bitvector_process(m, 1<< itir_reg->ps);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791
David Howellse781c3d2013-04-11 01:28:40 +0100792 seq_printf(m,
793 "\n\tpl : %d\n"
794 "\tar : %d\n"
795 "\trid : %x\n"
796 "\tp : %d\n"
797 "\tma : %d\n"
798 "\td : %d\n",
799 gr_reg->pl, gr_reg->ar, rid_reg->rid, gr_reg->p, gr_reg->ma,
800 gr_reg->d);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 }
802 }
David Howellse781c3d2013-04-11 01:28:40 +0100803 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804}
805
806
807
808/*
809 * List {name,function} pairs for every entry in /proc/palinfo/cpu*
810 */
David Howellse781c3d2013-04-11 01:28:40 +0100811static const palinfo_entry_t palinfo_entries[]={
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 { "version_info", version_info, },
813 { "vm_info", vm_info, },
814 { "cache_info", cache_info, },
815 { "power_info", power_info, },
816 { "register_info", register_info, },
817 { "processor_info", processor_info, },
818 { "perfmon_info", perfmon_info, },
819 { "frequency_info", frequency_info, },
820 { "bus_info", bus_info },
821 { "tr_info", tr_info, }
822};
823
824#define NR_PALINFO_ENTRIES (int) ARRAY_SIZE(palinfo_entries)
825
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826static struct proc_dir_entry *palinfo_dir;
827
828/*
829 * This data structure is used to pass which cpu,function is being requested
830 * It must fit in a 64bit quantity to be passed to the proc callback routine
831 *
832 * In SMP mode, when we get a request for another CPU, we must call that
833 * other CPU using IPI and wait for the result before returning.
834 */
835typedef union {
836 u64 value;
837 struct {
838 unsigned req_cpu: 32; /* for which CPU this info is */
839 unsigned func_id: 32; /* which function is requested */
840 } pal_func_cpu;
841} pal_func_cpu_u_t;
842
843#define req_cpu pal_func_cpu.req_cpu
844#define func_id pal_func_cpu.func_id
845
846#ifdef CONFIG_SMP
847
848/*
849 * used to hold information about final function to call
850 */
851typedef struct {
852 palinfo_func_t func; /* pointer to function to call */
David Howellse781c3d2013-04-11 01:28:40 +0100853 struct seq_file *m; /* buffer to store results */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854 int ret; /* return value from call */
855} palinfo_smp_data_t;
856
857
858/*
859 * this function does the actual final call and he called
860 * from the smp code, i.e., this is the palinfo callback routine
861 */
862static void
863palinfo_smp_call(void *info)
864{
865 palinfo_smp_data_t *data = (palinfo_smp_data_t *)info;
David Howellse781c3d2013-04-11 01:28:40 +0100866 data->ret = (*data->func)(data->m);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867}
868
869/*
870 * function called to trigger the IPI, we need to access a remote CPU
871 * Return:
872 * 0 : error or nothing to output
873 * otherwise how many bytes in the "page" buffer were written
874 */
875static
David Howellse781c3d2013-04-11 01:28:40 +0100876int palinfo_handle_smp(struct seq_file *m, pal_func_cpu_u_t *f)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877{
878 palinfo_smp_data_t ptr;
879 int ret;
880
881 ptr.func = palinfo_entries[f->func_id].proc_read;
David Howellse781c3d2013-04-11 01:28:40 +0100882 ptr.m = m;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883 ptr.ret = 0; /* just in case */
884
885
886 /* will send IPI to other CPU and wait for completion of remote call */
Jens Axboe8691e5a2008-06-06 11:18:06 +0200887 if ((ret=smp_call_function_single(f->req_cpu, palinfo_smp_call, &ptr, 1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 printk(KERN_ERR "palinfo: remote CPU call from %d to %d on function %d: "
889 "error %d\n", smp_processor_id(), f->req_cpu, f->func_id, ret);
890 return 0;
891 }
892 return ptr.ret;
893}
894#else /* ! CONFIG_SMP */
895static
David Howellse781c3d2013-04-11 01:28:40 +0100896int palinfo_handle_smp(struct seq_file *m, pal_func_cpu_u_t *f)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897{
898 printk(KERN_ERR "palinfo: should not be called with non SMP kernel\n");
899 return 0;
900}
901#endif /* CONFIG_SMP */
902
903/*
904 * Entry point routine: all calls go through this function
905 */
David Howellse781c3d2013-04-11 01:28:40 +0100906static int proc_palinfo_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907{
David Howellse781c3d2013-04-11 01:28:40 +0100908 pal_func_cpu_u_t *f = (pal_func_cpu_u_t *)&m->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909
910 /*
911 * in SMP mode, we may need to call another CPU to get correct
912 * information. PAL, by definition, is processor specific
913 */
914 if (f->req_cpu == get_cpu())
David Howellse781c3d2013-04-11 01:28:40 +0100915 (*palinfo_entries[f->func_id].proc_read)(m);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916 else
David Howellse781c3d2013-04-11 01:28:40 +0100917 palinfo_handle_smp(m, f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918
919 put_cpu();
David Howellse781c3d2013-04-11 01:28:40 +0100920 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921}
922
David Howellse781c3d2013-04-11 01:28:40 +0100923static int proc_palinfo_open(struct inode *inode, struct file *file)
924{
925 return single_open(file, proc_palinfo_show, PDE_DATA(inode));
926}
927
928static const struct file_operations proc_palinfo_fops = {
929 .open = proc_palinfo_open,
930 .read = seq_read,
931 .llseek = seq_lseek,
Al Viro75401462013-05-05 00:09:04 -0400932 .release = single_release,
David Howellse781c3d2013-04-11 01:28:40 +0100933};
934
Sebastian Andrzej Siewior715c3212016-11-03 15:50:11 +0100935static int palinfo_add_proc(unsigned int cpu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937 pal_func_cpu_u_t f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938 struct proc_dir_entry *cpu_dir;
939 int j;
Al Viroccf93202013-03-31 22:34:37 -0400940 char cpustr[3+4+1]; /* cpu numbers are up to 4095 on itanic */
941 sprintf(cpustr, "cpu%d", cpu);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942
943 cpu_dir = proc_mkdir(cpustr, palinfo_dir);
Al Viroccf93202013-03-31 22:34:37 -0400944 if (!cpu_dir)
Sebastian Andrzej Siewior715c3212016-11-03 15:50:11 +0100945 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946
947 f.req_cpu = cpu;
948
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949 for (j=0; j < NR_PALINFO_ENTRIES; j++) {
950 f.func_id = j;
David Howellse781c3d2013-04-11 01:28:40 +0100951 proc_create_data(palinfo_entries[j].name, 0, cpu_dir,
952 &proc_palinfo_fops, (void *)f.value);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953 }
Sebastian Andrzej Siewior715c3212016-11-03 15:50:11 +0100954 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955}
956
Sebastian Andrzej Siewior715c3212016-11-03 15:50:11 +0100957static int palinfo_del_proc(unsigned int hcpu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958{
Al Viroccf93202013-03-31 22:34:37 -0400959 char cpustr[3+4+1]; /* cpu numbers are up to 4095 on itanic */
Sebastian Andrzej Siewior715c3212016-11-03 15:50:11 +0100960
Al Viroccf93202013-03-31 22:34:37 -0400961 sprintf(cpustr, "cpu%d", hcpu);
962 remove_proc_subtree(cpustr, palinfo_dir);
Sebastian Andrzej Siewior715c3212016-11-03 15:50:11 +0100963 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964}
965
Sebastian Andrzej Siewior715c3212016-11-03 15:50:11 +0100966static enum cpuhp_state hp_online;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967
Sebastian Andrzej Siewior715c3212016-11-03 15:50:11 +0100968static int __init palinfo_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969{
970 int i = 0;
971
972 printk(KERN_INFO "PAL Information Facility v%s\n", PALINFO_VERSION);
973 palinfo_dir = proc_mkdir("pal", NULL);
Al Viroccf93202013-03-31 22:34:37 -0400974 if (!palinfo_dir)
975 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976
Sebastian Andrzej Siewior715c3212016-11-03 15:50:11 +0100977 i = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "ia64/palinfo:online",
978 palinfo_add_proc, palinfo_del_proc);
979 if (i < 0) {
980 remove_proc_subtree("pal", NULL);
981 return i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982 }
Sebastian Andrzej Siewior715c3212016-11-03 15:50:11 +0100983 hp_online = i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 return 0;
985}
986
Sebastian Andrzej Siewior715c3212016-11-03 15:50:11 +0100987static void __exit palinfo_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988{
Sebastian Andrzej Siewior715c3212016-11-03 15:50:11 +0100989 cpuhp_remove_state(hp_online);
Al Viroccf93202013-03-31 22:34:37 -0400990 remove_proc_subtree("pal", NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991}
992
993module_init(palinfo_init);
994module_exit(palinfo_exit);