*
* Copyright (C) 2000-2001, 2003 Hewlett-Packard Co
* Stephane Eranian <eranian@hpl.hp.com>
+ * Copyright (C) 2004 Intel Corporation
+ * Ashok Raj <ashok.raj@intel.com>
*
* 05/26/2000 S.Eranian initial release
* 08/21/2000 S.Eranian updated to July 2000 PAL specs
* 02/05/2001 S.Eranian fixed module support
* 10/23/2001 S.Eranian updated pal_perf_mon_info bug fixes
+ * 03/24/2004 Ashok Raj updated to work with CPU Hotplug
+ * 10/26/2006 Russ Anderson updated processor features to rev 2.2 spec
*/
-#include <linux/config.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/efi.h>
+#include <linux/notifier.h>
+#include <linux/cpu.h>
+#include <linux/cpumask.h>
#include <asm/pal.h>
#include <asm/sal.h>
}
p += sprintf(p,
"%s Cache level %lu:\n"
- "\tSize : %lu bytes\n"
+ "\tSize : %u bytes\n"
"\tAttributes : ",
cache_types[j+cci.pcci_unified], i+1,
cci.pcci_cache_size);
if ((status = ia64_pal_vm_summary(&vm_info_1, &vm_info_2)) !=0) {
printk(KERN_ERR "ia64_pal_vm_summary=%ld\n", status);
- return 0;
- }
-
+ } else {
- p += sprintf(p,
+ p += sprintf(p,
"Physical Address Space : %d bits\n"
"Virtual Address Space : %d bits\n"
"Protection Key Registers(PKR) : %d\n"
"Implemented bits in PKR.key : %d\n"
"Hash Tag ID : 0x%x\n"
- "Size of RR.rid : %d\n",
+ "Size of RR.rid : %d\n"
+ "Max Purges : ",
vm_info_1.pal_vm_info_1_s.phys_add_size,
- vm_info_2.pal_vm_info_2_s.impl_va_msb+1, vm_info_1.pal_vm_info_1_s.max_pkr+1,
- vm_info_1.pal_vm_info_1_s.key_size, vm_info_1.pal_vm_info_1_s.hash_tag_id,
+ vm_info_2.pal_vm_info_2_s.impl_va_msb+1,
+ vm_info_1.pal_vm_info_1_s.max_pkr+1,
+ vm_info_1.pal_vm_info_1_s.key_size,
+ vm_info_1.pal_vm_info_1_s.hash_tag_id,
vm_info_2.pal_vm_info_2_s.rid_size);
+ if (vm_info_2.pal_vm_info_2_s.max_purges == PAL_MAX_PURGES)
+ p += sprintf(p, "unlimited\n");
+ else
+ p += sprintf(p, "%d\n",
+ vm_info_2.pal_vm_info_2_s.max_purges ?
+ vm_info_2.pal_vm_info_2_s.max_purges : 1);
+ }
- if (ia64_pal_mem_attrib(&attrib) != 0)
- return 0;
-
- p += sprintf(p, "Supported memory attributes : ");
- sep = "";
- for (i = 0; i < 8; i++) {
- if (attrib & (1 << i)) {
- p += sprintf(p, "%s%s", sep, mem_attrib[i]);
- sep = ", ";
+ if (ia64_pal_mem_attrib(&attrib) == 0) {
+ p += sprintf(p, "Supported memory attributes : ");
+ sep = "";
+ for (i = 0; i < 8; i++) {
+ if (attrib & (1 << i)) {
+ p += sprintf(p, "%s%s", sep, mem_attrib[i]);
+ sep = ", ";
+ }
}
+ p += sprintf(p, "\n");
}
- p += sprintf(p, "\n");
if ((status = ia64_pal_vm_page_size(&tr_pages, &vw_pages)) !=0) {
printk(KERN_ERR "ia64_pal_vm_page_size=%ld\n", status);
- return 0;
- }
-
- p += sprintf(p,
- "\nTLB walker : %simplemented\n"
- "Number of DTR : %d\n"
- "Number of ITR : %d\n"
- "TLB insertable page sizes : ",
- vm_info_1.pal_vm_info_1_s.vw ? "" : "not ",
- vm_info_1.pal_vm_info_1_s.max_dtr_entry+1,
- vm_info_1.pal_vm_info_1_s.max_itr_entry+1);
+ } else {
+ p += sprintf(p,
+ "\nTLB walker : %simplemented\n"
+ "Number of DTR : %d\n"
+ "Number of ITR : %d\n"
+ "TLB insertable page sizes : ",
+ vm_info_1.pal_vm_info_1_s.vw ? "" : "not ",
+ vm_info_1.pal_vm_info_1_s.max_dtr_entry+1,
+ vm_info_1.pal_vm_info_1_s.max_itr_entry+1);
- p = bitvector_process(p, tr_pages);
- p += sprintf(p, "\nTLB purgeable page sizes : ");
+ p = bitvector_process(p, tr_pages);
- p = bitvector_process(p, vw_pages);
+ p += sprintf(p, "\nTLB purgeable page sizes : ");
+ p = bitvector_process(p, vw_pages);
+ }
if ((status=ia64_get_ptce(&ptce)) != 0) {
printk(KERN_ERR "ia64_get_ptce=%ld\n", status);
- return 0;
- }
-
- p += sprintf(p,
+ } else {
+ p += sprintf(p,
"\nPurge base address : 0x%016lx\n"
"Purge outer loop count : %d\n"
"Purge inner loop count : %d\n"
"Purge outer loop stride : %d\n"
"Purge inner loop stride : %d\n",
- ptce.base, ptce.count[0], ptce.count[1], ptce.stride[0], ptce.stride[1]);
+ ptce.base, ptce.count[0], ptce.count[1],
+ ptce.stride[0], ptce.stride[1]);
- p += sprintf(p,
+ p += sprintf(p,
"TC Levels : %d\n"
"Unique TC(s) : %d\n",
vm_info_1.pal_vm_info_1_s.num_tc_levels,
vm_info_1.pal_vm_info_1_s.max_unique_tcs);
- for(i=0; i < vm_info_1.pal_vm_info_1_s.num_tc_levels; i++) {
- for (j=2; j>0 ; j--) {
- tc_pages = 0; /* just in case */
+ for(i=0; i < vm_info_1.pal_vm_info_1_s.num_tc_levels; i++) {
+ for (j=2; j>0 ; j--) {
+ tc_pages = 0; /* just in case */
- /* even without unification, some levels may not be present */
- if ((status=ia64_pal_vm_info(i,j, &tc_info, &tc_pages)) != 0) {
- continue;
- }
+ /* even without unification, some levels may not be present */
+ if ((status=ia64_pal_vm_info(i,j, &tc_info, &tc_pages)) != 0) {
+ continue;
+ }
- p += sprintf(p,
+ p += sprintf(p,
"\n%s Translation Cache Level %d:\n"
"\tHash sets : %d\n"
"\tAssociativity : %d\n"
"\tNumber of entries : %d\n"
"\tFlags : ",
- cache_types[j+tc_info.tc_unified], i+1, tc_info.tc_num_sets,
- tc_info.tc_associativity, tc_info.tc_num_entries);
+ cache_types[j+tc_info.tc_unified], i+1,
+ tc_info.tc_num_sets,
+ tc_info.tc_associativity,
+ tc_info.tc_num_entries);
- if (tc_info.tc_pf) p += sprintf(p, "PreferredPageSizeOptimized ");
- if (tc_info.tc_unified) p += sprintf(p, "Unified ");
- if (tc_info.tc_reduce_tr) p += sprintf(p, "TCReduction");
+ if (tc_info.tc_pf)
+ p += sprintf(p, "PreferredPageSizeOptimized ");
+ if (tc_info.tc_unified)
+ p += sprintf(p, "Unified ");
+ if (tc_info.tc_reduce_tr)
+ p += sprintf(p, "TCReduction");
- p += sprintf(p, "\n\tSupported page sizes: ");
+ p += sprintf(p, "\n\tSupported page sizes: ");
- p = bitvector_process(p, tc_pages);
+ p = bitvector_process(p, tc_pages);
- /* when unified date (j=2) is enough */
- if (tc_info.tc_unified) break;
+ /* when unified date (j=2) is enough */
+ if (tc_info.tc_unified)
+ break;
+ }
}
}
p += sprintf(p, "\n");
p += sprintf(p, "\n");
}
- if (ia64_pal_rse_info(&phys_stacked, &hints) != 0) return 0;
+ if (ia64_pal_rse_info(&phys_stacked, &hints) == 0) {
p += sprintf(p,
"RSE stacked physical registers : %ld\n"
"RSE load/store hints : %ld (%s)\n",
phys_stacked, hints.ph_data,
- hints.ph_data < RSE_HINTS_COUNT ? rse_hints[hints.ph_data]: "(\?\?)");
-
+ hints.ph_data < RSE_HINTS_COUNT ? rse_hints[hints.ph_data]: "(??)");
+ }
if (ia64_pal_debug_info(&iregs, &dregs))
return 0;
NULL,NULL,NULL,NULL,NULL,NULL,NULL, NULL,NULL,
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL,
- NULL,NULL,NULL,NULL,NULL,
+ "Unimplemented instruction address fault",
+ "INIT, PMI, and LINT pins",
+ "Simple unimplemented instr addresses",
+ "Variable P-state performance",
+ "Virtual machine features implemented",
"XIP,XPSR,XFS implemented",
"XR1-XR3 implemented",
"Disable dynamic predicate prediction",
"Disable dynamic data cache prefetch",
"Disable dynamic inst cache prefetch",
"Disable dynamic branch prediction",
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ "Disable P-states",
+ "Enable MCA on Data Poisoning",
+ "Enable vmsw instruction",
+ "Enable extern environmental notification",
"Disable BINIT on processor time-out",
"Disable dynamic power management (DPM)",
"Disable coherency",
"Enable CMCI promotion",
"Enable MCA to BINIT promotion",
"Enable MCA promotion",
- "Enable BEER promotion"
+ "Enable BERR promotion"
};
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
- "Enable Cache Line Repl. Exclusive",
"Enable Cache Line Repl. Shared",
+ "Enable Cache Line Repl. Exclusive",
"Disable Transaction Queuing",
- "Disable Reponse Error Checking",
+ "Disable Response Error Checking",
"Disable Bus Error Checking",
"Disable Bus Requester Internal Error Signalling",
"Disable Bus Requester Error Signalling",
pal_version_u_t min_ver, cur_ver;
char *p = page;
- /* The PAL_VERSION call is advertised as being able to support
- * both physical and virtual mode calls. This seems to be a documentation
- * bug rather than firmware bug. In fact, it does only support physical mode.
- * So now the code reflects this fact and the pal_version() has been updated
- * accordingly.
- */
- if (ia64_pal_version(&min_ver, &cur_ver) != 0) return 0;
+ if (ia64_pal_version(&min_ver, &cur_ver) != 0)
+ return 0;
p += sprintf(p,
"PAL_vendor : 0x%02x (min=0x%02x)\n"
- "PAL_A : %x.%x.%x (min=%x.%x.%x)\n"
- "PAL_B : %x.%x.%x (min=%x.%x.%x)\n",
- cur_ver.pal_version_s.pv_pal_vendor, min_ver.pal_version_s.pv_pal_vendor,
-
- cur_ver.pal_version_s.pv_pal_a_model>>4,
- cur_ver.pal_version_s.pv_pal_a_model&0xf, cur_ver.pal_version_s.pv_pal_a_rev,
- min_ver.pal_version_s.pv_pal_a_model>>4,
- min_ver.pal_version_s.pv_pal_a_model&0xf, min_ver.pal_version_s.pv_pal_a_rev,
-
- cur_ver.pal_version_s.pv_pal_b_model>>4,
- cur_ver.pal_version_s.pv_pal_b_model&0xf, cur_ver.pal_version_s.pv_pal_b_rev,
- min_ver.pal_version_s.pv_pal_b_model>>4,
- min_ver.pal_version_s.pv_pal_b_model&0xf, min_ver.pal_version_s.pv_pal_b_rev);
+ "PAL_A : %02x.%02x (min=%02x.%02x)\n"
+ "PAL_B : %02x.%02x (min=%02x.%02x)\n",
+ cur_ver.pal_version_s.pv_pal_vendor,
+ min_ver.pal_version_s.pv_pal_vendor,
+ cur_ver.pal_version_s.pv_pal_a_model,
+ cur_ver.pal_version_s.pv_pal_a_rev,
+ min_ver.pal_version_s.pv_pal_a_model,
+ min_ver.pal_version_s.pv_pal_a_rev,
+ cur_ver.pal_version_s.pv_pal_b_model,
+ cur_ver.pal_version_s.pv_pal_b_rev,
+ min_ver.pal_version_s.pv_pal_b_model,
+ min_ver.pal_version_s.pv_pal_b_rev);
return p - page;
}
if (ia64_pal_freq_ratios(&proc, &bus, &itc) != 0) return 0;
p += sprintf(p,
- "Processor/Clock ratio : %ld/%ld\n"
- "Bus/Clock ratio : %ld/%ld\n"
- "ITC/Clock ratio : %ld/%ld\n",
+ "Processor/Clock ratio : %d/%d\n"
+ "Bus/Clock ratio : %d/%d\n"
+ "ITC/Clock ratio : %d/%d\n",
proc.num, proc.den, bus.num, bus.den, itc.num, itc.den);
return p - page;
* does not do recursion of deletion
*
* Notes:
- * - first +1 accounts for the cpuN entry
- * - second +1 account for toplevel palinfo
- *
+ * - +1 accounts for the cpuN directory entry in /proc/pal
*/
-#define NR_PALINFO_PROC_ENTRIES (NR_CPUS*(NR_PALINFO_ENTRIES+1)+1)
+#define NR_PALINFO_PROC_ENTRIES (NR_CPUS*(NR_PALINFO_ENTRIES+1))
static struct proc_dir_entry *palinfo_proc_entries[NR_PALINFO_PROC_ENTRIES];
+static struct proc_dir_entry *palinfo_dir;
/*
* This data structure is used to pass which cpu,function is being requested
return len;
}
-static int __init
-palinfo_init(void)
+static void
+create_palinfo_proc_entries(unsigned int cpu)
{
# define CPUSTR "cpu%d"
pal_func_cpu_u_t f;
- struct proc_dir_entry **pdir = palinfo_proc_entries;
- struct proc_dir_entry *palinfo_dir, *cpu_dir;
- int i, j;
+ struct proc_dir_entry **pdir;
+ struct proc_dir_entry *cpu_dir;
+ int j;
char cpustr[sizeof(CPUSTR)];
- printk(KERN_INFO "PAL Information Facility v%s\n", PALINFO_VERSION);
-
- palinfo_dir = proc_mkdir("pal", NULL);
/*
* we keep track of created entries in a depth-first order for
* cleanup purposes. Each entry is stored into palinfo_proc_entries
*/
- for (i=0; i < NR_CPUS; i++) {
+ sprintf(cpustr,CPUSTR, cpu);
- if (!cpu_online(i)) continue;
+ cpu_dir = proc_mkdir(cpustr, palinfo_dir);
- sprintf(cpustr,CPUSTR, i);
+ f.req_cpu = cpu;
- cpu_dir = proc_mkdir(cpustr, palinfo_dir);
-
- f.req_cpu = i;
+ /*
+ * Compute the location to store per cpu entries
+ * We dont store the top level entry in this list, but
+ * remove it finally after removing all cpu entries.
+ */
+ pdir = &palinfo_proc_entries[cpu*(NR_PALINFO_ENTRIES+1)];
+ *pdir++ = cpu_dir;
+ for (j=0; j < NR_PALINFO_ENTRIES; j++) {
+ f.func_id = j;
+ *pdir = create_proc_read_entry(
+ palinfo_entries[j].name, 0, cpu_dir,
+ palinfo_read_entry, (void *)f.value);
+ if (*pdir)
+ (*pdir)->owner = THIS_MODULE;
+ pdir++;
+ }
+}
- for (j=0; j < NR_PALINFO_ENTRIES; j++) {
- f.func_id = j;
- *pdir = create_proc_read_entry(
- palinfo_entries[j].name, 0, cpu_dir,
- palinfo_read_entry, (void *)f.value);
- if (*pdir)
- (*pdir)->owner = THIS_MODULE;
- pdir++;
+static void
+remove_palinfo_proc_entries(unsigned int hcpu)
+{
+ int j;
+ struct proc_dir_entry *cpu_dir, **pdir;
+
+ pdir = &palinfo_proc_entries[hcpu*(NR_PALINFO_ENTRIES+1)];
+ cpu_dir = *pdir;
+ *pdir++=NULL;
+ for (j=0; j < (NR_PALINFO_ENTRIES); j++) {
+ if ((*pdir)) {
+ remove_proc_entry ((*pdir)->name, cpu_dir);
+ *pdir ++= NULL;
}
- *pdir++ = cpu_dir;
}
- *pdir = palinfo_dir;
+
+ if (cpu_dir) {
+ remove_proc_entry(cpu_dir->name, palinfo_dir);
+ }
+}
+
+static int palinfo_cpu_callback(struct notifier_block *nfb,
+ unsigned long action, void *hcpu)
+{
+ unsigned int hotcpu = (unsigned long)hcpu;
+
+ switch (action) {
+ case CPU_ONLINE:
+ create_palinfo_proc_entries(hotcpu);
+ break;
+ case CPU_DEAD:
+ remove_palinfo_proc_entries(hotcpu);
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block palinfo_cpu_notifier =
+{
+ .notifier_call = palinfo_cpu_callback,
+ .priority = 0,
+};
+
+static int __init
+palinfo_init(void)
+{
+ int i = 0;
+
+ printk(KERN_INFO "PAL Information Facility v%s\n", PALINFO_VERSION);
+ palinfo_dir = proc_mkdir("pal", NULL);
+
+ /* Create palinfo dirs in /proc for all online cpus */
+ for_each_online_cpu(i) {
+ create_palinfo_proc_entries(i);
+ }
+
+ /* Register for future delivery via notify registration */
+ register_hotcpu_notifier(&palinfo_cpu_notifier);
return 0;
}
int i = 0;
/* remove all nodes: depth first pass. Could optimize this */
- for (i=0; i< NR_PALINFO_PROC_ENTRIES ; i++) {
- if (palinfo_proc_entries[i])
- remove_proc_entry (palinfo_proc_entries[i]->name, NULL);
+ for_each_online_cpu(i) {
+ remove_palinfo_proc_entries(i);
}
+
+ /*
+ * Remove the top level entry finally
+ */
+ remove_proc_entry(palinfo_dir->name, NULL);
+
+ /*
+ * Unregister from cpu notifier callbacks
+ */
+ unregister_hotcpu_notifier(&palinfo_cpu_notifier);
}
module_init(palinfo_init);