diff options
Diffstat (limited to 'arch/tile/kernel/pmc.c')
-rw-r--r-- | arch/tile/kernel/pmc.c | 121 |
1 files changed, 121 insertions, 0 deletions
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 | } | ||