blob: 78fa6579c9ead25f1adeea2b3c308248a4f7f451 [file] [log] [blame]
Thomas Gleixner09c434b2019-05-19 13:08:20 +01001// SPDX-License-Identifier: GPL-2.0-only
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
3 * palinfo.c
4 *
5 * Prints processor specific information reported by PAL.
6 * This code is based on specification of PAL as of the
7 * Intel IA-64 Architecture Software Developer's Manual v1.0.
8 *
9 *
10 * Copyright (C) 2000-2001, 2003 Hewlett-Packard Co
11 * Stephane Eranian <eranian@hpl.hp.com>
12 * Copyright (C) 2004 Intel Corporation
13 * Ashok Raj <ashok.raj@intel.com>
14 *
15 * 05/26/2000 S.Eranian initial release
16 * 08/21/2000 S.Eranian updated to July 2000 PAL specs
17 * 02/05/2001 S.Eranian fixed module support
18 * 10/23/2001 S.Eranian updated pal_perf_mon_info bug fixes
19 * 03/24/2004 Ashok Raj updated to work with CPU Hotplug
Russ Anderson895309f2006-12-07 11:06:35 -080020 * 10/26/2006 Russ Anderson updated processor features to rev 2.2 spec
Linus Torvalds1da177e2005-04-16 15:20:36 -070021 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#include <linux/types.h>
23#include <linux/errno.h>
24#include <linux/init.h>
25#include <linux/proc_fs.h>
David Howellse781c3d2013-04-11 01:28:40 +010026#include <linux/seq_file.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070027#include <linux/mm.h>
28#include <linux/module.h>
29#include <linux/efi.h>
30#include <linux/notifier.h>
31#include <linux/cpu.h>
32#include <linux/cpumask.h>
33
34#include <asm/pal.h>
35#include <asm/sal.h>
36#include <asm/page.h>
37#include <asm/processor.h>
38#include <linux/smp.h>
39
40MODULE_AUTHOR("Stephane Eranian <eranian@hpl.hp.com>");
41MODULE_DESCRIPTION("/proc interface to IA-64 PAL");
42MODULE_LICENSE("GPL");
43
44#define PALINFO_VERSION "0.5"
45
David Howellse781c3d2013-04-11 01:28:40 +010046typedef int (*palinfo_func_t)(struct seq_file *);
Linus Torvalds1da177e2005-04-16 15:20:36 -070047
48typedef struct {
49 const char *name; /* name of the proc entry */
50 palinfo_func_t proc_read; /* function to call for reading */
51 struct proc_dir_entry *entry; /* registered entry (removal) */
52} palinfo_entry_t;
53
54
55/*
56 * A bunch of string array to get pretty printing
57 */
58
David Howellse781c3d2013-04-11 01:28:40 +010059static const char *cache_types[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070060 "", /* not used */
61 "Instruction",
62 "Data",
63 "Data/Instruction" /* unified */
64};
65
66static const char *cache_mattrib[]={
67 "WriteThrough",
68 "WriteBack",
69 "", /* reserved */
70 "" /* reserved */
71};
72
73static const char *cache_st_hints[]={
74 "Temporal, level 1",
75 "Reserved",
76 "Reserved",
77 "Non-temporal, all levels",
78 "Reserved",
79 "Reserved",
80 "Reserved",
81 "Reserved"
82};
83
84static const char *cache_ld_hints[]={
85 "Temporal, level 1",
86 "Non-temporal, level 1",
87 "Reserved",
88 "Non-temporal, all levels",
89 "Reserved",
90 "Reserved",
91 "Reserved",
92 "Reserved"
93};
94
95static const char *rse_hints[]={
96 "enforced lazy",
97 "eager stores",
98 "eager loads",
99 "eager loads and stores"
100};
101
102#define RSE_HINTS_COUNT ARRAY_SIZE(rse_hints)
103
104static const char *mem_attrib[]={
105 "WB", /* 000 */
106 "SW", /* 001 */
107 "010", /* 010 */
108 "011", /* 011 */
109 "UC", /* 100 */
110 "UCE", /* 101 */
111 "WC", /* 110 */
112 "NaTPage" /* 111 */
113};
114
115/*
116 * Take a 64bit vector and produces a string such that
117 * if bit n is set then 2^n in clear text is generated. The adjustment
118 * to the right unit is also done.
119 *
120 * Input:
121 * - a pointer to a buffer to hold the string
122 * - a 64-bit vector
123 * Ouput:
124 * - a pointer to the end of the buffer
125 *
126 */
David Howellse781c3d2013-04-11 01:28:40 +0100127static void bitvector_process(struct seq_file *m, u64 vector)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128{
129 int i,j;
David Howellse781c3d2013-04-11 01:28:40 +0100130 static const char *units[]={ "", "K", "M", "G", "T" };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131
132 for (i=0, j=0; i < 64; i++ , j=i/10) {
David Howellse781c3d2013-04-11 01:28:40 +0100133 if (vector & 0x1)
134 seq_printf(m, "%d%s ", 1 << (i-j*10), units[j]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 vector >>= 1;
136 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137}
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 */
David Howellse781c3d2013-04-11 01:28:40 +0100151static void bitregister_process(struct seq_file *m, u64 *reg_info, int max)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152{
153 int i, begin, skip = 0;
154 u64 value = reg_info[0];
155
156 value >>= i = begin = ffs(value) - 1;
157
158 for(; i < max; i++ ) {
159
160 if (i != 0 && (i%64) == 0) value = *++reg_info;
161
162 if ((value & 0x1) == 0 && skip == 0) {
163 if (begin <= i - 2)
David Howellse781c3d2013-04-11 01:28:40 +0100164 seq_printf(m, "%d-%d ", begin, i-1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 else
David Howellse781c3d2013-04-11 01:28:40 +0100166 seq_printf(m, "%d ", i-1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 skip = 1;
168 begin = -1;
169 } else if ((value & 0x1) && skip == 1) {
170 skip = 0;
171 begin = i;
172 }
173 value >>=1;
174 }
175 if (begin > -1) {
176 if (begin < 127)
David Howellse781c3d2013-04-11 01:28:40 +0100177 seq_printf(m, "%d-127", begin);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178 else
David Howellse781c3d2013-04-11 01:28:40 +0100179 seq_puts(m, "127");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181}
182
David Howellse781c3d2013-04-11 01:28:40 +0100183static int power_info(struct seq_file *m)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184{
185 s64 status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 u64 halt_info_buffer[8];
187 pal_power_mgmt_info_u_t *halt_info =(pal_power_mgmt_info_u_t *)halt_info_buffer;
188 int i;
189
190 status = ia64_pal_halt_info(halt_info);
191 if (status != 0) return 0;
192
193 for (i=0; i < 8 ; i++ ) {
194 if (halt_info[i].pal_power_mgmt_info_s.im == 1) {
David Howellse781c3d2013-04-11 01:28:40 +0100195 seq_printf(m,
196 "Power level %d:\n"
197 "\tentry_latency : %d cycles\n"
198 "\texit_latency : %d cycles\n"
199 "\tpower consumption : %d mW\n"
200 "\tCache+TLB coherency : %s\n", i,
201 halt_info[i].pal_power_mgmt_info_s.entry_latency,
202 halt_info[i].pal_power_mgmt_info_s.exit_latency,
203 halt_info[i].pal_power_mgmt_info_s.power_consumption,
204 halt_info[i].pal_power_mgmt_info_s.co ? "Yes" : "No");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 } else {
David Howellse781c3d2013-04-11 01:28:40 +0100206 seq_printf(m,"Power level %d: not implemented\n", i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 }
208 }
David Howellse781c3d2013-04-11 01:28:40 +0100209 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210}
211
David Howellse781c3d2013-04-11 01:28:40 +0100212static int cache_info(struct seq_file *m)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213{
Matthew Wilcoxe088a4a2009-05-22 13:49:49 -0700214 unsigned long i, levels, unique_caches;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 pal_cache_config_info_t cci;
216 int j, k;
Matthew Wilcoxe088a4a2009-05-22 13:49:49 -0700217 long status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218
219 if ((status = ia64_pal_cache_summary(&levels, &unique_caches)) != 0) {
220 printk(KERN_ERR "ia64_pal_cache_summary=%ld\n", status);
221 return 0;
222 }
223
David Howellse781c3d2013-04-11 01:28:40 +0100224 seq_printf(m, "Cache levels : %ld\nUnique caches : %ld\n\n",
225 levels, unique_caches);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226
227 for (i=0; i < levels; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228 for (j=2; j >0 ; j--) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229 /* even without unification some level may not be present */
David Howellse781c3d2013-04-11 01:28:40 +0100230 if ((status=ia64_pal_cache_config_info(i,j, &cci)) != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232
David Howellse781c3d2013-04-11 01:28:40 +0100233 seq_printf(m,
234 "%s Cache level %lu:\n"
235 "\tSize : %u bytes\n"
236 "\tAttributes : ",
237 cache_types[j+cci.pcci_unified], i+1,
238 cci.pcci_cache_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239
David Howellse781c3d2013-04-11 01:28:40 +0100240 if (cci.pcci_unified)
241 seq_puts(m, "Unified ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242
David Howellse781c3d2013-04-11 01:28:40 +0100243 seq_printf(m, "%s\n", cache_mattrib[cci.pcci_cache_attr]);
244
245 seq_printf(m,
246 "\tAssociativity : %d\n"
247 "\tLine size : %d bytes\n"
248 "\tStride : %d bytes\n",
249 cci.pcci_assoc,
250 1<<cci.pcci_line_size,
251 1<<cci.pcci_stride);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 if (j == 1)
David Howellse781c3d2013-04-11 01:28:40 +0100253 seq_puts(m, "\tStore latency : N/A\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 else
David Howellse781c3d2013-04-11 01:28:40 +0100255 seq_printf(m, "\tStore latency : %d cycle(s)\n",
256 cci.pcci_st_latency);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257
David Howellse781c3d2013-04-11 01:28:40 +0100258 seq_printf(m,
259 "\tLoad latency : %d cycle(s)\n"
260 "\tStore hints : ", cci.pcci_ld_latency);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261
262 for(k=0; k < 8; k++ ) {
263 if ( cci.pcci_st_hints & 0x1)
David Howellse781c3d2013-04-11 01:28:40 +0100264 seq_printf(m, "[%s]", cache_st_hints[k]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265 cci.pcci_st_hints >>=1;
266 }
David Howellse781c3d2013-04-11 01:28:40 +0100267 seq_puts(m, "\n\tLoad hints : ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268
269 for(k=0; k < 8; k++ ) {
270 if (cci.pcci_ld_hints & 0x1)
David Howellse781c3d2013-04-11 01:28:40 +0100271 seq_printf(m, "[%s]", cache_ld_hints[k]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 cci.pcci_ld_hints >>=1;
273 }
David Howellse781c3d2013-04-11 01:28:40 +0100274 seq_printf(m,
275 "\n\tAlias boundary : %d byte(s)\n"
276 "\tTag LSB : %d\n"
277 "\tTag MSB : %d\n",
278 1<<cci.pcci_alias_boundary, cci.pcci_tag_lsb,
279 cci.pcci_tag_msb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280
281 /* when unified, data(j=2) is enough */
David Howellse781c3d2013-04-11 01:28:40 +0100282 if (cci.pcci_unified)
283 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 }
285 }
David Howellse781c3d2013-04-11 01:28:40 +0100286 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287}
288
289
David Howellse781c3d2013-04-11 01:28:40 +0100290static int vm_info(struct seq_file *m)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 u64 tr_pages =0, vw_pages=0, tc_pages;
293 u64 attrib;
294 pal_vm_info_1_u_t vm_info_1;
295 pal_vm_info_2_u_t vm_info_2;
296 pal_tc_info_u_t tc_info;
297 ia64_ptce_info_t ptce;
298 const char *sep;
299 int i, j;
Matthew Wilcoxe088a4a2009-05-22 13:49:49 -0700300 long status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301
302 if ((status = ia64_pal_vm_summary(&vm_info_1, &vm_info_2)) !=0) {
303 printk(KERN_ERR "ia64_pal_vm_summary=%ld\n", status);
Peter Chubb714d2dc2005-08-25 17:39:00 -0700304 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305
David Howellse781c3d2013-04-11 01:28:40 +0100306 seq_printf(m,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307 "Physical Address Space : %d bits\n"
308 "Virtual Address Space : %d bits\n"
309 "Protection Key Registers(PKR) : %d\n"
310 "Implemented bits in PKR.key : %d\n"
311 "Hash Tag ID : 0x%x\n"
Russ Anderson5b4d5682006-11-06 16:45:18 -0600312 "Size of RR.rid : %d\n"
313 "Max Purges : ",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 vm_info_1.pal_vm_info_1_s.phys_add_size,
Peter Chubb714d2dc2005-08-25 17:39:00 -0700315 vm_info_2.pal_vm_info_2_s.impl_va_msb+1,
316 vm_info_1.pal_vm_info_1_s.max_pkr+1,
317 vm_info_1.pal_vm_info_1_s.key_size,
318 vm_info_1.pal_vm_info_1_s.hash_tag_id,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 vm_info_2.pal_vm_info_2_s.rid_size);
Russ Anderson5b4d5682006-11-06 16:45:18 -0600320 if (vm_info_2.pal_vm_info_2_s.max_purges == PAL_MAX_PURGES)
David Howellse781c3d2013-04-11 01:28:40 +0100321 seq_puts(m, "unlimited\n");
Russ Anderson5b4d5682006-11-06 16:45:18 -0600322 else
David Howellse781c3d2013-04-11 01:28:40 +0100323 seq_printf(m, "%d\n",
Russ Anderson5b4d5682006-11-06 16:45:18 -0600324 vm_info_2.pal_vm_info_2_s.max_purges ?
325 vm_info_2.pal_vm_info_2_s.max_purges : 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 }
Peter Chubb714d2dc2005-08-25 17:39:00 -0700327
328 if (ia64_pal_mem_attrib(&attrib) == 0) {
David Howellse781c3d2013-04-11 01:28:40 +0100329 seq_puts(m, "Supported memory attributes : ");
Peter Chubb714d2dc2005-08-25 17:39:00 -0700330 sep = "";
331 for (i = 0; i < 8; i++) {
332 if (attrib & (1 << i)) {
David Howellse781c3d2013-04-11 01:28:40 +0100333 seq_printf(m, "%s%s", sep, mem_attrib[i]);
Peter Chubb714d2dc2005-08-25 17:39:00 -0700334 sep = ", ";
335 }
336 }
David Howellse781c3d2013-04-11 01:28:40 +0100337 seq_putc(m, '\n');
Peter Chubb714d2dc2005-08-25 17:39:00 -0700338 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339
340 if ((status = ia64_pal_vm_page_size(&tr_pages, &vw_pages)) !=0) {
341 printk(KERN_ERR "ia64_pal_vm_page_size=%ld\n", status);
Peter Chubb714d2dc2005-08-25 17:39:00 -0700342 } else {
343
David Howellse781c3d2013-04-11 01:28:40 +0100344 seq_printf(m,
345 "\nTLB walker : %simplemented\n"
346 "Number of DTR : %d\n"
347 "Number of ITR : %d\n"
348 "TLB insertable page sizes : ",
349 vm_info_1.pal_vm_info_1_s.vw ? "" : "not ",
350 vm_info_1.pal_vm_info_1_s.max_dtr_entry+1,
351 vm_info_1.pal_vm_info_1_s.max_itr_entry+1);
Peter Chubb714d2dc2005-08-25 17:39:00 -0700352
David Howellse781c3d2013-04-11 01:28:40 +0100353 bitvector_process(m, tr_pages);
Peter Chubb714d2dc2005-08-25 17:39:00 -0700354
David Howellse781c3d2013-04-11 01:28:40 +0100355 seq_puts(m, "\nTLB purgeable page sizes : ");
Peter Chubb714d2dc2005-08-25 17:39:00 -0700356
David Howellse781c3d2013-04-11 01:28:40 +0100357 bitvector_process(m, vw_pages);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 }
David Howellse781c3d2013-04-11 01:28:40 +0100359
360 if ((status = ia64_get_ptce(&ptce)) != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 printk(KERN_ERR "ia64_get_ptce=%ld\n", status);
Peter Chubb714d2dc2005-08-25 17:39:00 -0700362 } else {
David Howellse781c3d2013-04-11 01:28:40 +0100363 seq_printf(m,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 "\nPurge base address : 0x%016lx\n"
365 "Purge outer loop count : %d\n"
366 "Purge inner loop count : %d\n"
367 "Purge outer loop stride : %d\n"
368 "Purge inner loop stride : %d\n",
Peter Chubb714d2dc2005-08-25 17:39:00 -0700369 ptce.base, ptce.count[0], ptce.count[1],
370 ptce.stride[0], ptce.stride[1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371
David Howellse781c3d2013-04-11 01:28:40 +0100372 seq_printf(m,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 "TC Levels : %d\n"
374 "Unique TC(s) : %d\n",
375 vm_info_1.pal_vm_info_1_s.num_tc_levels,
376 vm_info_1.pal_vm_info_1_s.max_unique_tcs);
377
Peter Chubb714d2dc2005-08-25 17:39:00 -0700378 for(i=0; i < vm_info_1.pal_vm_info_1_s.num_tc_levels; i++) {
379 for (j=2; j>0 ; j--) {
380 tc_pages = 0; /* just in case */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381
Peter Chubb714d2dc2005-08-25 17:39:00 -0700382 /* even without unification, some levels may not be present */
David Howellse781c3d2013-04-11 01:28:40 +0100383 if ((status=ia64_pal_vm_info(i,j, &tc_info, &tc_pages)) != 0)
Peter Chubb714d2dc2005-08-25 17:39:00 -0700384 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385
David Howellse781c3d2013-04-11 01:28:40 +0100386 seq_printf(m,
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)
David Howellse781c3d2013-04-11 01:28:40 +0100398 seq_puts(m, "PreferredPageSizeOptimized ");
Peter Chubb714d2dc2005-08-25 17:39:00 -0700399 if (tc_info.tc_unified)
David Howellse781c3d2013-04-11 01:28:40 +0100400 seq_puts(m, "Unified ");
Peter Chubb714d2dc2005-08-25 17:39:00 -0700401 if (tc_info.tc_reduce_tr)
David Howellse781c3d2013-04-11 01:28:40 +0100402 seq_puts(m, "TCReduction");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403
David Howellse781c3d2013-04-11 01:28:40 +0100404 seq_puts(m, "\n\tSupported page sizes: ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405
David Howellse781c3d2013-04-11 01:28:40 +0100406 bitvector_process(m, 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 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414
David Howellse781c3d2013-04-11 01:28:40 +0100415 seq_putc(m, '\n');
416 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417}
418
419
David Howellse781c3d2013-04-11 01:28:40 +0100420static int register_info(struct seq_file *m)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 u64 reg_info[2];
423 u64 info;
Matthew Wilcoxe088a4a2009-05-22 13:49:49 -0700424 unsigned long phys_stacked;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 pal_hints_u_t hints;
Matthew Wilcoxe088a4a2009-05-22 13:49:49 -0700426 unsigned long iregs, dregs;
Joe Perchesc2164882010-09-13 21:23:48 -0700427 static const char * const info_type[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 "Implemented AR(s)",
429 "AR(s) with read side-effects",
430 "Implemented CR(s)",
431 "CR(s) with read side-effects",
432 };
433
434 for(info=0; info < 4; info++) {
David Howellse781c3d2013-04-11 01:28:40 +0100435 if (ia64_pal_register_info(info, &reg_info[0], &reg_info[1]) != 0)
436 return 0;
437 seq_printf(m, "%-32s : ", info_type[info]);
438 bitregister_process(m, reg_info, 128);
439 seq_putc(m, '\n');
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440 }
441
David Howellse781c3d2013-04-11 01:28:40 +0100442 if (ia64_pal_rse_info(&phys_stacked, &hints) == 0)
443 seq_printf(m,
444 "RSE stacked physical registers : %ld\n"
445 "RSE load/store hints : %ld (%s)\n",
446 phys_stacked, hints.ph_data,
447 hints.ph_data < RSE_HINTS_COUNT ? rse_hints[hints.ph_data]: "(??)");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 if (ia64_pal_debug_info(&iregs, &dregs))
450 return 0;
451
David Howellse781c3d2013-04-11 01:28:40 +0100452 seq_printf(m,
453 "Instruction debug register pairs : %ld\n"
454 "Data debug register pairs : %ld\n", iregs, dregs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455
David Howellse781c3d2013-04-11 01:28:40 +0100456 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457}
458
David Howellse781c3d2013-04-11 01:28:40 +0100459static const char *const proc_features_0[]={ /* Feature set 0 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 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,
463 NULL,NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,
Russ Anderson895309f2006-12-07 11:06:35 -0800464 "Unimplemented instruction address fault",
465 "INIT, PMI, and LINT pins",
466 "Simple unimplemented instr addresses",
467 "Variable P-state performance",
468 "Virtual machine features implemented",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469 "XIP,XPSR,XFS implemented",
470 "XR1-XR3 implemented",
471 "Disable dynamic predicate prediction",
472 "Disable processor physical number",
473 "Disable dynamic data cache prefetch",
474 "Disable dynamic inst cache prefetch",
475 "Disable dynamic branch prediction",
Russ Anderson895309f2006-12-07 11:06:35 -0800476 NULL, NULL, NULL, NULL,
477 "Disable P-states",
478 "Enable MCA on Data Poisoning",
479 "Enable vmsw instruction",
480 "Enable extern environmental notification",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 "Disable BINIT on processor time-out",
482 "Disable dynamic power management (DPM)",
483 "Disable coherency",
484 "Disable cache",
485 "Enable CMCI promotion",
486 "Enable MCA to BINIT promotion",
487 "Enable MCA promotion",
488 "Enable BERR promotion"
489};
490
David Howellse781c3d2013-04-11 01:28:40 +0100491static const char *const proc_features_16[]={ /* Feature set 16 */
Russ Andersonb8de4712007-10-16 17:02:38 -0500492 "Disable ETM",
493 "Enable ETM",
494 "Enable MCA on half-way timer",
495 "Enable snoop WC",
496 NULL,
497 "Enable Fast Deferral",
498 "Disable MCA on memory aliasing",
499 "Enable RSB",
500 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
501 "DP system processor",
502 "Low Voltage",
503 "HT supported",
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, NULL, NULL, NULL, NULL, NULL, NULL,
508 NULL, NULL, NULL, NULL, NULL
509};
510
David Howellse781c3d2013-04-11 01:28:40 +0100511static const char *const *const proc_features[]={
Russ Andersonb8de4712007-10-16 17:02:38 -0500512 proc_features_0,
513 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
514 NULL, NULL, NULL, NULL,
515 proc_features_16,
516 NULL, NULL, NULL, NULL,
517};
518
David Howellse781c3d2013-04-11 01:28:40 +0100519static void feature_set_info(struct seq_file *m, u64 avail, u64 status, u64 control,
520 unsigned long set)
Russ Andersonb8de4712007-10-16 17:02:38 -0500521{
David Howellse781c3d2013-04-11 01:28:40 +0100522 const char *const *vf, *const *v;
Russ Andersonb8de4712007-10-16 17:02:38 -0500523 int i;
524
525 vf = v = proc_features[set];
526 for(i=0; i < 64; i++, avail >>=1, status >>=1, control >>=1) {
527
528 if (!(control)) /* No remaining bits set */
529 break;
530 if (!(avail & 0x1)) /* Print only bits that are available */
531 continue;
532 if (vf)
533 v = vf + i;
534 if ( v && *v ) {
David Howellse781c3d2013-04-11 01:28:40 +0100535 seq_printf(m, "%-40s : %s %s\n", *v,
Russ Andersonb8de4712007-10-16 17:02:38 -0500536 avail & 0x1 ? (status & 0x1 ?
David Howellse781c3d2013-04-11 01:28:40 +0100537 "On " : "Off"): "",
Russ Andersonb8de4712007-10-16 17:02:38 -0500538 avail & 0x1 ? (control & 0x1 ?
539 "Ctrl" : "NoCtrl"): "");
540 } else {
David Howellse781c3d2013-04-11 01:28:40 +0100541 seq_printf(m, "Feature set %2ld bit %2d\t\t\t"
Russ Andersonb8de4712007-10-16 17:02:38 -0500542 " : %s %s\n",
543 set, i,
544 avail & 0x1 ? (status & 0x1 ?
545 "On " : "Off"): "",
546 avail & 0x1 ? (control & 0x1 ?
547 "Ctrl" : "NoCtrl"): "");
548 }
549 }
Russ Andersonb8de4712007-10-16 17:02:38 -0500550}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551
David Howellse781c3d2013-04-11 01:28:40 +0100552static int processor_info(struct seq_file *m)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553{
Russ Andersonb8de4712007-10-16 17:02:38 -0500554 u64 avail=1, status=1, control=1, feature_set=0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555 s64 ret;
556
Russ Andersonb8de4712007-10-16 17:02:38 -0500557 do {
558 ret = ia64_pal_proc_get_features(&avail, &status, &control,
559 feature_set);
David Howellse781c3d2013-04-11 01:28:40 +0100560 if (ret < 0)
561 return 0;
562
Russ Andersonb8de4712007-10-16 17:02:38 -0500563 if (ret == 1) {
564 feature_set++;
565 continue;
566 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567
David Howellse781c3d2013-04-11 01:28:40 +0100568 feature_set_info(m, avail, status, control, feature_set);
Russ Andersonb8de4712007-10-16 17:02:38 -0500569 feature_set++;
570 } while(1);
571
David Howellse781c3d2013-04-11 01:28:40 +0100572 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573}
574
David Howellse781c3d2013-04-11 01:28:40 +0100575static const char *const bus_features[]={
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
577 NULL,NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,
578 NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
579 NULL,NULL,
580 "Request Bus Parking",
581 "Bus Lock Mask",
582 "Enable Half Transfer",
583 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
584 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
585 NULL, NULL, NULL, NULL,
586 "Enable Cache Line Repl. Shared",
587 "Enable Cache Line Repl. Exclusive",
588 "Disable Transaction Queuing",
589 "Disable Response Error Checking",
590 "Disable Bus Error Checking",
591 "Disable Bus Requester Internal Error Signalling",
592 "Disable Bus Requester Error Signalling",
593 "Disable Bus Initialization Event Checking",
594 "Disable Bus Initialization Event Signalling",
595 "Disable Bus Address Error Checking",
596 "Disable Bus Address Error Signalling",
597 "Disable Bus Data Error Checking"
598};
599
600
David Howellse781c3d2013-04-11 01:28:40 +0100601static int bus_info(struct seq_file *m)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602{
David Howellse781c3d2013-04-11 01:28:40 +0100603 const char *const *v = bus_features;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604 pal_bus_features_u_t av, st, ct;
605 u64 avail, status, control;
606 int i;
607 s64 ret;
608
David Howellse781c3d2013-04-11 01:28:40 +0100609 if ((ret=ia64_pal_bus_get_features(&av, &st, &ct)) != 0)
610 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611
612 avail = av.pal_bus_features_val;
613 status = st.pal_bus_features_val;
614 control = ct.pal_bus_features_val;
615
616 for(i=0; i < 64; i++, v++, avail >>=1, status >>=1, control >>=1) {
David Howellse781c3d2013-04-11 01:28:40 +0100617 if ( ! *v )
618 continue;
619 seq_printf(m, "%-48s : %s%s %s\n", *v,
620 avail & 0x1 ? "" : "NotImpl",
621 avail & 0x1 ? (status & 0x1 ? "On" : "Off"): "",
622 avail & 0x1 ? (control & 0x1 ? "Ctrl" : "NoCtrl"): "");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 }
David Howellse781c3d2013-04-11 01:28:40 +0100624 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625}
626
David Howellse781c3d2013-04-11 01:28:40 +0100627static int version_info(struct seq_file *m)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628{
629 pal_version_u_t min_ver, cur_ver;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630
Matthew Wilcox1bf1eba2006-06-23 13:15:55 -0600631 if (ia64_pal_version(&min_ver, &cur_ver) != 0)
632 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633
David Howellse781c3d2013-04-11 01:28:40 +0100634 seq_printf(m,
635 "PAL_vendor : 0x%02x (min=0x%02x)\n"
636 "PAL_A : %02x.%02x (min=%02x.%02x)\n"
637 "PAL_B : %02x.%02x (min=%02x.%02x)\n",
638 cur_ver.pal_version_s.pv_pal_vendor,
639 min_ver.pal_version_s.pv_pal_vendor,
640 cur_ver.pal_version_s.pv_pal_a_model,
641 cur_ver.pal_version_s.pv_pal_a_rev,
642 min_ver.pal_version_s.pv_pal_a_model,
643 min_ver.pal_version_s.pv_pal_a_rev,
644 cur_ver.pal_version_s.pv_pal_b_model,
645 cur_ver.pal_version_s.pv_pal_b_rev,
646 min_ver.pal_version_s.pv_pal_b_model,
647 min_ver.pal_version_s.pv_pal_b_rev);
648 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649}
650
David Howellse781c3d2013-04-11 01:28:40 +0100651static int perfmon_info(struct seq_file *m)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 u64 pm_buffer[16];
654 pal_perf_mon_info_u_t pm_info;
655
David Howellse781c3d2013-04-11 01:28:40 +0100656 if (ia64_pal_perf_mon_info(pm_buffer, &pm_info) != 0)
657 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658
David Howellse781c3d2013-04-11 01:28:40 +0100659 seq_printf(m,
660 "PMC/PMD pairs : %d\n"
661 "Counter width : %d bits\n"
662 "Cycle event number : %d\n"
663 "Retired event number : %d\n"
664 "Implemented PMC : ",
665 pm_info.pal_perf_mon_info_s.generic,
666 pm_info.pal_perf_mon_info_s.width,
667 pm_info.pal_perf_mon_info_s.cycles,
668 pm_info.pal_perf_mon_info_s.retired);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669
David Howellse781c3d2013-04-11 01:28:40 +0100670 bitregister_process(m, pm_buffer, 256);
671 seq_puts(m, "\nImplemented PMD : ");
672 bitregister_process(m, pm_buffer+4, 256);
673 seq_puts(m, "\nCycles count capable : ");
674 bitregister_process(m, pm_buffer+8, 256);
675 seq_puts(m, "\nRetired bundles count capable : ");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676
677#ifdef CONFIG_ITANIUM
678 /*
679 * PAL_PERF_MON_INFO reports that only PMC4 can be used to count CPU_CYCLES
680 * which is wrong, both PMC4 and PMD5 support it.
681 */
David Howellse781c3d2013-04-11 01:28:40 +0100682 if (pm_buffer[12] == 0x10)
683 pm_buffer[12]=0x30;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684#endif
685
David Howellse781c3d2013-04-11 01:28:40 +0100686 bitregister_process(m, pm_buffer+12, 256);
687 seq_putc(m, '\n');
688 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689}
690
David Howellse781c3d2013-04-11 01:28:40 +0100691static int frequency_info(struct seq_file *m)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 struct pal_freq_ratio proc, itc, bus;
Matthew Wilcoxe088a4a2009-05-22 13:49:49 -0700694 unsigned long base;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695
696 if (ia64_pal_freq_base(&base) == -1)
David Howellse781c3d2013-04-11 01:28:40 +0100697 seq_puts(m, "Output clock : not implemented\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 else
David Howellse781c3d2013-04-11 01:28:40 +0100699 seq_printf(m, "Output clock : %ld ticks/s\n", base);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700
701 if (ia64_pal_freq_ratios(&proc, &bus, &itc) != 0) return 0;
702
David Howellse781c3d2013-04-11 01:28:40 +0100703 seq_printf(m,
Tony Luck2ab93912006-03-31 10:28:29 -0800704 "Processor/Clock ratio : %d/%d\n"
705 "Bus/Clock ratio : %d/%d\n"
706 "ITC/Clock ratio : %d/%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 proc.num, proc.den, bus.num, bus.den, itc.num, itc.den);
David Howellse781c3d2013-04-11 01:28:40 +0100708 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709}
710
David Howellse781c3d2013-04-11 01:28:40 +0100711static int tr_info(struct seq_file *m)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712{
Matthew Wilcoxe088a4a2009-05-22 13:49:49 -0700713 long status;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714 pal_tr_valid_u_t tr_valid;
715 u64 tr_buffer[4];
716 pal_vm_info_1_u_t vm_info_1;
717 pal_vm_info_2_u_t vm_info_2;
Matthew Wilcoxe088a4a2009-05-22 13:49:49 -0700718 unsigned long i, j;
719 unsigned long max[3], pgm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700720 struct ifa_reg {
Matthew Wilcoxe088a4a2009-05-22 13:49:49 -0700721 unsigned long valid:1;
722 unsigned long ig:11;
723 unsigned long vpn:52;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 } *ifa_reg;
725 struct itir_reg {
Matthew Wilcoxe088a4a2009-05-22 13:49:49 -0700726 unsigned long rv1:2;
727 unsigned long ps:6;
728 unsigned long key:24;
729 unsigned long rv2:32;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 } *itir_reg;
731 struct gr_reg {
Matthew Wilcoxe088a4a2009-05-22 13:49:49 -0700732 unsigned long p:1;
733 unsigned long rv1:1;
734 unsigned long ma:3;
735 unsigned long a:1;
736 unsigned long d:1;
737 unsigned long pl:2;
738 unsigned long ar:3;
739 unsigned long ppn:38;
740 unsigned long rv2:2;
741 unsigned long ed:1;
742 unsigned long ig:11;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 } *gr_reg;
744 struct rid_reg {
Matthew Wilcoxe088a4a2009-05-22 13:49:49 -0700745 unsigned long ig1:1;
746 unsigned long rv1:1;
747 unsigned long ig2:6;
748 unsigned long rid:24;
749 unsigned long rv2:32;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 } *rid_reg;
751
752 if ((status = ia64_pal_vm_summary(&vm_info_1, &vm_info_2)) !=0) {
753 printk(KERN_ERR "ia64_pal_vm_summary=%ld\n", status);
754 return 0;
755 }
756 max[0] = vm_info_1.pal_vm_info_1_s.max_itr_entry+1;
757 max[1] = vm_info_1.pal_vm_info_1_s.max_dtr_entry+1;
758
759 for (i=0; i < 2; i++ ) {
760 for (j=0; j < max[i]; j++) {
761
762 status = ia64_pal_tr_read(j, i, tr_buffer, &tr_valid);
763 if (status != 0) {
764 printk(KERN_ERR "palinfo: pal call failed on tr[%lu:%lu]=%ld\n",
765 i, j, status);
766 continue;
767 }
768
769 ifa_reg = (struct ifa_reg *)&tr_buffer[2];
770
David Howellse781c3d2013-04-11 01:28:40 +0100771 if (ifa_reg->valid == 0)
772 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773
774 gr_reg = (struct gr_reg *)tr_buffer;
775 itir_reg = (struct itir_reg *)&tr_buffer[1];
776 rid_reg = (struct rid_reg *)&tr_buffer[3];
777
778 pgm = -1 << (itir_reg->ps - 12);
David Howellse781c3d2013-04-11 01:28:40 +0100779 seq_printf(m,
780 "%cTR%lu: av=%d pv=%d dv=%d mv=%d\n"
781 "\tppn : 0x%lx\n"
782 "\tvpn : 0x%lx\n"
783 "\tps : ",
784 "ID"[i], j,
785 tr_valid.pal_tr_valid_s.access_rights_valid,
786 tr_valid.pal_tr_valid_s.priv_level_valid,
787 tr_valid.pal_tr_valid_s.dirty_bit_valid,
788 tr_valid.pal_tr_valid_s.mem_attr_valid,
789 (gr_reg->ppn & pgm)<< 12, (ifa_reg->vpn & pgm)<< 12);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790
David Howellse781c3d2013-04-11 01:28:40 +0100791 bitvector_process(m, 1<< itir_reg->ps);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792
David Howellse781c3d2013-04-11 01:28:40 +0100793 seq_printf(m,
794 "\n\tpl : %d\n"
795 "\tar : %d\n"
796 "\trid : %x\n"
797 "\tp : %d\n"
798 "\tma : %d\n"
799 "\td : %d\n",
800 gr_reg->pl, gr_reg->ar, rid_reg->rid, gr_reg->p, gr_reg->ma,
801 gr_reg->d);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 }
803 }
David Howellse781c3d2013-04-11 01:28:40 +0100804 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805}
806
807
808
809/*
810 * List {name,function} pairs for every entry in /proc/palinfo/cpu*
811 */
David Howellse781c3d2013-04-11 01:28:40 +0100812static const palinfo_entry_t palinfo_entries[]={
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813 { "version_info", version_info, },
814 { "vm_info", vm_info, },
815 { "cache_info", cache_info, },
816 { "power_info", power_info, },
817 { "register_info", register_info, },
818 { "processor_info", processor_info, },
819 { "perfmon_info", perfmon_info, },
820 { "frequency_info", frequency_info, },
821 { "bus_info", bus_info },
822 { "tr_info", tr_info, }
823};
824
825#define NR_PALINFO_ENTRIES (int) ARRAY_SIZE(palinfo_entries)
826
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827static struct proc_dir_entry *palinfo_dir;
828
829/*
830 * This data structure is used to pass which cpu,function is being requested
831 * It must fit in a 64bit quantity to be passed to the proc callback routine
832 *
833 * In SMP mode, when we get a request for another CPU, we must call that
834 * other CPU using IPI and wait for the result before returning.
835 */
836typedef union {
837 u64 value;
838 struct {
839 unsigned req_cpu: 32; /* for which CPU this info is */
840 unsigned func_id: 32; /* which function is requested */
841 } pal_func_cpu;
842} pal_func_cpu_u_t;
843
844#define req_cpu pal_func_cpu.req_cpu
845#define func_id pal_func_cpu.func_id
846
847#ifdef CONFIG_SMP
848
849/*
850 * used to hold information about final function to call
851 */
852typedef struct {
853 palinfo_func_t func; /* pointer to function to call */
David Howellse781c3d2013-04-11 01:28:40 +0100854 struct seq_file *m; /* buffer to store results */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855 int ret; /* return value from call */
856} palinfo_smp_data_t;
857
858
859/*
860 * this function does the actual final call and he called
861 * from the smp code, i.e., this is the palinfo callback routine
862 */
863static void
864palinfo_smp_call(void *info)
865{
866 palinfo_smp_data_t *data = (palinfo_smp_data_t *)info;
David Howellse781c3d2013-04-11 01:28:40 +0100867 data->ret = (*data->func)(data->m);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868}
869
870/*
871 * function called to trigger the IPI, we need to access a remote CPU
872 * Return:
873 * 0 : error or nothing to output
874 * otherwise how many bytes in the "page" buffer were written
875 */
876static
David Howellse781c3d2013-04-11 01:28:40 +0100877int palinfo_handle_smp(struct seq_file *m, pal_func_cpu_u_t *f)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878{
879 palinfo_smp_data_t ptr;
880 int ret;
881
882 ptr.func = palinfo_entries[f->func_id].proc_read;
David Howellse781c3d2013-04-11 01:28:40 +0100883 ptr.m = m;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884 ptr.ret = 0; /* just in case */
885
886
887 /* will send IPI to other CPU and wait for completion of remote call */
Jens Axboe8691e5a2008-06-06 11:18:06 +0200888 if ((ret=smp_call_function_single(f->req_cpu, palinfo_smp_call, &ptr, 1))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889 printk(KERN_ERR "palinfo: remote CPU call from %d to %d on function %d: "
890 "error %d\n", smp_processor_id(), f->req_cpu, f->func_id, ret);
891 return 0;
892 }
893 return ptr.ret;
894}
895#else /* ! CONFIG_SMP */
896static
David Howellse781c3d2013-04-11 01:28:40 +0100897int palinfo_handle_smp(struct seq_file *m, pal_func_cpu_u_t *f)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898{
899 printk(KERN_ERR "palinfo: should not be called with non SMP kernel\n");
900 return 0;
901}
902#endif /* CONFIG_SMP */
903
904/*
905 * Entry point routine: all calls go through this function
906 */
David Howellse781c3d2013-04-11 01:28:40 +0100907static int proc_palinfo_show(struct seq_file *m, void *v)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908{
David Howellse781c3d2013-04-11 01:28:40 +0100909 pal_func_cpu_u_t *f = (pal_func_cpu_u_t *)&m->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910
911 /*
912 * in SMP mode, we may need to call another CPU to get correct
913 * information. PAL, by definition, is processor specific
914 */
915 if (f->req_cpu == get_cpu())
David Howellse781c3d2013-04-11 01:28:40 +0100916 (*palinfo_entries[f->func_id].proc_read)(m);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917 else
David Howellse781c3d2013-04-11 01:28:40 +0100918 palinfo_handle_smp(m, f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919
920 put_cpu();
David Howellse781c3d2013-04-11 01:28:40 +0100921 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922}
923
Sebastian Andrzej Siewior715c3212016-11-03 15:50:11 +0100924static int palinfo_add_proc(unsigned int cpu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926 pal_func_cpu_u_t f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927 struct proc_dir_entry *cpu_dir;
928 int j;
Al Viroccf93202013-03-31 22:34:37 -0400929 char cpustr[3+4+1]; /* cpu numbers are up to 4095 on itanic */
930 sprintf(cpustr, "cpu%d", cpu);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931
932 cpu_dir = proc_mkdir(cpustr, palinfo_dir);
Al Viroccf93202013-03-31 22:34:37 -0400933 if (!cpu_dir)
Sebastian Andrzej Siewior715c3212016-11-03 15:50:11 +0100934 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935
936 f.req_cpu = cpu;
937
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938 for (j=0; j < NR_PALINFO_ENTRIES; j++) {
939 f.func_id = j;
Christoph Hellwig3f3942a2018-05-15 15:57:23 +0200940 proc_create_single_data(palinfo_entries[j].name, 0, cpu_dir,
941 proc_palinfo_show, (void *)f.value);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942 }
Sebastian Andrzej Siewior715c3212016-11-03 15:50:11 +0100943 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944}
945
Sebastian Andrzej Siewior715c3212016-11-03 15:50:11 +0100946static int palinfo_del_proc(unsigned int hcpu)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947{
Al Viroccf93202013-03-31 22:34:37 -0400948 char cpustr[3+4+1]; /* cpu numbers are up to 4095 on itanic */
Sebastian Andrzej Siewior715c3212016-11-03 15:50:11 +0100949
Al Viroccf93202013-03-31 22:34:37 -0400950 sprintf(cpustr, "cpu%d", hcpu);
951 remove_proc_subtree(cpustr, palinfo_dir);
Sebastian Andrzej Siewior715c3212016-11-03 15:50:11 +0100952 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953}
954
Sebastian Andrzej Siewior715c3212016-11-03 15:50:11 +0100955static enum cpuhp_state hp_online;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956
Sebastian Andrzej Siewior715c3212016-11-03 15:50:11 +0100957static int __init palinfo_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958{
959 int i = 0;
960
961 printk(KERN_INFO "PAL Information Facility v%s\n", PALINFO_VERSION);
962 palinfo_dir = proc_mkdir("pal", NULL);
Al Viroccf93202013-03-31 22:34:37 -0400963 if (!palinfo_dir)
964 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965
Sebastian Andrzej Siewior715c3212016-11-03 15:50:11 +0100966 i = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "ia64/palinfo:online",
967 palinfo_add_proc, palinfo_del_proc);
968 if (i < 0) {
969 remove_proc_subtree("pal", NULL);
970 return i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971 }
Sebastian Andrzej Siewior715c3212016-11-03 15:50:11 +0100972 hp_online = i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973 return 0;
974}
975
Sebastian Andrzej Siewior715c3212016-11-03 15:50:11 +0100976static void __exit palinfo_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977{
Sebastian Andrzej Siewior715c3212016-11-03 15:50:11 +0100978 cpuhp_remove_state(hp_online);
Al Viroccf93202013-03-31 22:34:37 -0400979 remove_proc_subtree("pal", NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980}
981
982module_init(palinfo_init);
983module_exit(palinfo_exit);