Bryan Wu | 1394f03 | 2007-05-06 14:50:22 -0700 | [diff] [blame] | 1 | /* |
| 2 | * File: arch/blackfin/oprofile/op_model_bf533.c |
| 3 | * Based on: |
| 4 | * Author: Anton Blanchard <anton@au.ibm.com> |
| 5 | * |
| 6 | * Created: |
| 7 | * Description: |
| 8 | * |
| 9 | * Modified: |
| 10 | * Copyright (C) 2004 Anton Blanchard <anton@au.ibm.com>, IBM |
| 11 | * Copyright 2004-2006 Analog Devices Inc. |
| 12 | * |
| 13 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ |
| 14 | * |
| 15 | * This program is free software; you can redistribute it and/or modify |
| 16 | * it under the terms of the GNU General Public License as published by |
| 17 | * the Free Software Foundation; either version 2 of the License, or |
| 18 | * (at your option) any later version. |
| 19 | * |
| 20 | * This program is distributed in the hope that it will be useful, |
| 21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 23 | * GNU General Public License for more details. |
| 24 | * |
| 25 | * You should have received a copy of the GNU General Public License |
| 26 | * along with this program; if not, see the file COPYING, or write |
| 27 | * to the Free Software Foundation, Inc., |
| 28 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| 29 | */ |
| 30 | |
| 31 | #include <linux/oprofile.h> |
| 32 | #include <linux/init.h> |
| 33 | #include <linux/smp.h> |
| 34 | #include <linux/interrupt.h> |
Mike Frysinger | 1f83b8f | 2007-07-12 22:58:21 +0800 | [diff] [blame] | 35 | #include <linux/ptrace.h> |
| 36 | #include <linux/irq.h> |
| 37 | #include <linux/io.h> |
Bryan Wu | 1394f03 | 2007-05-06 14:50:22 -0700 | [diff] [blame] | 38 | #include <asm/system.h> |
| 39 | #include <asm/processor.h> |
| 40 | #include <asm/blackfin.h> |
Bryan Wu | 1394f03 | 2007-05-06 14:50:22 -0700 | [diff] [blame] | 41 | |
| 42 | #include "op_blackfin.h" |
| 43 | |
| 44 | #define PM_ENABLE 0x01; |
| 45 | #define PM_CTL1_ENABLE 0x18 |
| 46 | #define PM_CTL0_ENABLE 0xC000 |
| 47 | #define COUNT_EDGE_ONLY 0x3000000 |
| 48 | |
| 49 | static int oprofile_running; |
| 50 | |
| 51 | static unsigned curr_pfctl, curr_count[2]; |
| 52 | |
| 53 | static int bfin533_reg_setup(struct op_counter_config *ctr) |
| 54 | { |
| 55 | unsigned int pfctl = ctr_read(); |
| 56 | unsigned int count[2]; |
| 57 | |
| 58 | /* set Blackfin perf monitor regs with ctr */ |
| 59 | if (ctr[0].enabled) { |
| 60 | pfctl |= (PM_CTL0_ENABLE | ((char)ctr[0].event << 5)); |
| 61 | count[0] = 0xFFFFFFFF - ctr[0].count; |
| 62 | curr_count[0] = count[0]; |
| 63 | } |
| 64 | if (ctr[1].enabled) { |
| 65 | pfctl |= (PM_CTL1_ENABLE | ((char)ctr[1].event << 16)); |
| 66 | count[1] = 0xFFFFFFFF - ctr[1].count; |
| 67 | curr_count[1] = count[1]; |
| 68 | } |
| 69 | |
| 70 | pr_debug("ctr[0].enabled=%d,ctr[1].enabled=%d,ctr[0].event<<5=0x%x,ctr[1].event<<16=0x%x\n", ctr[0].enabled, ctr[1].enabled, ctr[0].event << 5, ctr[1].event << 16); |
| 71 | pfctl |= COUNT_EDGE_ONLY; |
| 72 | curr_pfctl = pfctl; |
| 73 | |
| 74 | pr_debug("write 0x%x to pfctl\n", pfctl); |
| 75 | ctr_write(pfctl); |
| 76 | count_write(count); |
| 77 | |
| 78 | return 0; |
| 79 | } |
| 80 | |
| 81 | static int bfin533_start(struct op_counter_config *ctr) |
| 82 | { |
| 83 | unsigned int pfctl = ctr_read(); |
| 84 | |
| 85 | pfctl |= PM_ENABLE; |
| 86 | curr_pfctl = pfctl; |
| 87 | |
| 88 | ctr_write(pfctl); |
| 89 | |
| 90 | oprofile_running = 1; |
| 91 | pr_debug("start oprofile counter \n"); |
| 92 | |
| 93 | return 0; |
| 94 | } |
| 95 | |
| 96 | static void bfin533_stop(void) |
| 97 | { |
| 98 | int pfctl; |
| 99 | |
| 100 | pfctl = ctr_read(); |
| 101 | pfctl &= ~PM_ENABLE; |
| 102 | /* freeze counters */ |
| 103 | ctr_write(pfctl); |
| 104 | |
| 105 | oprofile_running = 0; |
| 106 | pr_debug("stop oprofile counter \n"); |
| 107 | } |
| 108 | |
| 109 | static int get_kernel(void) |
| 110 | { |
| 111 | int ipend, is_kernel; |
| 112 | |
| 113 | ipend = bfin_read_IPEND(); |
| 114 | |
| 115 | /* test bit 15 */ |
| 116 | is_kernel = ((ipend & 0x8000) != 0); |
| 117 | |
| 118 | return is_kernel; |
| 119 | } |
| 120 | |
| 121 | int pm_overflow_handler(int irq, struct pt_regs *regs) |
| 122 | { |
| 123 | int is_kernel; |
| 124 | int i, cpu; |
| 125 | unsigned int pc, pfctl; |
| 126 | unsigned int count[2]; |
| 127 | |
Harvey Harrison | b85d858 | 2008-04-23 09:39:01 +0800 | [diff] [blame] | 128 | pr_debug("get interrupt in %s\n", __func__); |
Bryan Wu | 1394f03 | 2007-05-06 14:50:22 -0700 | [diff] [blame] | 129 | if (oprofile_running == 0) { |
| 130 | pr_debug("error: entering interrupt when oprofile is stopped.\n\r"); |
| 131 | return -1; |
| 132 | } |
| 133 | |
| 134 | is_kernel = get_kernel(); |
| 135 | cpu = smp_processor_id(); |
| 136 | pc = regs->pc; |
| 137 | pfctl = ctr_read(); |
| 138 | |
| 139 | /* read the two event counter regs */ |
| 140 | count_read(count); |
| 141 | |
| 142 | /* if the counter overflows, add sample to oprofile buffer */ |
| 143 | for (i = 0; i < 2; ++i) { |
| 144 | if (oprofile_running) { |
| 145 | oprofile_add_sample(regs, i); |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | /* reset the perfmon counter */ |
| 150 | ctr_write(curr_pfctl); |
| 151 | count_write(curr_count); |
| 152 | return 0; |
| 153 | } |
| 154 | |
| 155 | struct op_bfin533_model op_model_bfin533 = { |
| 156 | .reg_setup = bfin533_reg_setup, |
| 157 | .start = bfin533_start, |
| 158 | .stop = bfin533_stop, |
| 159 | .num_counters = 2, |
| 160 | .name = "blackfin/bf533" |
| 161 | }; |