diff options
author | Zhigang Lu <zlu@tilera.com> | 2014-01-27 02:11:07 -0500 |
---|---|---|
committer | Chris Metcalf <cmetcalf@tilera.com> | 2014-03-07 11:19:47 -0500 |
commit | 8e3441ebab48c3537b1a9ae06fb7616a3332bd35 (patch) | |
tree | 6b2a748d7952827e121dd78aa0f4c6dcb449dfb9 | |
parent | 2e285458e62fe202cb81b831f41779c7e7e5cbe6 (diff) |
tile: Add support for handling PMC hardware
The PMC module is used by perf_events, oprofile and watchdogs.
Signed-off-by: Zhigang Lu <zlu@tilera.com>
Signed-off-by: Chris Metcalf <cmetcalf@tilera.com>
-rw-r--r-- | arch/tile/Kconfig | 4 | ||||
-rw-r--r-- | arch/tile/include/asm/pmc.h | 64 | ||||
-rw-r--r-- | arch/tile/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/tile/kernel/intvec_32.S | 13 | ||||
-rw-r--r-- | arch/tile/kernel/intvec_64.S | 13 | ||||
-rw-r--r-- | arch/tile/kernel/pmc.c | 121 |
6 files changed, 204 insertions, 12 deletions
diff --git a/arch/tile/Kconfig b/arch/tile/Kconfig index b3692ce78f90..3067b15e80d6 100644 --- a/arch/tile/Kconfig +++ b/arch/tile/Kconfig | |||
@@ -66,6 +66,10 @@ config HUGETLB_SUPER_PAGES | |||
66 | config GENERIC_TIME_VSYSCALL | 66 | config GENERIC_TIME_VSYSCALL |
67 | def_bool y | 67 | def_bool y |
68 | 68 | ||
69 | # Enable PMC if PERF_EVENTS, OPROFILE, or WATCHPOINTS are enabled. | ||
70 | config USE_PMC | ||
71 | bool | ||
72 | |||
69 | # FIXME: tilegx can implement a more efficient rwsem. | 73 | # FIXME: tilegx can implement a more efficient rwsem. |
70 | config RWSEM_GENERIC_SPINLOCK | 74 | config RWSEM_GENERIC_SPINLOCK |
71 | def_bool y | 75 | def_bool y |
diff --git a/arch/tile/include/asm/pmc.h b/arch/tile/include/asm/pmc.h new file mode 100644 index 000000000000..7ae3956d9008 --- /dev/null +++ b/arch/tile/include/asm/pmc.h | |||
@@ -0,0 +1,64 @@ | |||
1 | /* | ||
2 | * Copyright 2014 Tilera Corporation. All Rights Reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation, version 2. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
11 | * NON INFRINGEMENT. See the GNU General Public License for | ||
12 | * more details. | ||
13 | */ | ||
14 | |||
15 | #ifndef _ASM_TILE_PMC_H | ||
16 | #define _ASM_TILE_PMC_H | ||
17 | |||
18 | #include <linux/ptrace.h> | ||
19 | |||
20 | #define TILE_BASE_COUNTERS 2 | ||
21 | |||
22 | /* Bitfields below are derived from SPR PERF_COUNT_CTL*/ | ||
23 | #ifndef __tilegx__ | ||
24 | /* PERF_COUNT_CTL on TILEPro */ | ||
25 | #define TILE_CTL_EXCL_USER (1 << 7) /* exclude user level */ | ||
26 | #define TILE_CTL_EXCL_KERNEL (1 << 8) /* exclude kernel level */ | ||
27 | #define TILE_CTL_EXCL_HV (1 << 9) /* exclude hypervisor level */ | ||
28 | |||
29 | #define TILE_SEL_MASK 0x7f /* 7 bits for event SEL, | ||
30 | COUNT_0_SEL */ | ||
31 | #define TILE_PLM_MASK 0x780 /* 4 bits priv level msks, | ||
32 | COUNT_0_MASK*/ | ||
33 | #define TILE_EVENT_MASK (TILE_SEL_MASK | TILE_PLM_MASK) | ||
34 | |||
35 | #else /* __tilegx__*/ | ||
36 | /* PERF_COUNT_CTL on TILEGx*/ | ||
37 | #define TILE_CTL_EXCL_USER (1 << 10) /* exclude user level */ | ||
38 | #define TILE_CTL_EXCL_KERNEL (1 << 11) /* exclude kernel level */ | ||
39 | #define TILE_CTL_EXCL_HV (1 << 12) /* exclude hypervisor level */ | ||
40 | |||
41 | #define TILE_SEL_MASK 0x3f /* 6 bits for event SEL, | ||
42 | COUNT_0_SEL*/ | ||
43 | #define TILE_BOX_MASK 0x1c0 /* 3 bits box msks, | ||
44 | COUNT_0_BOX */ | ||
45 | #define TILE_PLM_MASK 0x3c00 /* 4 bits priv level msks, | ||
46 | COUNT_0_MASK */ | ||
47 | #define TILE_EVENT_MASK (TILE_SEL_MASK | TILE_BOX_MASK | TILE_PLM_MASK) | ||
48 | #endif /* __tilegx__*/ | ||
49 | |||
50 | /* Takes register and fault number. Returns error to disable the interrupt. */ | ||
51 | typedef int (*perf_irq_t)(struct pt_regs *, int); | ||
52 | |||
53 | int userspace_perf_handler(struct pt_regs *regs, int fault); | ||
54 | |||
55 | perf_irq_t reserve_pmc_hardware(perf_irq_t new_perf_irq); | ||
56 | void release_pmc_hardware(void); | ||
57 | |||
58 | unsigned long pmc_get_overflow(void); | ||
59 | void pmc_ack_overflow(unsigned long status); | ||
60 | |||
61 | void unmask_pmc_interrupts(void); | ||
62 | void mask_pmc_interrupts(void); | ||
63 | |||
64 | #endif /* _ASM_TILE_PMC_H */ | ||
diff --git a/arch/tile/kernel/Makefile b/arch/tile/kernel/Makefile index 27a2bf39dae8..71d835365c73 100644 --- a/arch/tile/kernel/Makefile +++ b/arch/tile/kernel/Makefile | |||
@@ -25,6 +25,7 @@ obj-$(CONFIG_PCI) += pci_gx.o | |||
25 | else | 25 | else |
26 | obj-$(CONFIG_PCI) += pci.o | 26 | obj-$(CONFIG_PCI) += pci.o |
27 | endif | 27 | endif |
28 | obj-$(CONFIG_USE_PMC) += pmc.o | ||
28 | obj-$(CONFIG_TILE_USB) += usb.o | 29 | obj-$(CONFIG_TILE_USB) += usb.o |
29 | obj-$(CONFIG_TILE_HVGLUE_TRACE) += hvglue_trace.o | 30 | obj-$(CONFIG_TILE_HVGLUE_TRACE) += hvglue_trace.o |
30 | obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o mcount_64.o | 31 | obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o mcount_64.o |
diff --git a/arch/tile/kernel/intvec_32.S b/arch/tile/kernel/intvec_32.S index 2cbe6d5dd6b0..605ffbda44ff 100644 --- a/arch/tile/kernel/intvec_32.S +++ b/arch/tile/kernel/intvec_32.S | |||
@@ -313,13 +313,13 @@ intvec_\vecname: | |||
313 | movei r3, 0 | 313 | movei r3, 0 |
314 | } | 314 | } |
315 | .else | 315 | .else |
316 | .ifc \c_routine, op_handle_perf_interrupt | 316 | .ifc \c_routine, handle_perf_interrupt |
317 | { | 317 | { |
318 | mfspr r2, PERF_COUNT_STS | 318 | mfspr r2, PERF_COUNT_STS |
319 | movei r3, -1 /* not used, but set for consistency */ | 319 | movei r3, -1 /* not used, but set for consistency */ |
320 | } | 320 | } |
321 | .else | 321 | .else |
322 | .ifc \c_routine, op_handle_aux_perf_interrupt | 322 | .ifc \c_routine, handle_perf_interrupt |
323 | { | 323 | { |
324 | mfspr r2, AUX_PERF_COUNT_STS | 324 | mfspr r2, AUX_PERF_COUNT_STS |
325 | movei r3, -1 /* not used, but set for consistency */ | 325 | movei r3, -1 /* not used, but set for consistency */ |
@@ -1835,8 +1835,9 @@ int_unalign: | |||
1835 | /* Include .intrpt array of interrupt vectors */ | 1835 | /* Include .intrpt array of interrupt vectors */ |
1836 | .section ".intrpt", "ax" | 1836 | .section ".intrpt", "ax" |
1837 | 1837 | ||
1838 | #define op_handle_perf_interrupt bad_intr | 1838 | #ifndef CONFIG_USE_PMC |
1839 | #define op_handle_aux_perf_interrupt bad_intr | 1839 | #define handle_perf_interrupt bad_intr |
1840 | #endif | ||
1840 | 1841 | ||
1841 | #ifndef CONFIG_HARDWALL | 1842 | #ifndef CONFIG_HARDWALL |
1842 | #define do_hardwall_trap bad_intr | 1843 | #define do_hardwall_trap bad_intr |
@@ -1877,7 +1878,7 @@ int_unalign: | |||
1877 | int_hand INT_IDN_AVAIL, IDN_AVAIL, bad_intr | 1878 | int_hand INT_IDN_AVAIL, IDN_AVAIL, bad_intr |
1878 | int_hand INT_UDN_AVAIL, UDN_AVAIL, bad_intr | 1879 | int_hand INT_UDN_AVAIL, UDN_AVAIL, bad_intr |
1879 | int_hand INT_PERF_COUNT, PERF_COUNT, \ | 1880 | int_hand INT_PERF_COUNT, PERF_COUNT, \ |
1880 | op_handle_perf_interrupt, handle_nmi | 1881 | handle_perf_interrupt, handle_nmi |
1881 | int_hand INT_INTCTRL_3, INTCTRL_3, bad_intr | 1882 | int_hand INT_INTCTRL_3, INTCTRL_3, bad_intr |
1882 | #if CONFIG_KERNEL_PL == 2 | 1883 | #if CONFIG_KERNEL_PL == 2 |
1883 | dc_dispatch INT_INTCTRL_2, INTCTRL_2 | 1884 | dc_dispatch INT_INTCTRL_2, INTCTRL_2 |
@@ -1902,7 +1903,7 @@ int_unalign: | |||
1902 | int_hand INT_SN_CPL, SN_CPL, bad_intr | 1903 | int_hand INT_SN_CPL, SN_CPL, bad_intr |
1903 | int_hand INT_DOUBLE_FAULT, DOUBLE_FAULT, do_trap | 1904 | int_hand INT_DOUBLE_FAULT, DOUBLE_FAULT, do_trap |
1904 | int_hand INT_AUX_PERF_COUNT, AUX_PERF_COUNT, \ | 1905 | int_hand INT_AUX_PERF_COUNT, AUX_PERF_COUNT, \ |
1905 | op_handle_aux_perf_interrupt, handle_nmi | 1906 | handle_perf_interrupt, handle_nmi |
1906 | 1907 | ||
1907 | /* Synthetic interrupt delivered only by the simulator */ | 1908 | /* Synthetic interrupt delivered only by the simulator */ |
1908 | int_hand INT_BREAKPOINT, BREAKPOINT, do_breakpoint | 1909 | int_hand INT_BREAKPOINT, BREAKPOINT, do_breakpoint |
diff --git a/arch/tile/kernel/intvec_64.S b/arch/tile/kernel/intvec_64.S index b8fc497f2437..8f892a58afd4 100644 --- a/arch/tile/kernel/intvec_64.S +++ b/arch/tile/kernel/intvec_64.S | |||
@@ -509,10 +509,10 @@ intvec_\vecname: | |||
509 | .ifc \c_routine, do_trap | 509 | .ifc \c_routine, do_trap |
510 | mfspr r2, GPV_REASON | 510 | mfspr r2, GPV_REASON |
511 | .else | 511 | .else |
512 | .ifc \c_routine, op_handle_perf_interrupt | 512 | .ifc \c_routine, handle_perf_interrupt |
513 | mfspr r2, PERF_COUNT_STS | 513 | mfspr r2, PERF_COUNT_STS |
514 | .else | 514 | .else |
515 | .ifc \c_routine, op_handle_aux_perf_interrupt | 515 | .ifc \c_routine, handle_perf_interrupt |
516 | mfspr r2, AUX_PERF_COUNT_STS | 516 | mfspr r2, AUX_PERF_COUNT_STS |
517 | .endif | 517 | .endif |
518 | .endif | 518 | .endif |
@@ -1491,8 +1491,9 @@ STD_ENTRY(fill_ra_stack) | |||
1491 | .global intrpt_start | 1491 | .global intrpt_start |
1492 | intrpt_start: | 1492 | intrpt_start: |
1493 | 1493 | ||
1494 | #define op_handle_perf_interrupt bad_intr | 1494 | #ifndef CONFIG_USE_PMC |
1495 | #define op_handle_aux_perf_interrupt bad_intr | 1495 | #define handle_perf_interrupt bad_intr |
1496 | #endif | ||
1496 | 1497 | ||
1497 | #ifndef CONFIG_HARDWALL | 1498 | #ifndef CONFIG_HARDWALL |
1498 | #define do_hardwall_trap bad_intr | 1499 | #define do_hardwall_trap bad_intr |
@@ -1540,9 +1541,9 @@ intrpt_start: | |||
1540 | #endif | 1541 | #endif |
1541 | int_hand INT_IPI_0, IPI_0, bad_intr | 1542 | int_hand INT_IPI_0, IPI_0, bad_intr |
1542 | int_hand INT_PERF_COUNT, PERF_COUNT, \ | 1543 | int_hand INT_PERF_COUNT, PERF_COUNT, \ |
1543 | op_handle_perf_interrupt, handle_nmi | 1544 | handle_perf_interrupt, handle_nmi |
1544 | int_hand INT_AUX_PERF_COUNT, AUX_PERF_COUNT, \ | 1545 | int_hand INT_AUX_PERF_COUNT, AUX_PERF_COUNT, \ |
1545 | op_handle_perf_interrupt, handle_nmi | 1546 | handle_perf_interrupt, handle_nmi |
1546 | int_hand INT_INTCTRL_3, INTCTRL_3, bad_intr | 1547 | int_hand INT_INTCTRL_3, INTCTRL_3, bad_intr |
1547 | #if CONFIG_KERNEL_PL == 2 | 1548 | #if CONFIG_KERNEL_PL == 2 |
1548 | dc_dispatch INT_INTCTRL_2, INTCTRL_2 | 1549 | dc_dispatch INT_INTCTRL_2, INTCTRL_2 |
diff --git a/arch/tile/kernel/pmc.c b/arch/tile/kernel/pmc.c new file mode 100644 index 000000000000..db62cc34b955 --- /dev/null +++ b/arch/tile/kernel/pmc.c | |||
@@ -0,0 +1,121 @@ | |||
1 | /* | ||
2 | * Copyright 2014 Tilera Corporation. All Rights Reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation, version 2. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
11 | * NON INFRINGEMENT. See the GNU General Public License for | ||
12 | * more details. | ||
13 | */ | ||
14 | |||
15 | #include <linux/errno.h> | ||
16 | #include <linux/spinlock.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/atomic.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | |||
21 | #include <asm/processor.h> | ||
22 | #include <asm/pmc.h> | ||
23 | |||
24 | perf_irq_t perf_irq = NULL; | ||
25 | int handle_perf_interrupt(struct pt_regs *regs, int fault) | ||
26 | { | ||
27 | int retval; | ||
28 | |||
29 | if (!perf_irq) | ||
30 | panic("Unexpected PERF_COUNT interrupt %d\n", fault); | ||
31 | |||
32 | nmi_enter(); | ||
33 | retval = perf_irq(regs, fault); | ||
34 | nmi_exit(); | ||
35 | return retval; | ||
36 | } | ||
37 | |||
38 | /* Reserve PMC hardware if it is available. */ | ||
39 | perf_irq_t reserve_pmc_hardware(perf_irq_t new_perf_irq) | ||
40 | { | ||
41 | return cmpxchg(&perf_irq, NULL, new_perf_irq); | ||
42 | } | ||
43 | EXPORT_SYMBOL(reserve_pmc_hardware); | ||
44 | |||
45 | /* Release PMC hardware. */ | ||
46 | void release_pmc_hardware(void) | ||
47 | { | ||
48 | perf_irq = NULL; | ||
49 | } | ||
50 | EXPORT_SYMBOL(release_pmc_hardware); | ||
51 | |||
52 | |||
53 | /* | ||
54 | * Get current overflow status of each performance counter, | ||
55 | * and auxiliary performance counter. | ||
56 | */ | ||
57 | unsigned long | ||
58 | pmc_get_overflow(void) | ||
59 | { | ||
60 | unsigned long status; | ||
61 | |||
62 | /* | ||
63 | * merge base+aux into a single vector | ||
64 | */ | ||
65 | status = __insn_mfspr(SPR_PERF_COUNT_STS); | ||
66 | status |= __insn_mfspr(SPR_AUX_PERF_COUNT_STS) << TILE_BASE_COUNTERS; | ||
67 | return status; | ||
68 | } | ||
69 | |||
70 | /* | ||
71 | * Clear the status bit for the corresponding counter, if written | ||
72 | * with a one. | ||
73 | */ | ||
74 | void | ||
75 | pmc_ack_overflow(unsigned long status) | ||
76 | { | ||
77 | /* | ||
78 | * clear overflow status by writing ones | ||
79 | */ | ||
80 | __insn_mtspr(SPR_PERF_COUNT_STS, status); | ||
81 | __insn_mtspr(SPR_AUX_PERF_COUNT_STS, status >> TILE_BASE_COUNTERS); | ||
82 | } | ||
83 | |||
84 | /* | ||
85 | * The perf count interrupts are masked and unmasked explicitly, | ||
86 | * and only here. The normal irq_enable() does not enable them, | ||
87 | * and irq_disable() does not disable them. That lets these | ||
88 | * routines drive the perf count interrupts orthogonally. | ||
89 | * | ||
90 | * We also mask the perf count interrupts on entry to the perf count | ||
91 | * interrupt handler in assembly code, and by default unmask them | ||
92 | * again (with interrupt critical section protection) just before | ||
93 | * returning from the interrupt. If the perf count handler returns | ||
94 | * a non-zero error code, then we don't re-enable them before returning. | ||
95 | * | ||
96 | * For Pro, we rely on both interrupts being in the same word to update | ||
97 | * them atomically so we never have one enabled and one disabled. | ||
98 | */ | ||
99 | |||
100 | #if CHIP_HAS_SPLIT_INTR_MASK() | ||
101 | # if INT_PERF_COUNT < 32 || INT_AUX_PERF_COUNT < 32 | ||
102 | # error Fix assumptions about which word PERF_COUNT interrupts are in | ||
103 | # endif | ||
104 | #endif | ||
105 | |||
106 | static inline unsigned long long pmc_mask(void) | ||
107 | { | ||
108 | unsigned long long mask = 1ULL << INT_PERF_COUNT; | ||
109 | mask |= 1ULL << INT_AUX_PERF_COUNT; | ||
110 | return mask; | ||
111 | } | ||
112 | |||
113 | void unmask_pmc_interrupts(void) | ||
114 | { | ||
115 | interrupt_mask_reset_mask(pmc_mask()); | ||
116 | } | ||
117 | |||
118 | void mask_pmc_interrupts(void) | ||
119 | { | ||
120 | interrupt_mask_set_mask(pmc_mask()); | ||
121 | } | ||