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 /arch/tile/kernel | |
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>
Diffstat (limited to 'arch/tile/kernel')
-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 |
4 files changed, 136 insertions, 12 deletions
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 | } | ||