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 | } | ||
