1 /*
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 
log_int(unsigned long pc,unsigned long curr_ccr,unsigned long next_ccr)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 }
log_int_di(void)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 
log_int_ei(void)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 
log_int_read_proc(char * page,char ** start,off_t off,int count,int * eof,void * data)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
log_int_init(void)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 */
214