1 |
1275 |
phoenix |
/*
|
2 |
|
|
* arch/cris/kernel/debug.c
|
3 |
|
|
* Various debug routines:
|
4 |
|
|
* o Logging of interrupt enabling/disabling. /proc/debug_interrupt
|
5 |
|
|
* gives result and enables logging when read.
|
6 |
|
|
*
|
7 |
|
|
* Copyright (C) 2003 Axis Communications AB
|
8 |
|
|
*/
|
9 |
|
|
|
10 |
|
|
#include <linux/config.h>
|
11 |
|
|
#include <asm/system.h>
|
12 |
|
|
#include <linux/init.h>
|
13 |
|
|
#include <linux/proc_fs.h>
|
14 |
|
|
#include <asm/svinto.h>
|
15 |
|
|
|
16 |
|
|
#ifdef CONFIG_ETRAX_DEBUG_INTERRUPT
|
17 |
|
|
#define LOG_INT_SIZE 8192
|
18 |
|
|
#define DEBUG_INT_PROC_FILE "debug_interrupt"
|
19 |
|
|
#define LOG_INT_SHOW_MIN_USEC 45000
|
20 |
|
|
|
21 |
|
|
/* These are global and can be used to trig certain events. */
|
22 |
|
|
int log_int_pos = 0;
|
23 |
|
|
int log_int_size = LOG_INT_SIZE;
|
24 |
|
|
int log_int_trig0_pos = 0;
|
25 |
|
|
int log_int_trig1_pos = 0;
|
26 |
|
|
|
27 |
|
|
int log_int_enable = 0; /* Enabled every read of /proc/debug_interrupt */
|
28 |
|
|
|
29 |
|
|
struct log_int_struct
|
30 |
|
|
{
|
31 |
|
|
unsigned long pc;
|
32 |
|
|
unsigned long ev_timer_data;
|
33 |
|
|
};
|
34 |
|
|
|
35 |
|
|
static struct log_int_struct log_ints[LOG_INT_SIZE];
|
36 |
|
|
|
37 |
|
|
//static unsigned long prev_log_int_t = 0;
|
38 |
|
|
static unsigned long prev_logged_ccr = 0;
|
39 |
|
|
static unsigned long prev_ei_timer_data = 0;
|
40 |
|
|
static unsigned long prev_di_timer_data = 0;
|
41 |
|
|
|
42 |
|
|
#define CCR_EI_BIT 5
|
43 |
|
|
enum {
|
44 |
|
|
INT_OLD_MASK = 0x01, INT_OLD_BIT = 0,
|
45 |
|
|
INT_NEW_MASK = 0x02, INT_NEW_BIT = 1,
|
46 |
|
|
INT_EI_CHANGE_MASK = 0x04, INT_EI_CHANGE_BIT = 2,
|
47 |
|
|
INT_ACTION_RESTORE = 0x08, INT_ACTION_RESTORE_BIT = 3,
|
48 |
|
|
INT_ACTION_MISSED = 0x10, INT_ACTION_MISSED_BIT = 4,
|
49 |
|
|
INT_ACTION_LONG = 0x20, INT_ACTION_LONG_BIT = 5,
|
50 |
|
|
|
51 |
|
|
INT_OLD_EI = INT_OLD_MASK, INT_OLD_EI_BIT = INT_OLD_BIT,
|
52 |
|
|
INT_NEW_EI = INT_NEW_MASK, INT_NEW_EI_BIT = INT_NEW_BIT,
|
53 |
|
|
INT_EV_DI = INT_OLD_EI | INT_EI_CHANGE_MASK,
|
54 |
|
|
INT_EV_NOP_DI = 0,
|
55 |
|
|
INT_EV_EI = INT_NEW_EI | INT_EI_CHANGE_MASK,
|
56 |
|
|
INT_EV_NOP_EI = INT_OLD_EI | INT_NEW_EI,
|
57 |
|
|
INT_EV_RESTORE_DI= INT_EV_DI | INT_EI_CHANGE_MASK | INT_ACTION_RESTORE,
|
58 |
|
|
INT_EV_RESTORE_EI= INT_EV_EI | INT_EI_CHANGE_MASK | INT_ACTION_RESTORE,
|
59 |
|
|
INT_EV_RESTORE_NOP_DI = INT_EV_NOP_DI | INT_ACTION_RESTORE,
|
60 |
|
|
INT_EV_RESTORE_NOP_EI = INT_EV_NOP_EI | INT_ACTION_RESTORE,
|
61 |
|
|
};
|
62 |
|
|
|
63 |
|
|
void log_int(unsigned long pc, unsigned long curr_ccr, unsigned long next_ccr)
|
64 |
|
|
{
|
65 |
|
|
unsigned long t;
|
66 |
|
|
int ev;
|
67 |
|
|
static int no_change_cnt = 0;
|
68 |
|
|
|
69 |
|
|
/* Just disable interrupts without logging here,
|
70 |
|
|
* the caller will either do ei, di or restore
|
71 |
|
|
*/
|
72 |
|
|
__asm__ __volatile__ ("di" : : :"memory");
|
73 |
|
|
t = *R_TIMER_DATA;
|
74 |
|
|
|
75 |
|
|
if (curr_ccr & CCR_EI_MASK) {
|
76 |
|
|
prev_ei_timer_data = t;
|
77 |
|
|
ev = INT_OLD_EI;
|
78 |
|
|
} else {
|
79 |
|
|
prev_di_timer_data = t;
|
80 |
|
|
if ( (((prev_ei_timer_data >> 8) & 0x000000FF) -
|
81 |
|
|
((prev_di_timer_data >> 8) & 0x000000FF)) & ~0x03)
|
82 |
|
|
ev = INT_ACTION_LONG;
|
83 |
|
|
else
|
84 |
|
|
ev = 0;
|
85 |
|
|
}
|
86 |
|
|
if ((curr_ccr ^ prev_logged_ccr) & CCR_EI_MASK)
|
87 |
|
|
ev |= INT_ACTION_MISSED;
|
88 |
|
|
if (next_ccr & CCR_EI_MASK)
|
89 |
|
|
ev |= INT_NEW_EI;
|
90 |
|
|
if ((curr_ccr ^ next_ccr) & CCR_EI_MASK) {
|
91 |
|
|
ev |= INT_EI_CHANGE_MASK;
|
92 |
|
|
no_change_cnt = 0;
|
93 |
|
|
} else
|
94 |
|
|
no_change_cnt++;
|
95 |
|
|
|
96 |
|
|
prev_logged_ccr = next_ccr;
|
97 |
|
|
|
98 |
|
|
if (log_int_enable &&
|
99 |
|
|
((ev & (INT_EI_CHANGE_MASK | INT_ACTION_MISSED | INT_ACTION_LONG))
|
100 |
|
|
|| (no_change_cnt < 40)) &&
|
101 |
|
|
log_int_pos < LOG_INT_SIZE) {
|
102 |
|
|
int i;
|
103 |
|
|
i = log_int_pos;
|
104 |
|
|
log_int_pos++;
|
105 |
|
|
log_ints[i].pc = pc;
|
106 |
|
|
log_ints[i].ev_timer_data = (t & 0x00FFFFFF) |
|
107 |
|
|
((ev & 0xFF) << 24);
|
108 |
|
|
|
109 |
|
|
}
|
110 |
|
|
// __asm__ __volatile__ ("move %0,$ccr" : : "rm" (curr_ccr) : "memory");
|
111 |
|
|
}
|
112 |
|
|
void log_int_di(void)
|
113 |
|
|
{
|
114 |
|
|
unsigned long pc;
|
115 |
|
|
unsigned long flags;
|
116 |
|
|
__asm__ __volatile__ ("move $srp,%0" : "=rm" (pc) : : "memory");
|
117 |
|
|
__asm__ __volatile__ ("move $ccr,%0" : "=rm" (flags) : : "memory");
|
118 |
|
|
log_int(pc, flags, 0);
|
119 |
|
|
}
|
120 |
|
|
|
121 |
|
|
void log_int_ei(void)
|
122 |
|
|
{
|
123 |
|
|
unsigned long pc;
|
124 |
|
|
unsigned long flags;
|
125 |
|
|
__asm__ __volatile__ ("move $srp,%0" : "=rm" (pc) : : "memory");
|
126 |
|
|
__asm__ __volatile__ ("move $ccr,%0" : "=rm" (flags) : : "memory");
|
127 |
|
|
log_int(pc, flags, CCR_EI_MASK);
|
128 |
|
|
}
|
129 |
|
|
|
130 |
|
|
|
131 |
|
|
static int log_int_read_proc(char *page, char **start, off_t off, int count,
|
132 |
|
|
int *eof, void *data)
|
133 |
|
|
{
|
134 |
|
|
int i, len = 0;
|
135 |
|
|
off_t begin = 0;
|
136 |
|
|
int j, t0, t1, t, thigh, tlow, tdi0;
|
137 |
|
|
int di0, ei1;
|
138 |
|
|
len += sprintf(page + len, "trig0: %i trig1: %i\n", log_int_trig0_pos, log_int_trig1_pos);
|
139 |
|
|
for (i = 0; i < log_int_pos-1; i++) {
|
140 |
|
|
|
141 |
|
|
if (((log_ints[i].ev_timer_data >> 24) & INT_NEW_EI)) {
|
142 |
|
|
di0 = i;
|
143 |
|
|
while (di0 < log_int_pos-1 && (((log_ints[di0].ev_timer_data >> 24) & INT_NEW_EI)))
|
144 |
|
|
di0++;
|
145 |
|
|
ei1 = di0+1;
|
146 |
|
|
while (ei1 < log_int_pos-1 && (((log_ints[ei1].ev_timer_data >> 24) & INT_NEW_EI) == 0))
|
147 |
|
|
ei1++;
|
148 |
|
|
thigh = timer_data_to_ns(log_ints[ei1].ev_timer_data);
|
149 |
|
|
tlow = timer_data_to_ns(log_ints[di0].ev_timer_data);
|
150 |
|
|
tdi0 = timer_data_to_ns(log_ints[di0].ev_timer_data);
|
151 |
|
|
|
152 |
|
|
t = thigh-tlow;
|
153 |
|
|
j = di0-1;
|
154 |
|
|
if ((t > LOG_INT_SHOW_MIN_USEC) || (log_int_trig0_pos-30 < j && j < log_int_trig1_pos+30)) {
|
155 |
|
|
|
156 |
|
|
for (; j <= ei1; j++) {
|
157 |
|
|
t0 = ((log_ints[j+1].ev_timer_data & 0xFF) -
|
158 |
|
|
(log_ints[j].ev_timer_data & 0xFF));
|
159 |
|
|
t1 = (((log_ints[j+1].ev_timer_data >> 8) & 0xFF) -
|
160 |
|
|
((log_ints[j].ev_timer_data >> 8) & 0xFF));
|
161 |
|
|
if (t1 == 0 || t1 == 1) {
|
162 |
|
|
if (t0 < 0)
|
163 |
|
|
t0 += 256;
|
164 |
|
|
} else {
|
165 |
|
|
if (t1 < 0)
|
166 |
|
|
t1 += 256;
|
167 |
|
|
}
|
168 |
|
|
thigh = timer_data_to_ns(log_ints[j+1].ev_timer_data);
|
169 |
|
|
tlow = timer_data_to_ns(log_ints[j].ev_timer_data);
|
170 |
|
|
t = thigh-tlow;
|
171 |
|
|
len += sprintf(page + len, "%4i PC %08lX-%08lX %08lX-%08lX %s high %i in %-6i ns %-7i to %-7i = %-5i ns, from first di %i ns %s\n",
|
172 |
|
|
j, log_ints[j].pc, log_ints[j+1].pc,
|
173 |
|
|
log_ints[j].ev_timer_data, log_ints[j+1].ev_timer_data,
|
174 |
|
|
((log_ints[j].ev_timer_data >> 24) & INT_NEW_EI)!= 0?"ei":"di", t1, t,
|
175 |
|
|
tlow, thigh, thigh-tlow, thigh-tdi0,
|
176 |
|
|
j==log_int_trig0_pos?"TRIG0":(j==log_int_trig1_pos?"TRIG1":""));
|
177 |
|
|
if (len+begin > off+count)
|
178 |
|
|
goto done;
|
179 |
|
|
if (len+begin < off) {
|
180 |
|
|
begin += len;
|
181 |
|
|
len = 0;
|
182 |
|
|
}
|
183 |
|
|
}
|
184 |
|
|
|
185 |
|
|
len += sprintf(page + len,"\n");
|
186 |
|
|
i = ei1-2;
|
187 |
|
|
}
|
188 |
|
|
|
189 |
|
|
}
|
190 |
|
|
|
191 |
|
|
}
|
192 |
|
|
log_int_enable = 1;
|
193 |
|
|
log_int_pos = 0;
|
194 |
|
|
log_int_trig0_pos = 0;
|
195 |
|
|
log_int_trig1_pos = 0;
|
196 |
|
|
*eof = 1;
|
197 |
|
|
done:
|
198 |
|
|
if (off >= len+begin)
|
199 |
|
|
return 0;
|
200 |
|
|
*start = page + (off-begin);
|
201 |
|
|
return ((count < begin+len-off) ? count : begin+len-off);
|
202 |
|
|
}
|
203 |
|
|
|
204 |
|
|
static int __init
|
205 |
|
|
log_int_init(void)
|
206 |
|
|
{
|
207 |
|
|
create_proc_read_entry (DEBUG_INT_PROC_FILE, 0, 0, log_int_read_proc, NULL);
|
208 |
|
|
printk(KERN_INFO "/proc/" DEBUG_INT_PROC_FILE " size %i.\r\n",
|
209 |
|
|
LOG_INT_SIZE);
|
210 |
|
|
return 0;
|
211 |
|
|
}
|
212 |
|
|
module_init(log_int_init);
|
213 |
|
|
#endif /* CONFIG_ETRAX_DEBUG_INTERRUPT */
|