summaryrefslogtreecommitdiffstats
path: root/arch/sparc
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sparc')
-rw-r--r--arch/sparc/Kconfig9
-rw-r--r--arch/sparc/Makefile4
-rw-r--r--arch/sparc/boot/btfixupprep.c8
-rw-r--r--arch/sparc/boot/piggyback_32.c10
-rw-r--r--arch/sparc/boot/piggyback_64.c2
-rw-r--r--arch/sparc/include/asm/hardirq_32.h12
-rw-r--r--arch/sparc/include/asm/irq_32.h4
-rw-r--r--arch/sparc/include/asm/mman.h2
-rw-r--r--arch/sparc/include/asm/perf_counter.h14
-rw-r--r--arch/sparc/include/asm/perf_event.h14
-rw-r--r--arch/sparc/include/asm/pgtable_64.h4
-rw-r--r--arch/sparc/include/asm/smp_64.h1
-rw-r--r--arch/sparc/include/asm/system_64.h4
-rw-r--r--arch/sparc/include/asm/topology_64.h16
-rw-r--r--arch/sparc/include/asm/unistd.h2
-rw-r--r--arch/sparc/include/asm/vio.h2
-rw-r--r--arch/sparc/kernel/Makefile8
-rw-r--r--arch/sparc/kernel/init_task.c5
-rw-r--r--arch/sparc/kernel/irq_64.c2
-rw-r--r--arch/sparc/kernel/ktlb.S8
-rw-r--r--arch/sparc/kernel/ldc.c4
-rw-r--r--arch/sparc/kernel/nmi.c4
-rw-r--r--arch/sparc/kernel/pcr.c10
-rw-r--r--arch/sparc/kernel/perf_counter.c557
-rw-r--r--arch/sparc/kernel/perf_event.c1053
-rw-r--r--arch/sparc/kernel/prom_common.c4
-rw-r--r--arch/sparc/kernel/smp_64.c53
-rw-r--r--arch/sparc/kernel/sys_sparc32.c1
-rw-r--r--arch/sparc/kernel/systbls.h3
-rw-r--r--arch/sparc/kernel/systbls_32.S2
-rw-r--r--arch/sparc/kernel/systbls_64.S4
-rw-r--r--arch/sparc/kernel/visemul.c2
-rw-r--r--arch/sparc/mm/init_32.c2
-rw-r--r--arch/sparc/mm/init_64.c2
-rw-r--r--arch/sparc/mm/init_64.h2
-rw-r--r--arch/sparc/oprofile/init.c1
36 files changed, 1174 insertions, 661 deletions
diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig
index 86b82348b97c..05ef5380a687 100644
--- a/arch/sparc/Kconfig
+++ b/arch/sparc/Kconfig
@@ -25,7 +25,8 @@ config SPARC
select ARCH_WANT_OPTIONAL_GPIOLIB
select RTC_CLASS
select RTC_DRV_M48T59
- select HAVE_PERF_COUNTERS
+ select HAVE_PERF_EVENTS
+ select PERF_USE_VMALLOC
select HAVE_DMA_ATTRS
select HAVE_DMA_API_DEBUG
@@ -47,7 +48,8 @@ config SPARC64
select RTC_DRV_BQ4802
select RTC_DRV_SUN4V
select RTC_DRV_STARFIRE
- select HAVE_PERF_COUNTERS
+ select HAVE_PERF_EVENTS
+ select PERF_USE_VMALLOC
config ARCH_DEFCONFIG
string
@@ -102,6 +104,9 @@ config HAVE_SETUP_PER_CPU_AREA
config NEED_PER_CPU_EMBED_FIRST_CHUNK
def_bool y if SPARC64
+config NEED_PER_CPU_PAGE_FIRST_CHUNK
+ def_bool y if SPARC64
+
config GENERIC_HARDIRQS_NO__DO_IRQ
bool
def_bool y if SPARC64
diff --git a/arch/sparc/Makefile b/arch/sparc/Makefile
index 467221dd5702..dfe272d14465 100644
--- a/arch/sparc/Makefile
+++ b/arch/sparc/Makefile
@@ -31,7 +31,6 @@ export BITS := 32
#KBUILD_CFLAGS += -g -pipe -fcall-used-g5 -fcall-used-g7
KBUILD_CFLAGS += -m32 -pipe -mno-fpu -fcall-used-g5 -fcall-used-g7
KBUILD_AFLAGS += -m32
-CPPFLAGS_vmlinux.lds += -m32
#LDFLAGS_vmlinux = -N -Ttext 0xf0004000
# Since 2.5.40, the first stage is left not btfix-ed.
@@ -45,9 +44,6 @@ else
CHECKFLAGS += -D__sparc__ -D__sparc_v9__ -D__arch64__ -m64
-# Undefine sparc when processing vmlinux.lds - it is used
-# And teach CPP we are doing 64 bit builds (for this case)
-CPPFLAGS_vmlinux.lds += -m64 -Usparc
LDFLAGS := -m elf64_sparc
export BITS := 64
diff --git a/arch/sparc/boot/btfixupprep.c b/arch/sparc/boot/btfixupprep.c
index 52a4208fe4f0..bbf91b9c3d39 100644
--- a/arch/sparc/boot/btfixupprep.c
+++ b/arch/sparc/boot/btfixupprep.c
@@ -61,14 +61,14 @@ unsigned long lastfoffset = -1;
unsigned long lastfrelno;
btfixup *lastf;
-void fatal(void) __attribute__((noreturn));
-void fatal(void)
+static void fatal(void) __attribute__((noreturn));
+static void fatal(void)
{
fprintf(stderr, "Malformed output from objdump\n%s\n", buffer);
exit(1);
}
-btfixup *find(int type, char *name)
+static btfixup *find(int type, char *name)
{
int i;
for (i = 0; i < last; i++) {
@@ -88,7 +88,7 @@ btfixup *find(int type, char *name)
return array + last - 1;
}
-void set_mode (char *buffer)
+static void set_mode (char *buffer)
{
for (mode = 0;; mode++)
if (buffer[mode] < '0' || buffer[mode] > '9')
diff --git a/arch/sparc/boot/piggyback_32.c b/arch/sparc/boot/piggyback_32.c
index e8dc9adfcd61..ac944aec7301 100644
--- a/arch/sparc/boot/piggyback_32.c
+++ b/arch/sparc/boot/piggyback_32.c
@@ -35,17 +35,17 @@
* as PROM looks for a.out image only.
*/
-unsigned short ld2(char *p)
+static unsigned short ld2(char *p)
{
return (p[0] << 8) | p[1];
}
-unsigned int ld4(char *p)
+static unsigned int ld4(char *p)
{
return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
}
-void st4(char *p, unsigned int x)
+static void st4(char *p, unsigned int x)
{
p[0] = x >> 24;
p[1] = x >> 16;
@@ -53,7 +53,7 @@ void st4(char *p, unsigned int x)
p[3] = x;
}
-void usage(void)
+static void usage(void)
{
/* fs_img.gz is an image of initial ramdisk. */
fprintf(stderr, "Usage: piggyback vmlinux.aout System.map fs_img.gz\n");
@@ -61,7 +61,7 @@ void usage(void)
exit(1);
}
-void die(char *str)
+static void die(char *str)
{
perror (str);
exit(1);
diff --git a/arch/sparc/boot/piggyback_64.c b/arch/sparc/boot/piggyback_64.c
index c63fd1b6bdd4..a26a686cb5aa 100644
--- a/arch/sparc/boot/piggyback_64.c
+++ b/arch/sparc/boot/piggyback_64.c
@@ -32,7 +32,7 @@
/* Note: run this on an a.out kernel (use elftoaout for it), as PROM looks for a.out image onlly
usage: piggyback vmlinux System.map tail, where tail is gzipped fs of the initial ramdisk */
-void die(char *str)
+static void die(char *str)
{
perror (str);
exit(1);
diff --git a/arch/sparc/include/asm/hardirq_32.h b/arch/sparc/include/asm/hardirq_32.h
index 4f63ed8df551..162007643cdc 100644
--- a/arch/sparc/include/asm/hardirq_32.h
+++ b/arch/sparc/include/asm/hardirq_32.h
@@ -7,17 +7,7 @@
#ifndef __SPARC_HARDIRQ_H
#define __SPARC_HARDIRQ_H
-#include <linux/threads.h>
-#include <linux/spinlock.h>
-#include <linux/cache.h>
-
-/* entry.S is sensitive to the offsets of these fields */ /* XXX P3 Is it? */
-typedef struct {
- unsigned int __softirq_pending;
-} ____cacheline_aligned irq_cpustat_t;
-
-#include <linux/irq_cpustat.h> /* Standard mappings for irq_cpustat_t above */
-
#define HARDIRQ_BITS 8
+#include <asm-generic/hardirq.h>
#endif /* __SPARC_HARDIRQ_H */
diff --git a/arch/sparc/include/asm/irq_32.h b/arch/sparc/include/asm/irq_32.h
index ea43057d4763..cbf4801deaaf 100644
--- a/arch/sparc/include/asm/irq_32.h
+++ b/arch/sparc/include/asm/irq_32.h
@@ -6,10 +6,10 @@
#ifndef _SPARC_IRQ_H
#define _SPARC_IRQ_H
-#include <linux/interrupt.h>
-
#define NR_IRQS 16
+#include <linux/interrupt.h>
+
#define irq_canonicalize(irq) (irq)
extern void __init init_IRQ(void);
diff --git a/arch/sparc/include/asm/mman.h b/arch/sparc/include/asm/mman.h
index 988192e8e956..c3029ad6619a 100644
--- a/arch/sparc/include/asm/mman.h
+++ b/arch/sparc/include/asm/mman.h
@@ -20,6 +20,8 @@
#define MAP_POPULATE 0x8000 /* populate (prefault) pagetables */
#define MAP_NONBLOCK 0x10000 /* do not block on IO */
+#define MAP_STACK 0x20000 /* give out an address that is best suited for process/thread stacks */
+#define MAP_HUGETLB 0x40000 /* create a huge page mapping */
#ifdef __KERNEL__
#ifndef __ASSEMBLY__
diff --git a/arch/sparc/include/asm/perf_counter.h b/arch/sparc/include/asm/perf_counter.h
deleted file mode 100644
index 5d7a8ca0e491..000000000000
--- a/arch/sparc/include/asm/perf_counter.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef __ASM_SPARC_PERF_COUNTER_H
-#define __ASM_SPARC_PERF_COUNTER_H
-
-extern void set_perf_counter_pending(void);
-
-#define PERF_COUNTER_INDEX_OFFSET 0
-
-#ifdef CONFIG_PERF_COUNTERS
-extern void init_hw_perf_counters(void);
-#else
-static inline void init_hw_perf_counters(void) { }
-#endif
-
-#endif
diff --git a/arch/sparc/include/asm/perf_event.h b/arch/sparc/include/asm/perf_event.h
new file mode 100644
index 000000000000..7e2669894ce8
--- /dev/null
+++ b/arch/sparc/include/asm/perf_event.h
@@ -0,0 +1,14 @@
+#ifndef __ASM_SPARC_PERF_EVENT_H
+#define __ASM_SPARC_PERF_EVENT_H
+
+extern void set_perf_event_pending(void);
+
+#define PERF_EVENT_INDEX_OFFSET 0
+
+#ifdef CONFIG_PERF_EVENTS
+extern void init_hw_perf_events(void);
+#else
+static inline void init_hw_perf_events(void) { }
+#endif
+
+#endif
diff --git a/arch/sparc/include/asm/pgtable_64.h b/arch/sparc/include/asm/pgtable_64.h
index 0ff92fa22064..f3cb790fa2ae 100644
--- a/arch/sparc/include/asm/pgtable_64.h
+++ b/arch/sparc/include/asm/pgtable_64.h
@@ -41,8 +41,8 @@
#define LOW_OBP_ADDRESS _AC(0x00000000f0000000,UL)
#define HI_OBP_ADDRESS _AC(0x0000000100000000,UL)
#define VMALLOC_START _AC(0x0000000100000000,UL)
-#define VMALLOC_END _AC(0x0000000200000000,UL)
-#define VMEMMAP_BASE _AC(0x0000000200000000,UL)
+#define VMALLOC_END _AC(0x0000010000000000,UL)
+#define VMEMMAP_BASE _AC(0x0000010000000000,UL)
#define vmemmap ((struct page *)VMEMMAP_BASE)
diff --git a/arch/sparc/include/asm/smp_64.h b/arch/sparc/include/asm/smp_64.h
index becb6bf353a9..f49e11cd4ded 100644
--- a/arch/sparc/include/asm/smp_64.h
+++ b/arch/sparc/include/asm/smp_64.h
@@ -36,7 +36,6 @@ extern int sparc64_multi_core;
extern void arch_send_call_function_single_ipi(int cpu);
extern void arch_send_call_function_ipi_mask(const struct cpumask *mask);
-#define arch_send_call_function_ipi_mask arch_send_call_function_ipi_mask
/*
* General functions that each host system must provide.
diff --git a/arch/sparc/include/asm/system_64.h b/arch/sparc/include/asm/system_64.h
index 25e848f0cad7..d47a98e66972 100644
--- a/arch/sparc/include/asm/system_64.h
+++ b/arch/sparc/include/asm/system_64.h
@@ -63,6 +63,10 @@ do { __asm__ __volatile__("ba,pt %%xcc, 1f\n\t" \
: : : "memory"); \
} while (0)
+/* The kernel always executes in TSO memory model these days,
+ * and furthermore most sparc64 chips implement more stringent
+ * memory ordering than required by the specifications.
+ */
#define mb() membar_safe("#StoreLoad")
#define rmb() __asm__ __volatile__("":::"memory")
#define wmb() __asm__ __volatile__("":::"memory")
diff --git a/arch/sparc/include/asm/topology_64.h b/arch/sparc/include/asm/topology_64.h
index 26cd25c08399..600a79035fa1 100644
--- a/arch/sparc/include/asm/topology_64.h
+++ b/arch/sparc/include/asm/topology_64.h
@@ -12,22 +12,8 @@ static inline int cpu_to_node(int cpu)
#define parent_node(node) (node)
-static inline cpumask_t node_to_cpumask(int node)
-{
- return numa_cpumask_lookup_table[node];
-}
#define cpumask_of_node(node) (&numa_cpumask_lookup_table[node])
-/*
- * Returns a pointer to the cpumask of CPUs on Node 'node'.
- * Deprecated: use "const struct cpumask *mask = cpumask_of_node(node)"
- */
-#define node_to_cpumask_ptr(v, node) \
- cpumask_t *v = &(numa_cpumask_lookup_table[node])
-
-#define node_to_cpumask_ptr_next(v, node) \
- v = &(numa_cpumask_lookup_table[node])
-
struct pci_bus;
#ifdef CONFIG_PCI
extern int pcibus_to_node(struct pci_bus *pbus);
@@ -71,8 +57,6 @@ static inline int pcibus_to_node(struct pci_bus *pbus)
#ifdef CONFIG_SMP
#define topology_physical_package_id(cpu) (cpu_data(cpu).proc_id)
#define topology_core_id(cpu) (cpu_data(cpu).core_id)
-#define topology_core_siblings(cpu) (cpu_core_map[cpu])
-#define topology_thread_siblings(cpu) (per_cpu(cpu_sibling_map, cpu))
#define topology_core_cpumask(cpu) (&cpu_core_map[cpu])
#define topology_thread_cpumask(cpu) (&per_cpu(cpu_sibling_map, cpu))
#define mc_capable() (sparc64_multi_core)
diff --git a/arch/sparc/include/asm/unistd.h b/arch/sparc/include/asm/unistd.h
index 706df669f3b8..42f2316c3eaa 100644
--- a/arch/sparc/include/asm/unistd.h
+++ b/arch/sparc/include/asm/unistd.h
@@ -395,7 +395,7 @@
#define __NR_preadv 324
#define __NR_pwritev 325
#define __NR_rt_tgsigqueueinfo 326
-#define __NR_perf_counter_open 327
+#define __NR_perf_event_open 327
#define NR_SYSCALLS 328
diff --git a/arch/sparc/include/asm/vio.h b/arch/sparc/include/asm/vio.h
index d4de32f0f8af..9d83d3bcb494 100644
--- a/arch/sparc/include/asm/vio.h
+++ b/arch/sparc/include/asm/vio.h
@@ -258,8 +258,6 @@ static inline void *vio_dring_entry(struct vio_dring_state *dr,
static inline u32 vio_dring_avail(struct vio_dring_state *dr,
unsigned int ring_size)
{
- BUILD_BUG_ON(!is_power_of_2(ring_size));
-
return (dr->pending -
((dr->prod - dr->cons) & (ring_size - 1)));
}
diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile
index 247cc620cee5..5b47fab9966e 100644
--- a/arch/sparc/kernel/Makefile
+++ b/arch/sparc/kernel/Makefile
@@ -7,7 +7,11 @@ ccflags-y := -Werror
extra-y := head_$(BITS).o
extra-y += init_task.o
-extra-y += vmlinux.lds
+
+# Undefine sparc when processing vmlinux.lds - it is used
+# And teach CPP we are doing $(BITS) builds (for this case)
+CPPFLAGS_vmlinux.lds := -Usparc -m$(BITS)
+extra-y += vmlinux.lds
obj-$(CONFIG_SPARC32) += entry.o wof.o wuf.o
obj-$(CONFIG_SPARC32) += etrap_32.o
@@ -104,5 +108,5 @@ obj-$(CONFIG_AUDIT) += audit.o
audit--$(CONFIG_AUDIT) := compat_audit.o
obj-$(CONFIG_COMPAT) += $(audit--y)
-pc--$(CONFIG_PERF_COUNTERS) := perf_counter.o
+pc--$(CONFIG_PERF_EVENTS) := perf_event.o
obj-$(CONFIG_SPARC64) += $(pc--y)
diff --git a/arch/sparc/kernel/init_task.c b/arch/sparc/kernel/init_task.c
index 28125c5b3d3c..5fe3d65581f7 100644
--- a/arch/sparc/kernel/init_task.c
+++ b/arch/sparc/kernel/init_task.c
@@ -18,6 +18,5 @@ EXPORT_SYMBOL(init_task);
* If this is not aligned on a 8k boundry, then you should change code
* in etrap.S which assumes it.
*/
-union thread_union init_thread_union
- __attribute__((section (".data.init_task")))
- = { INIT_THREAD_INFO(init_task) };
+union thread_union init_thread_union __init_task_data =
+ { INIT_THREAD_INFO(init_task) };
diff --git a/arch/sparc/kernel/irq_64.c b/arch/sparc/kernel/irq_64.c
index 8daab33fc17d..8ab1d4728a4b 100644
--- a/arch/sparc/kernel/irq_64.c
+++ b/arch/sparc/kernel/irq_64.c
@@ -229,7 +229,7 @@ static unsigned int sun4u_compute_tid(unsigned long imap, unsigned long cpuid)
tid = ((a << IMAP_AID_SHIFT) |
(n << IMAP_NID_SHIFT));
tid &= (IMAP_AID_SAFARI |
- IMAP_NID_SAFARI);;
+ IMAP_NID_SAFARI);
}
} else {
tid = cpuid << IMAP_TID_SHIFT;
diff --git a/arch/sparc/kernel/ktlb.S b/arch/sparc/kernel/ktlb.S
index 3ea6e8cde8c5..1d361477d7d6 100644
--- a/arch/sparc/kernel/ktlb.S
+++ b/arch/sparc/kernel/ktlb.S
@@ -280,8 +280,8 @@ kvmap_dtlb_nonlinear:
#ifdef CONFIG_SPARSEMEM_VMEMMAP
/* Do not use the TSB for vmemmap. */
- mov (VMEMMAP_BASE >> 24), %g5
- sllx %g5, 24, %g5
+ mov (VMEMMAP_BASE >> 40), %g5
+ sllx %g5, 40, %g5
cmp %g4,%g5
bgeu,pn %xcc, kvmap_vmemmap
nop
@@ -293,8 +293,8 @@ kvmap_dtlb_tsbmiss:
sethi %hi(MODULES_VADDR), %g5
cmp %g4, %g5
blu,pn %xcc, kvmap_dtlb_longpath
- mov (VMALLOC_END >> 24), %g5
- sllx %g5, 24, %g5
+ mov (VMALLOC_END >> 40), %g5
+ sllx %g5, 40, %g5
cmp %g4, %g5
bgeu,pn %xcc, kvmap_dtlb_longpath
nop
diff --git a/arch/sparc/kernel/ldc.c b/arch/sparc/kernel/ldc.c
index adf5f273868a..cb3c72c45aab 100644
--- a/arch/sparc/kernel/ldc.c
+++ b/arch/sparc/kernel/ldc.c
@@ -1242,13 +1242,13 @@ int ldc_bind(struct ldc_channel *lp, const char *name)
snprintf(lp->tx_irq_name, LDC_IRQ_NAME_MAX, "%s TX", name);
err = request_irq(lp->cfg.rx_irq, ldc_rx,
- IRQF_SAMPLE_RANDOM | IRQF_SHARED,
+ IRQF_SAMPLE_RANDOM | IRQF_DISABLED | IRQF_SHARED,
lp->rx_irq_name, lp);
if (err)
return err;
err = request_irq(lp->cfg.tx_irq, ldc_tx,
- IRQF_SAMPLE_RANDOM | IRQF_SHARED,
+ IRQF_SAMPLE_RANDOM | IRQF_DISABLED | IRQF_SHARED,
lp->tx_irq_name, lp);
if (err) {
free_irq(lp->cfg.rx_irq, lp);
diff --git a/arch/sparc/kernel/nmi.c b/arch/sparc/kernel/nmi.c
index 378eb53e0776..b129611590a4 100644
--- a/arch/sparc/kernel/nmi.c
+++ b/arch/sparc/kernel/nmi.c
@@ -19,7 +19,7 @@
#include <linux/delay.h>
#include <linux/smp.h>
-#include <asm/perf_counter.h>
+#include <asm/perf_event.h>
#include <asm/ptrace.h>
#include <asm/local.h>
#include <asm/pcr.h>
@@ -265,7 +265,7 @@ int __init nmi_init(void)
}
}
if (!err)
- init_hw_perf_counters();
+ init_hw_perf_events();
return err;
}
diff --git a/arch/sparc/kernel/pcr.c b/arch/sparc/kernel/pcr.c
index 68ff00107073..2d94e7a03af5 100644
--- a/arch/sparc/kernel/pcr.c
+++ b/arch/sparc/kernel/pcr.c
@@ -7,7 +7,7 @@
#include <linux/init.h>
#include <linux/irq.h>
-#include <linux/perf_counter.h>
+#include <linux/perf_event.h>
#include <asm/pil.h>
#include <asm/pcr.h>
@@ -15,7 +15,7 @@
/* This code is shared between various users of the performance
* counters. Users will be oprofile, pseudo-NMI watchdog, and the
- * perf_counter support layer.
+ * perf_event support layer.
*/
#define PCR_SUN4U_ENABLE (PCR_PIC_PRIV | PCR_STRACE | PCR_UTRACE)
@@ -42,14 +42,14 @@ void deferred_pcr_work_irq(int irq, struct pt_regs *regs)
old_regs = set_irq_regs(regs);
irq_enter();
-#ifdef CONFIG_PERF_COUNTERS
- perf_counter_do_pending();
+#ifdef CONFIG_PERF_EVENTS
+ perf_event_do_pending();
#endif
irq_exit();
set_irq_regs(old_regs);
}
-void set_perf_counter_pending(void)
+void set_perf_event_pending(void)
{
set_softint(1 << PIL_DEFERRED_PCR_WORK);
}
diff --git a/arch/sparc/kernel/perf_counter.c b/arch/sparc/kernel/perf_counter.c
deleted file mode 100644
index 09de4035eaa9..000000000000
--- a/arch/sparc/kernel/perf_counter.c
+++ /dev/null
@@ -1,557 +0,0 @@
-/* Performance counter support for sparc64.
- *
- * Copyright (C) 2009 David S. Miller <davem@davemloft.net>
- *
- * This code is based almost entirely upon the x86 perf counter
- * code, which is:
- *
- * Copyright (C) 2008 Thomas Gleixner <tglx@linutronix.de>
- * Copyright (C) 2008-2009 Red Hat, Inc., Ingo Molnar
- * Copyright (C) 2009 Jaswinder Singh Rajput
- * Copyright (C) 2009 Advanced Micro Devices, Inc., Robert Richter
- * Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
- */
-
-#include <linux/perf_counter.h>
-#include <linux/kprobes.h>
-#include <linux/kernel.h>
-#include <linux/kdebug.h>
-#include <linux/mutex.h>
-
-#include <asm/cpudata.h>
-#include <asm/atomic.h>
-#include <asm/nmi.h>
-#include <asm/pcr.h>
-
-/* Sparc64 chips have two performance counters, 32-bits each, with
- * overflow interrupts generated on transition from 0xffffffff to 0.
- * The counters are accessed in one go using a 64-bit register.
- *
- * Both counters are controlled using a single control register. The
- * only way to stop all sampling is to clear all of the context (user,
- * supervisor, hypervisor) sampling enable bits. But these bits apply
- * to both counters, thus the two counters can't be enabled/disabled
- * individually.
- *
- * The control register has two event fields, one for each of the two
- * counters. It's thus nearly impossible to have one counter going
- * while keeping the other one stopped. Therefore it is possible to
- * get overflow interrupts for counters not currently "in use" and
- * that condition must be checked in the overflow interrupt handler.
- *
- * So we use a hack, in that we program inactive counters with the
- * "sw_count0" and "sw_count1" events. These count how many times
- * the instruction "sethi %hi(0xfc000), %g0" is executed. It's an
- * unusual way to encode a NOP and therefore will not trigger in
- * normal code.
- */
-
-#define MAX_HWCOUNTERS 2
-#define MAX_PERIOD ((1UL << 32) - 1)
-
-#define PIC_UPPER_INDEX 0
-#define PIC_LOWER_INDEX 1
-
-struct cpu_hw_counters {
- struct perf_counter *counters[MAX_HWCOUNTERS];
- unsigned long used_mask[BITS_TO_LONGS(MAX_HWCOUNTERS)];
- unsigned long active_mask[BITS_TO_LONGS(MAX_HWCOUNTERS)];
- int enabled;
-};
-DEFINE_PER_CPU(struct cpu_hw_counters, cpu_hw_counters) = { .enabled = 1, };
-
-struct perf_event_map {
- u16 encoding;
- u8 pic_mask;
-#define PIC_NONE 0x00
-#define PIC_UPPER 0x01
-#define PIC_LOWER 0x02
-};
-
-struct sparc_pmu {
- const struct perf_event_map *(*event_map)(int);
- int max_events;
- int upper_shift;
- int lower_shift;
- int event_mask;
- int hv_bit;
- int irq_bit;
- int upper_nop;
- int lower_nop;
-};
-
-static const struct perf_event_map ultra3i_perfmon_event_map[] = {
- [PERF_COUNT_HW_CPU_CYCLES] = { 0x0000, PIC_UPPER | PIC_LOWER },
- [PERF_COUNT_HW_INSTRUCTIONS] = { 0x0001, PIC_UPPER | PIC_LOWER },
- [PERF_COUNT_HW_CACHE_REFERENCES] = { 0x0009, PIC_LOWER },
- [PERF_COUNT_HW_CACHE_MISSES] = { 0x0009, PIC_UPPER },
-};
-
-static const struct perf_event_map *ultra3i_event_map(int event)
-{
- return &ultra3i_perfmon_event_map[event];
-}
-
-static const struct sparc_pmu ultra3i_pmu = {
- .event_map = ultra3i_event_map,
- .max_events = ARRAY_SIZE(ultra3i_perfmon_event_map),
- .upper_shift = 11,
- .lower_shift = 4,
- .event_mask = 0x3f,
- .upper_nop = 0x1c,
- .lower_nop = 0x14,
-};
-
-static const struct perf_event_map niagara2_perfmon_event_map[] = {
- [PERF_COUNT_HW_CPU_CYCLES] = { 0x02ff, PIC_UPPER | PIC_LOWER },
- [PERF_COUNT_HW_INSTRUCTIONS] = { 0x02ff, PIC_UPPER | PIC_LOWER },
- [PERF_COUNT_HW_CACHE_REFERENCES] = { 0x0208, PIC_UPPER | PIC_LOWER },
- [PERF_COUNT_HW_CACHE_MISSES] = { 0x0302, PIC_UPPER | PIC_LOWER },
- [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = { 0x0201, PIC_UPPER | PIC_LOWER },
- [PERF_COUNT_HW_BRANCH_MISSES] = { 0x0202, PIC_UPPER | PIC_LOWER },
-};
-
-static const struct perf_event_map *niagara2_event_map(int event)
-{
- return &niagara2_perfmon_event_map[event];
-}
-
-static const struct sparc_pmu niagara2_pmu = {
- .event_map = niagara2_event_map,
- .max_events = ARRAY_SIZE(niagara2_perfmon_event_map),
- .upper_shift = 19,
- .lower_shift = 6,
- .event_mask = 0xfff,
- .hv_bit = 0x8,
- .irq_bit = 0x03,
- .upper_nop = 0x220,
- .lower_nop = 0x220,
-};
-
-static const struct sparc_pmu *sparc_pmu __read_mostly;
-
-static u64 event_encoding(u64 event, int idx)
-{
- if (idx == PIC_UPPER_INDEX)
- event <<= sparc_pmu->upper_shift;
- else
- event <<= sparc_pmu->lower_shift;
- return event;
-}
-
-static u64 mask_for_index(int idx)
-{
- return event_encoding(sparc_pmu->event_mask, idx);
-}
-
-static u64 nop_for_index(int idx)
-{
- return event_encoding(idx == PIC_UPPER_INDEX ?
- sparc_pmu->upper_nop :
- sparc_pmu->lower_nop, idx);
-}
-
-static inline void sparc_pmu_enable_counter(struct hw_perf_counter *hwc,
- int idx)
-{
- u64 val, mask = mask_for_index(idx);
-
- val = pcr_ops->read();
- pcr_ops->write((val & ~mask) | hwc->config);
-}
-
-static inline void sparc_pmu_disable_counter(struct hw_perf_counter *hwc,
- int idx)
-{
- u64 mask = mask_for_index(idx);
- u64 nop = nop_for_index(idx);
- u64 val = pcr_ops->read();
-
- pcr_ops->write((val & ~mask) | nop);
-}
-
-void hw_perf_enable(void)
-{
- struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters);
- u64 val;
- int i;
-
- if (cpuc->enabled)
- return;
-
- cpuc->enabled = 1;
- barrier();
-
- val = pcr_ops->read();
-
- for (i = 0; i < MAX_HWCOUNTERS; i++) {
- struct perf_counter *cp = cpuc->counters[i];
- struct hw_perf_counter *hwc;
-
- if (!cp)
- continue;
- hwc = &cp->hw;
- val |= hwc->config_base;
- }
-
- pcr_ops->write(val);
-}
-
-void hw_perf_disable(void)
-{
- struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters);
- u64 val;
-
- if (!cpuc->enabled)
- return;
-
- cpuc->enabled = 0;
-
- val = pcr_ops->read();
- val &= ~(PCR_UTRACE | PCR_STRACE |
- sparc_pmu->hv_bit | sparc_pmu->irq_bit);
- pcr_ops->write(val);
-}
-
-static u32 read_pmc(int idx)
-{
- u64 val;
-
- read_pic(val);
- if (idx == PIC_UPPER_INDEX)
- val >>= 32;
-
- return val & 0xffffffff;
-}
-
-static void write_pmc(int idx, u64 val)
-{
- u64 shift, mask, pic;
-
- shift = 0;
- if (idx == PIC_UPPER_INDEX)
- shift = 32;
-
- mask = ((u64) 0xffffffff) << shift;
- val <<= shift;
-
- read_pic(pic);
- pic &= ~mask;
- pic |= val;
- write_pic(pic);
-}
-
-static int sparc_perf_counter_set_period(struct perf_counter *counter,
- struct hw_perf_counter *hwc, int idx)
-{
- s64 left = atomic64_read(&hwc->period_left);
- s64 period = hwc->sample_period;
- int ret = 0;
-
- if (unlikely(left <= -period)) {
- left = period;
- atomic64_set(&hwc->period_left, left);
- hwc->last_period = period;
- ret = 1;
- }
-
- if (unlikely(left <= 0)) {
- left += period;
- atomic64_set(&hwc->period_left, left);
- hwc->last_period = period;
- ret = 1;
- }
- if (left > MAX_PERIOD)
- left = MAX_PERIOD;
-
- atomic64_set(&hwc->prev_count, (u64)-left);
-
- write_pmc(idx, (u64)(-left) & 0xffffffff);
-
- perf_counter_update_userpage(counter);
-
- return ret;
-}
-
-static int sparc_pmu_enable(struct perf_counter *counter)
-{
- struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters);
- struct hw_perf_counter *hwc = &counter->hw;
- int idx = hwc->idx;
-
- if (test_and_set_bit(idx, cpuc->used_mask))
- return -EAGAIN;
-
- sparc_pmu_disable_counter(hwc, idx);
-
- cpuc->counters[idx] = counter;
- set_bit(idx, cpuc->active_mask);
-
- sparc_perf_counter_set_period(counter, hwc, idx);
- sparc_pmu_enable_counter(hwc, idx);
- perf_counter_update_userpage(counter);
- return 0;
-}
-
-static u64 sparc_perf_counter_update(struct perf_counter *counter,
- struct hw_perf_counter *hwc, int idx)
-{
- int shift = 64 - 32;
- u64 prev_raw_count, new_raw_count;
- s64 delta;
-
-again:
- prev_raw_count = atomic64_read(&hwc->prev_count);
- new_raw_count = read_pmc(idx);
-
- if (atomic64_cmpxchg(&hwc->prev_count, prev_raw_count,
- new_raw_count) != prev_raw_count)
- goto again;
-
- delta = (new_raw_count << shift) - (prev_raw_count << shift);
- delta >>= shift;
-
- atomic64_add(delta, &counter->count);
- atomic64_sub(delta, &hwc->period_left);
-
- return new_raw_count;
-}
-
-static void sparc_pmu_disable(struct perf_counter *counter)
-{
- struct cpu_hw_counters *cpuc = &__get_cpu_var(cpu_hw_counters);
- struct hw_perf_counter *hwc = &counter->hw;
- int idx = hwc->idx;
-
- clear_bit(idx, cpuc->active_mask);
- sparc_pmu_disable_counter(hwc, idx);
-
- barrier();
-
- sparc_perf_counter_update(counter, hwc, idx);
- cpuc->counters[idx] = NULL;
- clear_bit(idx, cpuc->used_mask);
-
- perf_counter_update_userpage(counter);
-}
-
-static void sparc_pmu_read(struct perf_counter *counter)
-{
- struct hw_perf_counter *hwc = &counter->hw;
- sparc_perf_counter_update(counter, hwc, hwc->idx);
-}
-
-static void sparc_pmu_unthrottle(struct perf_counter *counter)
-{
- struct hw_perf_counter *hwc = &counter->hw;
- sparc_pmu_enable_counter(hwc, hwc->idx);
-}
-
-static atomic_t active_counters = ATOMIC_INIT(0);
-static DEFINE_MUTEX(pmc_grab_mutex);
-
-void perf_counter_grab_pmc(void)
-{
- if (atomic_inc_not_zero(&active_counters))
- return;
-
- mutex_lock(&pmc_grab_mutex);
- if (atomic_read(&active_counters) == 0) {
- if (atomic_read(&nmi_active) > 0) {
- on_each_cpu(stop_nmi_watchdog, NULL, 1);
- BUG_ON(atomic_read(&nmi_active) != 0);
- }
- atomic_inc(&active_counters);
- }
- mutex_unlock(&pmc_grab_mutex);
-}
-
-void perf_counter_release_pmc(void)
-{
- if (atomic_dec_and_mutex_lock(&active_counters, &pmc_grab_mutex)) {
- if (atomic_read(&nmi_active) == 0)
- on_each_cpu(start_nmi_watchdog, NULL, 1);
- mutex_unlock(&pmc_grab_mutex);
- }
-}
-
-static void hw_perf_counter_destroy(struct perf_counter *counter)
-{
- perf_counter_release_pmc();
-}
-
-static int __hw_perf_counter_init(struct perf_counter *counter)
-{
- struct perf_counter_attr *attr = &counter->attr;
- struct hw_perf_counter *hwc = &counter->hw;
- const struct perf_event_map *pmap;
- u64 enc;
-
- if (atomic_read(&nmi_active) < 0)
- return -ENODEV;
-
- if (attr->type != PERF_TYPE_HARDWARE)
- return -EOPNOTSUPP;
-
- if (attr->config >= sparc_pmu->max_events)
- return -EINVAL;
-
- perf_counter_grab_pmc();
- counter->destroy = hw_perf_counter_destroy;
-
- /* We save the enable bits in the config_base. So to
- * turn off sampling just write 'config', and to enable
- * things write 'config | config_base'.
- */
- hwc->config_base = sparc_pmu->irq_bit;
- if (!attr->exclude_user)
- hwc->config_base |= PCR_UTRACE;
- if (!attr->exclude_kernel)
- hwc->config_base |= PCR_STRACE;
- if (!attr->exclude_hv)
- hwc->config_base |= sparc_pmu->hv_bit;
-
- if (!hwc->sample_period) {
- hwc->sample_period = MAX_PERIOD;
- hwc->last_period = hwc->sample_period;
- atomic64_set(&hwc->period_left, hwc->sample_period);
- }
-
- pmap = sparc_pmu->event_map(attr->config);
-
- enc = pmap->encoding;
- if (pmap->pic_mask & PIC_UPPER) {
- hwc->idx = PIC_UPPER_INDEX;
- enc <<= sparc_pmu->upper_shift;
- } else {
- hwc->idx = PIC_LOWER_INDEX;
- enc <<= sparc_pmu->lower_shift;
- }
-
- hwc->config |= enc;
- return 0;
-}
-
-static const struct pmu pmu = {
- .enable = sparc_pmu_enable,
- .disable = sparc_pmu_disable,
- .read = sparc_pmu_read,
- .unthrottle = sparc_pmu_unthrottle,
-};
-
-const struct pmu *hw_perf_counter_init(struct perf_counter *counter)
-{
- int err = __hw_perf_counter_init(counter);
-
- if (err)
- return ERR_PTR(err);
- return &pmu;
-}
-
-void perf_counter_print_debug(void)
-{
- unsigned long flags;
- u64 pcr, pic;
- int cpu;
-
- if (!sparc_pmu)
- return;
-
- local_irq_save(flags);
-
- cpu = smp_processor_id();
-
- pcr = pcr_ops->read();
- read_pic(pic);
-
- pr_info("\n");
- pr_info("CPU#%d: PCR[%016llx] PIC[%016llx]\n",
- cpu, pcr, pic);
-
- local_irq_restore(flags);
-}
-
-static int __kprobes perf_counter_nmi_handler(struct notifier_block *self,
- unsigned long cmd, void *__args)
-{
- struct die_args *args = __args;
- struct perf_sample_data data;
- struct cpu_hw_counters *cpuc;
- struct pt_regs *regs;
- int idx;
-
- if (!atomic_read(&active_counters))
- return NOTIFY_DONE;
-
- switch (cmd) {
- case DIE_NMI:
- break;
-
- default:
- return NOTIFY_DONE;
- }
-
- regs = args->regs;
-
- data.regs = regs;
- data.addr = 0;
-
- cpuc = &__get_cpu_var(cpu_hw_counters);
- for (idx = 0; idx < MAX_HWCOUNTERS; idx++) {
- struct perf_counter *counter = cpuc->counters[idx];
- struct hw_perf_counter *hwc;
- u64 val;
-
- if (!test_bit(idx, cpuc->active_mask))
- continue;
- hwc = &counter->hw;
- val = sparc_perf_counter_update(counter, hwc, idx);
- if (val & (1ULL << 31))
- continue;
-
- data.period = counter->hw.last_period;
- if (!sparc_perf_counter_set_period(counter, hwc, idx))
- continue;
-
- if (perf_counter_overflow(counter, 1, &data))
- sparc_pmu_disable_counter(hwc, idx);
- }
-
- return NOTIFY_STOP;
-}
-
-static __read_mostly struct notifier_block perf_counter_nmi_notifier = {
- .notifier_call = perf_counter_nmi_handler,
-};
-
-static bool __init supported_pmu(void)
-{
- if (!strcmp(sparc_pmu_type, "ultra3i")) {
- sparc_pmu = &ultra3i_pmu;
- return true;
- }
- if (!strcmp(sparc_pmu_type, "niagara2")) {
- sparc_pmu = &niagara2_pmu;
- return true;
- }
- return false;
-}
-
-void __init init_hw_perf_counters(void)
-{
- pr_info("Performance counters: ");
-
- if (!supported_pmu()) {
- pr_cont("No support for PMU type '%s'\n", sparc_pmu_type);
- return;
- }
-
- pr_cont("Supported PMU type is '%s'\n", sparc_pmu_type);
-
- /* All sparc64 PMUs currently have 2 counters. But this simple
- * driver only supports one active counter at a time.
- */
- perf_max_counters = 1;
-
- register_die_notifier(&perf_counter_nmi_notifier);
-}
diff --git a/arch/sparc/kernel/perf_event.c b/arch/sparc/kernel/perf_event.c
new file mode 100644
index 000000000000..fa5936e1c3b9
--- /dev/null
+++ b/arch/sparc/kernel/perf_event.c
@@ -0,0 +1,1053 @@
+/* Performance event support for sparc64.
+ *
+ * Copyright (C) 2009 David S. Miller <davem@davemloft.net>
+ *
+ * This code is based almost entirely upon the x86 perf event
+ * code, which is:
+ *
+ * Copyright (C) 2008 Thomas Gleixner <tglx@linutronix.de>
+ * Copyright (C) 2008-2009 Red Hat, Inc., Ingo Molnar
+ * Copyright (C) 2009 Jaswinder Singh Rajput
+ * Copyright (C) 2009 Advanced Micro Devices, Inc., Robert Richter
+ * Copyright (C) 2008-2009 Red Hat, Inc., Peter Zijlstra <pzijlstr@redhat.com>
+ */
+
+#include <linux/perf_event.h>
+#include <linux/kprobes.h>
+#include <linux/kernel.h>
+#include <linux/kdebug.h>
+#include <linux/mutex.h>
+
+#include <asm/cpudata.h>
+#include <asm/atomic.h>
+#include <asm/nmi.h>
+#include <asm/pcr.h>
+
+/* Sparc64 chips have two performance counters, 32-bits each, with
+ * overflow interrupts generated on transition from 0xffffffff to 0.
+ * The counters are accessed in one go using a 64-bit register.
+ *
+ * Both counters are controlled using a single control register. The
+ * only way to stop all sampling is to clear all of the context (user,
+ * supervisor, hypervisor) sampling enable bits. But these bits apply
+ * to both counters, thus the two counters can't be enabled/disabled
+ * individually.
+ *
+ * The control register has two event fields, one for each of the two
+ * counters. It's thus nearly impossible to have one counter going
+ * while keeping the other one stopped. Therefore it is possible to
+ * get overflow interrupts for counters not currently "in use" and
+ * that condition must be checked in the overflow interrupt handler.
+ *
+ * So we use a hack, in that we program inactive counters with the
+ * "sw_count0" and "sw_count1" events. These count how many times
+ * the instruction "sethi %hi(0xfc000), %g0" is executed. It's an
+ * unusual way to encode a NOP and therefore will not trigger in
+ * normal code.
+ */
+
+#define MAX_HWEVENTS 2
+#define MAX_PERIOD ((1UL << 32) - 1)
+
+#define PIC_UPPER_INDEX 0
+#define PIC_LOWER_INDEX 1
+
+struct cpu_hw_events {
+ struct perf_event *events[MAX_HWEVENTS];
+ unsigned long used_mask[BITS_TO_LONGS(MAX_HWEVENTS)];
+ unsigned long active_mask[BITS_TO_LONGS(MAX_HWEVENTS)];
+ u64 pcr;
+ int enabled;
+};
+DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = { .enabled = 1, };
+
+struct perf_event_map {
+ u16 encoding;
+ u8 pic_mask;
+#define PIC_NONE 0x00
+#define PIC_UPPER 0x01
+#define PIC_LOWER 0x02
+};
+
+static unsigned long perf_event_encode(const struct perf_event_map *pmap)
+{
+ return ((unsigned long) pmap->encoding << 16) | pmap->pic_mask;
+}
+
+static void perf_event_decode(unsigned long val, u16 *enc, u8 *msk)
+{
+ *msk = val & 0xff;
+ *enc = val >> 16;
+}
+
+#define C(x) PERF_COUNT_HW_CACHE_##x
+
+#define CACHE_OP_UNSUPPORTED 0xfffe
+#define CACHE_OP_NONSENSE 0xffff
+
+typedef struct perf_event_map cache_map_t
+ [PERF_COUNT_HW_CACHE_MAX]
+ [PERF_COUNT_HW_CACHE_OP_MAX]
+ [PERF_COUNT_HW_CACHE_RESULT_MAX];
+
+struct sparc_pmu {
+ const struct perf_event_map *(*event_map)(int);
+ const cache_map_t *cache_map;
+ int max_events;
+ int upper_shift;
+ int lower_shift;
+ int event_mask;
+ int hv_bit;
+ int irq_bit;
+ int upper_nop;
+ int lower_nop;
+};
+
+static const struct perf_event_map ultra3_perfmon_event_map[] = {
+ [PERF_COUNT_HW_CPU_CYCLES] = { 0x0000, PIC_UPPER | PIC_LOWER },
+ [PERF_COUNT_HW_INSTRUCTIONS] = { 0x0001, PIC_UPPER | PIC_LOWER },
+ [PERF_COUNT_HW_CACHE_REFERENCES] = { 0x0009, PIC_LOWER },
+ [PERF_COUNT_HW_CACHE_MISSES] = { 0x0009, PIC_UPPER },
+};
+
+static const struct perf_event_map *ultra3_event_map(int event_id)
+{
+ return &ultra3_perfmon_event_map[event_id];
+}
+
+static const cache_map_t ultra3_cache_map = {
+[C(L1D)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = { 0x09, PIC_LOWER, },
+ [C(RESULT_MISS)] = { 0x09, PIC_UPPER, },
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = { 0x0a, PIC_LOWER },
+ [C(RESULT_MISS)] = { 0x0a, PIC_UPPER },
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+ [C(RESULT_MISS)] = { CACHE_OP_UNSUPPORTED },
+ },
+},
+[C(L1I)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = { 0x09, PIC_LOWER, },
+ [C(RESULT_MISS)] = { 0x09, PIC_UPPER, },
+ },
+ [ C(OP_WRITE) ] = {
+ [ C(RESULT_ACCESS) ] = { CACHE_OP_NONSENSE },
+ [ C(RESULT_MISS) ] = { CACHE_OP_NONSENSE },
+ },
+ [ C(OP_PREFETCH) ] = {
+ [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+ [ C(RESULT_MISS) ] = { CACHE_OP_UNSUPPORTED },
+ },
+},
+[C(LL)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = { 0x0c, PIC_LOWER, },
+ [C(RESULT_MISS)] = { 0x0c, PIC_UPPER, },
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = { 0x0c, PIC_LOWER },
+ [C(RESULT_MISS)] = { 0x0c, PIC_UPPER },
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+ [C(RESULT_MISS)] = { CACHE_OP_UNSUPPORTED },
+ },
+},
+[C(DTLB)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+ [C(RESULT_MISS)] = { 0x12, PIC_UPPER, },
+ },
+ [ C(OP_WRITE) ] = {
+ [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+ [ C(RESULT_MISS) ] = { CACHE_OP_UNSUPPORTED },
+ },
+ [ C(OP_PREFETCH) ] = {
+ [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+ [ C(RESULT_MISS) ] = { CACHE_OP_UNSUPPORTED },
+ },
+},
+[C(ITLB)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+ [C(RESULT_MISS)] = { 0x11, PIC_UPPER, },
+ },
+ [ C(OP_WRITE) ] = {
+ [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+ [ C(RESULT_MISS) ] = { CACHE_OP_UNSUPPORTED },
+ },
+ [ C(OP_PREFETCH) ] = {
+ [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+ [ C(RESULT_MISS) ] = { CACHE_OP_UNSUPPORTED },
+ },
+},
+[C(BPU)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+ [C(RESULT_MISS)] = { CACHE_OP_UNSUPPORTED },
+ },
+ [ C(OP_WRITE) ] = {
+ [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+ [ C(RESULT_MISS) ] = { CACHE_OP_UNSUPPORTED },
+ },
+ [ C(OP_PREFETCH) ] = {
+ [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+ [ C(RESULT_MISS) ] = { CACHE_OP_UNSUPPORTED },
+ },
+},
+};
+
+static const struct sparc_pmu ultra3_pmu = {
+ .event_map = ultra3_event_map,
+ .cache_map = &ultra3_cache_map,
+ .max_events = ARRAY_SIZE(ultra3_perfmon_event_map),
+ .upper_shift = 11,
+ .lower_shift = 4,
+ .event_mask = 0x3f,
+ .upper_nop = 0x1c,
+ .lower_nop = 0x14,
+};
+
+/* Niagara1 is very limited. The upper PIC is hard-locked to count
+ * only instructions, so it is free running which creates all kinds of
+ * problems. Some hardware designs make one wonder if the creator
+ * even looked at how this stuff gets used by software.
+ */
+static const struct perf_event_map niagara1_perfmon_event_map[] = {
+ [PERF_COUNT_HW_CPU_CYCLES] = { 0x00, PIC_UPPER },
+ [PERF_COUNT_HW_INSTRUCTIONS] = { 0x00, PIC_UPPER },
+ [PERF_COUNT_HW_CACHE_REFERENCES] = { 0, PIC_NONE },
+ [PERF_COUNT_HW_CACHE_MISSES] = { 0x03, PIC_LOWER },
+};
+
+static const struct perf_event_map *niagara1_event_map(int event_id)
+{
+ return &niagara1_perfmon_event_map[event_id];
+}
+
+static const cache_map_t niagara1_cache_map = {
+[C(L1D)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+ [C(RESULT_MISS)] = { 0x03, PIC_LOWER, },
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+ [C(RESULT_MISS)] = { 0x03, PIC_LOWER, },
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+ [C(RESULT_MISS)] = { CACHE_OP_UNSUPPORTED },
+ },
+},
+[C(L1I)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = { 0x00, PIC_UPPER },
+ [C(RESULT_MISS)] = { 0x02, PIC_LOWER, },
+ },
+ [ C(OP_WRITE) ] = {
+ [ C(RESULT_ACCESS) ] = { CACHE_OP_NONSENSE },
+ [ C(RESULT_MISS) ] = { CACHE_OP_NONSENSE },
+ },
+ [ C(OP_PREFETCH) ] = {
+ [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+ [ C(RESULT_MISS) ] = { CACHE_OP_UNSUPPORTED },
+ },
+},
+[C(LL)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+ [C(RESULT_MISS)] = { 0x07, PIC_LOWER, },
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+ [C(RESULT_MISS)] = { 0x07, PIC_LOWER, },
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+ [C(RESULT_MISS)] = { CACHE_OP_UNSUPPORTED },
+ },
+},
+[C(DTLB)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+ [C(RESULT_MISS)] = { 0x05, PIC_LOWER, },
+ },
+ [ C(OP_WRITE) ] = {
+ [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+ [ C(RESULT_MISS) ] = { CACHE_OP_UNSUPPORTED },
+ },
+ [ C(OP_PREFETCH) ] = {
+ [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+ [ C(RESULT_MISS) ] = { CACHE_OP_UNSUPPORTED },
+ },
+},
+[C(ITLB)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+ [C(RESULT_MISS)] = { 0x04, PIC_LOWER, },
+ },
+ [ C(OP_WRITE) ] = {
+ [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+ [ C(RESULT_MISS) ] = { CACHE_OP_UNSUPPORTED },
+ },
+ [ C(OP_PREFETCH) ] = {
+ [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+ [ C(RESULT_MISS) ] = { CACHE_OP_UNSUPPORTED },
+ },
+},
+[C(BPU)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+ [C(RESULT_MISS)] = { CACHE_OP_UNSUPPORTED },
+ },
+ [ C(OP_WRITE) ] = {
+ [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+ [ C(RESULT_MISS) ] = { CACHE_OP_UNSUPPORTED },
+ },
+ [ C(OP_PREFETCH) ] = {
+ [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+ [ C(RESULT_MISS) ] = { CACHE_OP_UNSUPPORTED },
+ },
+},
+};
+
+static const struct sparc_pmu niagara1_pmu = {
+ .event_map = niagara1_event_map,
+ .cache_map = &niagara1_cache_map,
+ .max_events = ARRAY_SIZE(niagara1_perfmon_event_map),
+ .upper_shift = 0,
+ .lower_shift = 4,
+ .event_mask = 0x7,
+ .upper_nop = 0x0,
+ .lower_nop = 0x0,
+};
+
+static const struct perf_event_map niagara2_perfmon_event_map[] = {
+ [PERF_COUNT_HW_CPU_CYCLES] = { 0x02ff, PIC_UPPER | PIC_LOWER },
+ [PERF_COUNT_HW_INSTRUCTIONS] = { 0x02ff, PIC_UPPER | PIC_LOWER },
+ [PERF_COUNT_HW_CACHE_REFERENCES] = { 0x0208, PIC_UPPER | PIC_LOWER },
+ [PERF_COUNT_HW_CACHE_MISSES] = { 0x0302, PIC_UPPER | PIC_LOWER },
+ [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = { 0x0201, PIC_UPPER | PIC_LOWER },
+ [PERF_COUNT_HW_BRANCH_MISSES] = { 0x0202, PIC_UPPER | PIC_LOWER },
+};
+
+static const struct perf_event_map *niagara2_event_map(int event_id)
+{
+ return &niagara2_perfmon_event_map[event_id];
+}
+
+static const cache_map_t niagara2_cache_map = {
+[C(L1D)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = { 0x0208, PIC_UPPER | PIC_LOWER, },
+ [C(RESULT_MISS)] = { 0x0302, PIC_UPPER | PIC_LOWER, },
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = { 0x0210, PIC_UPPER | PIC_LOWER, },
+ [C(RESULT_MISS)] = { 0x0302, PIC_UPPER | PIC_LOWER, },
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+ [C(RESULT_MISS)] = { CACHE_OP_UNSUPPORTED },
+ },
+},
+[C(L1I)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = { 0x02ff, PIC_UPPER | PIC_LOWER, },
+ [C(RESULT_MISS)] = { 0x0301, PIC_UPPER | PIC_LOWER, },
+ },
+ [ C(OP_WRITE) ] = {
+ [ C(RESULT_ACCESS) ] = { CACHE_OP_NONSENSE },
+ [ C(RESULT_MISS) ] = { CACHE_OP_NONSENSE },
+ },
+ [ C(OP_PREFETCH) ] = {
+ [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+ [ C(RESULT_MISS) ] = { CACHE_OP_UNSUPPORTED },
+ },
+},
+[C(LL)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = { 0x0208, PIC_UPPER | PIC_LOWER, },
+ [C(RESULT_MISS)] = { 0x0330, PIC_UPPER | PIC_LOWER, },
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = { 0x0210, PIC_UPPER | PIC_LOWER, },
+ [C(RESULT_MISS)] = { 0x0320, PIC_UPPER | PIC_LOWER, },
+ },
+ [C(OP_PREFETCH)] = {
+ [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+ [C(RESULT_MISS)] = { CACHE_OP_UNSUPPORTED },
+ },
+},
+[C(DTLB)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+ [C(RESULT_MISS)] = { 0x0b08, PIC_UPPER | PIC_LOWER, },
+ },
+ [ C(OP_WRITE) ] = {
+ [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+ [ C(RESULT_MISS) ] = { CACHE_OP_UNSUPPORTED },
+ },
+ [ C(OP_PREFETCH) ] = {
+ [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+ [ C(RESULT_MISS) ] = { CACHE_OP_UNSUPPORTED },
+ },
+},
+[C(ITLB)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+ [C(RESULT_MISS)] = { 0xb04, PIC_UPPER | PIC_LOWER, },
+ },
+ [ C(OP_WRITE) ] = {
+ [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+ [ C(RESULT_MISS) ] = { CACHE_OP_UNSUPPORTED },
+ },
+ [ C(OP_PREFETCH) ] = {
+ [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+ [ C(RESULT_MISS) ] = { CACHE_OP_UNSUPPORTED },
+ },
+},
+[C(BPU)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = { CACHE_OP_UNSUPPORTED },
+ [C(RESULT_MISS)] = { CACHE_OP_UNSUPPORTED },
+ },
+ [ C(OP_WRITE) ] = {
+ [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+ [ C(RESULT_MISS) ] = { CACHE_OP_UNSUPPORTED },
+ },
+ [ C(OP_PREFETCH) ] = {
+ [ C(RESULT_ACCESS) ] = { CACHE_OP_UNSUPPORTED },
+ [ C(RESULT_MISS) ] = { CACHE_OP_UNSUPPORTED },
+ },
+},
+};
+
+static const struct sparc_pmu niagara2_pmu = {
+ .event_map = niagara2_event_map,
+ .cache_map = &niagara2_cache_map,
+ .max_events = ARRAY_SIZE(niagara2_perfmon_event_map),
+ .upper_shift = 19,
+ .lower_shift = 6,
+ .event_mask = 0xfff,
+ .hv_bit = 0x8,
+ .irq_bit = 0x30,
+ .upper_nop = 0x220,
+ .lower_nop = 0x220,
+};
+
+static const struct sparc_pmu *sparc_pmu __read_mostly;
+
+static u64 event_encoding(u64 event_id, int idx)
+{
+ if (idx == PIC_UPPER_INDEX)
+ event_id <<= sparc_pmu->upper_shift;
+ else
+ event_id <<= sparc_pmu->lower_shift;
+ return event_id;
+}
+
+static u64 mask_for_index(int idx)
+{
+ return event_encoding(sparc_pmu->event_mask, idx);
+}
+
+static u64 nop_for_index(int idx)
+{
+ return event_encoding(idx == PIC_UPPER_INDEX ?
+ sparc_pmu->upper_nop :
+ sparc_pmu->lower_nop, idx);
+}
+
+static inline void sparc_pmu_enable_event(struct cpu_hw_events *cpuc, struct hw_perf_event *hwc, int idx)
+{
+ u64 val, mask = mask_for_index(idx);
+
+ val = cpuc->pcr;
+ val &= ~mask;
+ val |= hwc->config;
+ cpuc->pcr = val;
+
+ pcr_ops->write(cpuc->pcr);
+}
+
+static inline void sparc_pmu_disable_event(struct cpu_hw_events *cpuc, struct hw_perf_event *hwc, int idx)
+{
+ u64 mask = mask_for_index(idx);
+ u64 nop = nop_for_index(idx);
+ u64 val;
+
+ val = cpuc->pcr;
+ val &= ~mask;
+ val |= nop;
+ cpuc->pcr = val;
+
+ pcr_ops->write(cpuc->pcr);
+}
+
+void hw_perf_enable(void)
+{
+ struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+ u64 val;
+ int i;
+
+ if (cpuc->enabled)
+ return;
+
+ cpuc->enabled = 1;
+ barrier();
+
+ val = cpuc->pcr;
+
+ for (i = 0; i < MAX_HWEVENTS; i++) {
+ struct perf_event *cp = cpuc->events[i];
+ struct hw_perf_event *hwc;
+
+ if (!cp)
+ continue;
+ hwc = &cp->hw;
+ val |= hwc->config_base;
+ }
+
+ cpuc->pcr = val;
+
+ pcr_ops->write(cpuc->pcr);
+}
+
+void hw_perf_disable(void)
+{
+ struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+ u64 val;
+
+ if (!cpuc->enabled)
+ return;
+
+ cpuc->enabled = 0;
+
+ val = cpuc->pcr;
+ val &= ~(PCR_UTRACE | PCR_STRACE |
+ sparc_pmu->hv_bit | sparc_pmu->irq_bit);
+ cpuc->pcr = val;
+
+ pcr_ops->write(cpuc->pcr);
+}
+
+static u32 read_pmc(int idx)
+{
+ u64 val;
+
+ read_pic(val);
+ if (idx == PIC_UPPER_INDEX)
+ val >>= 32;
+
+ return val & 0xffffffff;
+}
+
+static void write_pmc(int idx, u64 val)
+{
+ u64 shift, mask, pic;
+
+ shift = 0;
+ if (idx == PIC_UPPER_INDEX)
+ shift = 32;
+
+ mask = ((u64) 0xffffffff) << shift;
+ val <<= shift;
+
+ read_pic(pic);
+ pic &= ~mask;
+ pic |= val;
+ write_pic(pic);
+}
+
+static int sparc_perf_event_set_period(struct perf_event *event,
+ struct hw_perf_event *hwc, int idx)
+{
+ s64 left = atomic64_read(&hwc->period_left);
+ s64 period = hwc->sample_period;
+ int ret = 0;
+
+ if (unlikely(left <= -period)) {
+ left = period;
+ atomic64_set(&hwc->period_left, left);
+ hwc->last_period = period;
+ ret = 1;
+ }
+
+ if (unlikely(left <= 0)) {
+ left += period;
+ atomic64_set(&hwc->period_left, left);
+ hwc->last_period = period;
+ ret = 1;
+ }
+ if (left > MAX_PERIOD)
+ left = MAX_PERIOD;
+
+ atomic64_set(&hwc->prev_count, (u64)-left);
+
+ write_pmc(idx, (u64)(-left) & 0xffffffff);
+
+ perf_event_update_userpage(event);
+
+ return ret;
+}
+
+static int sparc_pmu_enable(struct perf_event *event)
+{
+ struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+ struct hw_perf_event *hwc = &event->hw;
+ int idx = hwc->idx;
+
+ if (test_and_set_bit(idx, cpuc->used_mask))
+ return -EAGAIN;
+
+ sparc_pmu_disable_event(cpuc, hwc, idx);
+
+ cpuc->events[idx] = event;
+ set_bit(idx, cpuc->active_mask);
+
+ sparc_perf_event_set_period(event, hwc, idx);
+ sparc_pmu_enable_event(cpuc, hwc, idx);
+ perf_event_update_userpage(event);
+ return 0;
+}
+
+static u64 sparc_perf_event_update(struct perf_event *event,
+ struct hw_perf_event *hwc, int idx)
+{
+ int shift = 64 - 32;
+ u64 prev_raw_count, new_raw_count;
+ s64 delta;
+
+again:
+ prev_raw_count = atomic64_read(&hwc->prev_count);
+ new_raw_count = read_pmc(idx);
+
+ if (atomic64_cmpxchg(&hwc->prev_count, prev_raw_count,
+ new_raw_count) != prev_raw_count)
+ goto again;
+
+ delta = (new_raw_count << shift) - (prev_raw_count << shift);
+ delta >>= shift;
+
+ atomic64_add(delta, &event->count);
+ atomic64_sub(delta, &hwc->period_left);
+
+ return new_raw_count;
+}
+
+static void sparc_pmu_disable(struct perf_event *event)
+{
+ struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+ struct hw_perf_event *hwc = &event->hw;
+ int idx = hwc->idx;
+
+ clear_bit(idx, cpuc->active_mask);
+ sparc_pmu_disable_event(cpuc, hwc, idx);
+
+ barrier();
+
+ sparc_perf_event_update(event, hwc, idx);
+ cpuc->events[idx] = NULL;
+ clear_bit(idx, cpuc->used_mask);
+
+ perf_event_update_userpage(event);
+}
+
+static void sparc_pmu_read(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+
+ sparc_perf_event_update(event, hwc, hwc->idx);
+}
+
+static void sparc_pmu_unthrottle(struct perf_event *event)
+{
+ struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+ struct hw_perf_event *hwc = &event->hw;
+
+ sparc_pmu_enable_event(cpuc, hwc, hwc->idx);
+}
+
+static atomic_t active_events = ATOMIC_INIT(0);
+static DEFINE_MUTEX(pmc_grab_mutex);
+
+static void perf_stop_nmi_watchdog(void *unused)
+{
+ struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+
+ stop_nmi_watchdog(NULL);
+ cpuc->pcr = pcr_ops->read();
+}
+
+void perf_event_grab_pmc(void)
+{
+ if (atomic_inc_not_zero(&active_events))
+ return;
+
+ mutex_lock(&pmc_grab_mutex);
+ if (atomic_read(&active_events) == 0) {
+ if (atomic_read(&nmi_active) > 0) {
+ on_each_cpu(perf_stop_nmi_watchdog, NULL, 1);
+ BUG_ON(atomic_read(&nmi_active) != 0);
+ }
+ atomic_inc(&active_events);
+ }
+ mutex_unlock(&pmc_grab_mutex);
+}
+
+void perf_event_release_pmc(void)
+{
+ if (atomic_dec_and_mutex_lock(&active_events, &pmc_grab_mutex)) {
+ if (atomic_read(&nmi_active) == 0)
+ on_each_cpu(start_nmi_watchdog, NULL, 1);
+ mutex_unlock(&pmc_grab_mutex);
+ }
+}
+
+static const struct perf_event_map *sparc_map_cache_event(u64 config)
+{
+ unsigned int cache_type, cache_op, cache_result;
+ const struct perf_event_map *pmap;
+
+ if (!sparc_pmu->cache_map)
+ return ERR_PTR(-ENOENT);
+
+ cache_type = (config >> 0) & 0xff;
+ if (cache_type >= PERF_COUNT_HW_CACHE_MAX)
+ return ERR_PTR(-EINVAL);
+
+ cache_op = (config >> 8) & 0xff;
+ if (cache_op >= PERF_COUNT_HW_CACHE_OP_MAX)
+ return ERR_PTR(-EINVAL);
+
+ cache_result = (config >> 16) & 0xff;
+ if (cache_result >= PERF_COUNT_HW_CACHE_RESULT_MAX)
+ return ERR_PTR(-EINVAL);
+
+ pmap = &((*sparc_pmu->cache_map)[cache_type][cache_op][cache_result]);
+
+ if (pmap->encoding == CACHE_OP_UNSUPPORTED)
+ return ERR_PTR(-ENOENT);
+
+ if (pmap->encoding == CACHE_OP_NONSENSE)
+ return ERR_PTR(-EINVAL);
+
+ return pmap;
+}
+
+static void hw_perf_event_destroy(struct perf_event *event)
+{
+ perf_event_release_pmc();
+}
+
+/* Make sure all events can be scheduled into the hardware at
+ * the same time. This is simplified by the fact that we only
+ * need to support 2 simultaneous HW events.
+ */
+static int sparc_check_constraints(unsigned long *events, int n_ev)
+{
+ if (n_ev <= perf_max_events) {
+ u8 msk1, msk2;
+ u16 dummy;
+
+ if (n_ev == 1)
+ return 0;
+ BUG_ON(n_ev != 2);
+ perf_event_decode(events[0], &dummy, &msk1);
+ perf_event_decode(events[1], &dummy, &msk2);
+
+ /* If both events can go on any counter, OK. */
+ if (msk1 == (PIC_UPPER | PIC_LOWER) &&
+ msk2 == (PIC_UPPER | PIC_LOWER))
+ return 0;
+
+ /* If one event is limited to a specific counter,
+ * and the other can go on both, OK.
+ */
+ if ((msk1 == PIC_UPPER || msk1 == PIC_LOWER) &&
+ msk2 == (PIC_UPPER | PIC_LOWER))
+ return 0;
+ if ((msk2 == PIC_UPPER || msk2 == PIC_LOWER) &&
+ msk1 == (PIC_UPPER | PIC_LOWER))
+ return 0;
+
+ /* If the events are fixed to different counters, OK. */
+ if ((msk1 == PIC_UPPER && msk2 == PIC_LOWER) ||
+ (msk1 == PIC_LOWER && msk2 == PIC_UPPER))
+ return 0;
+
+ /* Otherwise, there is a conflict. */
+ }
+
+ return -1;
+}
+
+static int check_excludes(struct perf_event **evts, int n_prev, int n_new)
+{
+ int eu = 0, ek = 0, eh = 0;
+ struct perf_event *event;
+ int i, n, first;
+
+ n = n_prev + n_new;
+ if (n <= 1)
+ return 0;
+
+ first = 1;
+ for (i = 0; i < n; i++) {
+ event = evts[i];
+ if (first) {
+ eu = event->attr.exclude_user;
+ ek = event->attr.exclude_kernel;
+ eh = event->attr.exclude_hv;
+ first = 0;
+ } else if (event->attr.exclude_user != eu ||
+ event->attr.exclude_kernel != ek ||
+ event->attr.exclude_hv != eh) {
+ return -EAGAIN;
+ }
+ }
+
+ return 0;
+}
+
+static int collect_events(struct perf_event *group, int max_count,
+ struct perf_event *evts[], unsigned long *events)
+{
+ struct perf_event *event;
+ int n = 0;
+
+ if (!is_software_event(group)) {
+ if (n >= max_count)
+ return -1;
+ evts[n] = group;
+ events[n++] = group->hw.event_base;
+ }
+ list_for_each_entry(event, &group->sibling_list, group_entry) {
+ if (!is_software_event(event) &&
+ event->state != PERF_EVENT_STATE_OFF) {
+ if (n >= max_count)
+ return -1;
+ evts[n] = event;
+ events[n++] = event->hw.event_base;
+ }
+ }
+ return n;
+}
+
+static int __hw_perf_event_init(struct perf_event *event)
+{
+ struct perf_event_attr *attr = &event->attr;
+ struct perf_event *evts[MAX_HWEVENTS];
+ struct hw_perf_event *hwc = &event->hw;
+ unsigned long events[MAX_HWEVENTS];
+ const struct perf_event_map *pmap;
+ u64 enc;
+ int n;
+
+ if (atomic_read(&nmi_active) < 0)
+ return -ENODEV;
+
+ if (attr->type == PERF_TYPE_HARDWARE) {
+ if (attr->config >= sparc_pmu->max_events)
+ return -EINVAL;
+ pmap = sparc_pmu->event_map(attr->config);
+ } else if (attr->type == PERF_TYPE_HW_CACHE) {
+ pmap = sparc_map_cache_event(attr->config);
+ if (IS_ERR(pmap))
+ return PTR_ERR(pmap);
+ } else
+ return -EOPNOTSUPP;
+
+ /* We save the enable bits in the config_base. So to
+ * turn off sampling just write 'config', and to enable
+ * things write 'config | config_base'.
+ */
+ hwc->config_base = sparc_pmu->irq_bit;
+ if (!attr->exclude_user)
+ hwc->config_base |= PCR_UTRACE;
+ if (!attr->exclude_kernel)
+ hwc->config_base |= PCR_STRACE;
+ if (!attr->exclude_hv)
+ hwc->config_base |= sparc_pmu->hv_bit;
+
+ hwc->event_base = perf_event_encode(pmap);
+
+ enc = pmap->encoding;
+
+ n = 0;
+ if (event->group_leader != event) {
+ n = collect_events(event->group_leader,
+ perf_max_events - 1,
+ evts, events);
+ if (n < 0)
+ return -EINVAL;
+ }
+ events[n] = hwc->event_base;
+ evts[n] = event;
+
+ if (check_excludes(evts, n, 1))
+ return -EINVAL;
+
+ if (sparc_check_constraints(events, n + 1))
+ return -EINVAL;
+
+ /* Try to do all error checking before this point, as unwinding
+ * state after grabbing the PMC is difficult.
+ */
+ perf_event_grab_pmc();
+ event->destroy = hw_perf_event_destroy;
+
+ if (!hwc->sample_period) {
+ hwc->sample_period = MAX_PERIOD;
+ hwc->last_period = hwc->sample_period;
+ atomic64_set(&hwc->period_left, hwc->sample_period);
+ }
+
+ if (pmap->pic_mask & PIC_UPPER) {
+ hwc->idx = PIC_UPPER_INDEX;
+ enc <<= sparc_pmu->upper_shift;
+ } else {
+ hwc->idx = PIC_LOWER_INDEX;
+ enc <<= sparc_pmu->lower_shift;
+ }
+
+ hwc->config |= enc;
+ return 0;
+}
+
+static const struct pmu pmu = {
+ .enable = sparc_pmu_enable,
+ .disable = sparc_pmu_disable,
+ .read = sparc_pmu_read,
+ .unthrottle = sparc_pmu_unthrottle,
+};
+
+const struct pmu *hw_perf_event_init(struct perf_event *event)
+{
+ int err = __hw_perf_event_init(event);
+
+ if (err)
+ return ERR_PTR(err);
+ return &pmu;
+}
+
+void perf_event_print_debug(void)
+{
+ unsigned long flags;
+ u64 pcr, pic;
+ int cpu;
+
+ if (!sparc_pmu)
+ return;
+
+ local_irq_save(flags);
+
+ cpu = smp_processor_id();
+
+ pcr = pcr_ops->read();
+ read_pic(pic);
+
+ pr_info("\n");
+ pr_info("CPU#%d: PCR[%016llx] PIC[%016llx]\n",
+ cpu, pcr, pic);
+
+ local_irq_restore(flags);
+}
+
+static int __kprobes perf_event_nmi_handler(struct notifier_block *self,
+ unsigned long cmd, void *__args)
+{
+ struct die_args *args = __args;
+ struct perf_sample_data data;
+ struct cpu_hw_events *cpuc;
+ struct pt_regs *regs;
+ int idx;
+
+ if (!atomic_read(&active_events))
+ return NOTIFY_DONE;
+
+ switch (cmd) {
+ case DIE_NMI:
+ break;
+
+ default:
+ return NOTIFY_DONE;
+ }
+
+ regs = args->regs;
+
+ data.addr = 0;
+
+ cpuc = &__get_cpu_var(cpu_hw_events);
+ for (idx = 0; idx < MAX_HWEVENTS; idx++) {
+ struct perf_event *event = cpuc->events[idx];
+ struct hw_perf_event *hwc;
+ u64 val;
+
+ if (!test_bit(idx, cpuc->active_mask))
+ continue;
+ hwc = &event->hw;
+ val = sparc_perf_event_update(event, hwc, idx);
+ if (val & (1ULL << 31))
+ continue;
+
+ data.period = event->hw.last_period;
+ if (!sparc_perf_event_set_period(event, hwc, idx))
+ continue;
+
+ if (perf_event_overflow(event, 1, &data, regs))
+ sparc_pmu_disable_event(cpuc, hwc, idx);
+ }
+
+ return NOTIFY_STOP;
+}
+
+static __read_mostly struct notifier_block perf_event_nmi_notifier = {
+ .notifier_call = perf_event_nmi_handler,
+};
+
+static bool __init supported_pmu(void)
+{
+ if (!strcmp(sparc_pmu_type, "ultra3") ||
+ !strcmp(sparc_pmu_type, "ultra3+") ||
+ !strcmp(sparc_pmu_type, "ultra3i") ||
+ !strcmp(sparc_pmu_type, "ultra4+")) {
+ sparc_pmu = &ultra3_pmu;
+ return true;
+ }
+ if (!strcmp(sparc_pmu_type, "niagara")) {
+ sparc_pmu = &niagara1_pmu;
+ return true;
+ }
+ if (!strcmp(sparc_pmu_type, "niagara2")) {
+ sparc_pmu = &niagara2_pmu;
+ return true;
+ }
+ return false;
+}
+
+void __init init_hw_perf_events(void)
+{
+ pr_info("Performance events: ");
+
+ if (!supported_pmu()) {
+ pr_cont("No support for PMU type '%s'\n", sparc_pmu_type);
+ return;
+ }
+
+ pr_cont("Supported PMU type is '%s'\n", sparc_pmu_type);
+
+ /* All sparc64 PMUs currently have 2 events. But this simple
+ * driver only supports one active event at a time.
+ */
+ perf_max_events = 1;
+
+ register_die_notifier(&perf_event_nmi_notifier);
+}
diff --git a/arch/sparc/kernel/prom_common.c b/arch/sparc/kernel/prom_common.c
index 138910c67206..d80a65d9e893 100644
--- a/arch/sparc/kernel/prom_common.c
+++ b/arch/sparc/kernel/prom_common.c
@@ -79,6 +79,7 @@ int of_set_property(struct device_node *dp, const char *name, void *val, int len
err = -ENODEV;
+ mutex_lock(&of_set_property_mutex);
write_lock(&devtree_lock);
prevp = &dp->properties;
while (*prevp) {
@@ -88,9 +89,7 @@ int of_set_property(struct device_node *dp, const char *name, void *val, int len
void *old_val = prop->value;
int ret;
- mutex_lock(&of_set_property_mutex);
ret = prom_setprop(dp->node, name, val, len);
- mutex_unlock(&of_set_property_mutex);
err = -EINVAL;
if (ret >= 0) {
@@ -109,6 +108,7 @@ int of_set_property(struct device_node *dp, const char *name, void *val, int len
prevp = &(*prevp)->next;
}
write_unlock(&devtree_lock);
+ mutex_unlock(&of_set_property_mutex);
/* XXX Upate procfs if necessary... */
diff --git a/arch/sparc/kernel/smp_64.c b/arch/sparc/kernel/smp_64.c
index ff68373ce6d6..aa36223497b9 100644
--- a/arch/sparc/kernel/smp_64.c
+++ b/arch/sparc/kernel/smp_64.c
@@ -1420,7 +1420,7 @@ static void __init pcpu_free_bootmem(void *ptr, size_t size)
free_bootmem(__pa(ptr), size);
}
-static int pcpu_cpu_distance(unsigned int from, unsigned int to)
+static int __init pcpu_cpu_distance(unsigned int from, unsigned int to)
{
if (cpu_to_node(from) == cpu_to_node(to))
return LOCAL_DISTANCE;
@@ -1428,18 +1428,53 @@ static int pcpu_cpu_distance(unsigned int from, unsigned int to)
return REMOTE_DISTANCE;
}
+static void __init pcpu_populate_pte(unsigned long addr)
+{
+ pgd_t *pgd = pgd_offset_k(addr);
+ pud_t *pud;
+ pmd_t *pmd;
+
+ pud = pud_offset(pgd, addr);
+ if (pud_none(*pud)) {
+ pmd_t *new;
+
+ new = __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, PAGE_SIZE);
+ pud_populate(&init_mm, pud, new);
+ }
+
+ pmd = pmd_offset(pud, addr);
+ if (!pmd_present(*pmd)) {
+ pte_t *new;
+
+ new = __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, PAGE_SIZE);
+ pmd_populate_kernel(&init_mm, pmd, new);
+ }
+}
+
void __init setup_per_cpu_areas(void)
{
unsigned long delta;
unsigned int cpu;
- int rc;
-
- rc = pcpu_embed_first_chunk(PERCPU_MODULE_RESERVE,
- PERCPU_DYNAMIC_RESERVE, 4 << 20,
- pcpu_cpu_distance, pcpu_alloc_bootmem,
- pcpu_free_bootmem);
- if (rc)
- panic("failed to initialize first chunk (%d)", rc);
+ int rc = -EINVAL;
+
+ if (pcpu_chosen_fc != PCPU_FC_PAGE) {
+ rc = pcpu_embed_first_chunk(PERCPU_MODULE_RESERVE,
+ PERCPU_DYNAMIC_RESERVE, 4 << 20,
+ pcpu_cpu_distance,
+ pcpu_alloc_bootmem,
+ pcpu_free_bootmem);
+ if (rc)
+ pr_warning("PERCPU: %s allocator failed (%d), "
+ "falling back to page size\n",
+ pcpu_fc_names[pcpu_chosen_fc], rc);
+ }
+ if (rc < 0)
+ rc = pcpu_page_first_chunk(PERCPU_MODULE_RESERVE,
+ pcpu_alloc_bootmem,
+ pcpu_free_bootmem,
+ pcpu_populate_pte);
+ if (rc < 0)
+ panic("cannot initialize percpu area (err=%d)", rc);
delta = (unsigned long)pcpu_base_addr - (unsigned long)__per_cpu_start;
for_each_possible_cpu(cpu)
diff --git a/arch/sparc/kernel/sys_sparc32.c b/arch/sparc/kernel/sys_sparc32.c
index f5000a460c05..04e28b2671c8 100644
--- a/arch/sparc/kernel/sys_sparc32.c
+++ b/arch/sparc/kernel/sys_sparc32.c
@@ -16,7 +16,6 @@
#include <linux/signal.h>
#include <linux/resource.h>
#include <linux/times.h>
-#include <linux/utsname.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/sem.h>
diff --git a/arch/sparc/kernel/systbls.h b/arch/sparc/kernel/systbls.h
index 15c2d752b2bc..a63c5d2d9849 100644
--- a/arch/sparc/kernel/systbls.h
+++ b/arch/sparc/kernel/systbls.h
@@ -3,10 +3,11 @@
#include <linux/kernel.h>
#include <linux/types.h>
-#include <linux/utsname.h>
#include <asm/utrap.h>
#include <asm/signal.h>
+struct new_utsname;
+
extern asmlinkage unsigned long sys_getpagesize(void);
extern asmlinkage unsigned long sparc_brk(unsigned long brk);
extern asmlinkage long sparc_pipe(struct pt_regs *regs);
diff --git a/arch/sparc/kernel/systbls_32.S b/arch/sparc/kernel/systbls_32.S
index 04181577cb65..0f1658d37490 100644
--- a/arch/sparc/kernel/systbls_32.S
+++ b/arch/sparc/kernel/systbls_32.S
@@ -82,5 +82,5 @@ sys_call_table:
/*310*/ .long sys_utimensat, sys_signalfd, sys_timerfd_create, sys_eventfd, sys_fallocate
/*315*/ .long sys_timerfd_settime, sys_timerfd_gettime, sys_signalfd4, sys_eventfd2, sys_epoll_create1
/*320*/ .long sys_dup3, sys_pipe2, sys_inotify_init1, sys_accept4, sys_preadv
-/*325*/ .long sys_pwritev, sys_rt_tgsigqueueinfo, sys_perf_counter_open
+/*325*/ .long sys_pwritev, sys_rt_tgsigqueueinfo, sys_perf_event_open
diff --git a/arch/sparc/kernel/systbls_64.S b/arch/sparc/kernel/systbls_64.S
index 91b06b7f7acf..009825f6e73c 100644
--- a/arch/sparc/kernel/systbls_64.S
+++ b/arch/sparc/kernel/systbls_64.S
@@ -83,7 +83,7 @@ sys_call_table32:
/*310*/ .word compat_sys_utimensat, compat_sys_signalfd, sys_timerfd_create, sys_eventfd, compat_sys_fallocate
.word compat_sys_timerfd_settime, compat_sys_timerfd_gettime, compat_sys_signalfd4, sys_eventfd2, sys_epoll_create1
/*320*/ .word sys_dup3, sys_pipe2, sys_inotify_init1, sys_accept4, compat_sys_preadv
- .word compat_sys_pwritev, compat_sys_rt_tgsigqueueinfo, sys_perf_counter_open
+ .word compat_sys_pwritev, compat_sys_rt_tgsigqueueinfo, sys_perf_event_open
#endif /* CONFIG_COMPAT */
@@ -158,4 +158,4 @@ sys_call_table:
/*310*/ .word sys_utimensat, sys_signalfd, sys_timerfd_create, sys_eventfd, sys_fallocate
.word sys_timerfd_settime, sys_timerfd_gettime, sys_signalfd4, sys_eventfd2, sys_epoll_create1
/*320*/ .word sys_dup3, sys_pipe2, sys_inotify_init1, sys_accept4, sys_preadv
- .word sys_pwritev, sys_rt_tgsigqueueinfo, sys_perf_counter_open
+ .word sys_pwritev, sys_rt_tgsigqueueinfo, sys_perf_event_open
diff --git a/arch/sparc/kernel/visemul.c b/arch/sparc/kernel/visemul.c
index b956fd71c131..d231cbd5c526 100644
--- a/arch/sparc/kernel/visemul.c
+++ b/arch/sparc/kernel/visemul.c
@@ -617,7 +617,7 @@ static void pmul(struct pt_regs *regs, unsigned int insn, unsigned int opf)
rs2 = fps_regval(f, RS2(insn));
rd_val = 0;
- src2 = (rs2 >> (opf == FMUL8x16AU_OPF) ? 16 : 0);
+ src2 = rs2 >> (opf == FMUL8x16AU_OPF ? 16 : 0);
for (byte = 0; byte < 4; byte++) {
u16 src1 = (rs1 >> (byte * 8)) & 0x00ff;
u32 prod = src1 * src2;
diff --git a/arch/sparc/mm/init_32.c b/arch/sparc/mm/init_32.c
index 54114ad0bdee..dc7c3b17a15f 100644
--- a/arch/sparc/mm/init_32.c
+++ b/arch/sparc/mm/init_32.c
@@ -472,7 +472,7 @@ void __init mem_init(void)
reservedpages++;
printk(KERN_INFO "Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, %dk init, %ldk highmem)\n",
- (unsigned long) nr_free_pages() << (PAGE_SHIFT-10),
+ nr_free_pages() << (PAGE_SHIFT-10),
num_physpages << (PAGE_SHIFT - 10),
codepages << (PAGE_SHIFT-10),
reservedpages << (PAGE_SHIFT - 10),
diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c
index a70a5e1904d9..1886d37d411b 100644
--- a/arch/sparc/mm/init_64.c
+++ b/arch/sparc/mm/init_64.c
@@ -265,7 +265,7 @@ static void flush_dcache(unsigned long pfn)
struct page *page;
page = pfn_to_page(pfn);
- if (page && page_mapping(page)) {
+ if (page) {
unsigned long pg_flags;
pg_flags = page->flags;
diff --git a/arch/sparc/mm/init_64.h b/arch/sparc/mm/init_64.h
index c2f772dbd556..77d1b313e344 100644
--- a/arch/sparc/mm/init_64.h
+++ b/arch/sparc/mm/init_64.h
@@ -45,7 +45,7 @@ extern void free_initmem(void);
#define VMEMMAP_ALIGN(x) (((x)+VMEMMAP_CHUNK-1UL)&VMEMMAP_CHUNK_MASK)
#define VMEMMAP_SIZE ((((1UL << MAX_PHYSADDR_BITS) >> PAGE_SHIFT) * \
- sizeof(struct page *)) >> VMEMMAP_CHUNK_SHIFT)
+ sizeof(struct page)) >> VMEMMAP_CHUNK_SHIFT)
extern unsigned long vmemmap_table[VMEMMAP_SIZE];
#endif
diff --git a/arch/sparc/oprofile/init.c b/arch/sparc/oprofile/init.c
index f97cb8b6ee5f..f9024bccff16 100644
--- a/arch/sparc/oprofile/init.c
+++ b/arch/sparc/oprofile/init.c
@@ -11,6 +11,7 @@
#include <linux/oprofile.h>
#include <linux/errno.h>
#include <linux/init.h>
+#include <linux/param.h> /* for HZ */
#ifdef CONFIG_SPARC64
#include <linux/notifier.h>