diff options
Diffstat (limited to 'arch/cris/arch-v32/kernel/debugport.c')
-rw-r--r-- | arch/cris/arch-v32/kernel/debugport.c | 461 |
1 files changed, 461 insertions, 0 deletions
diff --git a/arch/cris/arch-v32/kernel/debugport.c b/arch/cris/arch-v32/kernel/debugport.c new file mode 100644 index 00000000000..ffc1ebf2dfe --- /dev/null +++ b/arch/cris/arch-v32/kernel/debugport.c | |||
@@ -0,0 +1,461 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2003, Axis Communications AB. | ||
3 | */ | ||
4 | |||
5 | #include <linux/config.h> | ||
6 | #include <linux/console.h> | ||
7 | #include <linux/init.h> | ||
8 | #include <linux/major.h> | ||
9 | #include <linux/delay.h> | ||
10 | #include <linux/tty.h> | ||
11 | #include <asm/system.h> | ||
12 | #include <asm/io.h> | ||
13 | #include <asm/arch/hwregs/ser_defs.h> | ||
14 | #include <asm/arch/hwregs/dma_defs.h> | ||
15 | #include <asm/arch/pinmux.h> | ||
16 | |||
17 | #include <asm/irq.h> | ||
18 | #include <asm/arch/hwregs/intr_vect_defs.h> | ||
19 | |||
20 | struct dbg_port | ||
21 | { | ||
22 | unsigned char nbr; | ||
23 | unsigned long instance; | ||
24 | unsigned int started; | ||
25 | unsigned long baudrate; | ||
26 | unsigned char parity; | ||
27 | unsigned int bits; | ||
28 | }; | ||
29 | |||
30 | struct dbg_port ports[] = | ||
31 | { | ||
32 | { | ||
33 | 0, | ||
34 | regi_ser0, | ||
35 | 0, | ||
36 | 115200, | ||
37 | 'N', | ||
38 | 8 | ||
39 | }, | ||
40 | { | ||
41 | 1, | ||
42 | regi_ser1, | ||
43 | 0, | ||
44 | 115200, | ||
45 | 'N', | ||
46 | 8 | ||
47 | }, | ||
48 | { | ||
49 | 2, | ||
50 | regi_ser2, | ||
51 | 0, | ||
52 | 115200, | ||
53 | 'N', | ||
54 | 8 | ||
55 | }, | ||
56 | { | ||
57 | 3, | ||
58 | regi_ser3, | ||
59 | 0, | ||
60 | 115200, | ||
61 | 'N', | ||
62 | 8 | ||
63 | } | ||
64 | }; | ||
65 | static struct dbg_port *port = | ||
66 | #if defined(CONFIG_ETRAX_DEBUG_PORT0) | ||
67 | &ports[0]; | ||
68 | #elif defined(CONFIG_ETRAX_DEBUG_PORT1) | ||
69 | &ports[1]; | ||
70 | #elif defined(CONFIG_ETRAX_DEBUG_PORT2) | ||
71 | &ports[2]; | ||
72 | #elif defined(CONFIG_ETRAX_DEBUG_PORT3) | ||
73 | &ports[3]; | ||
74 | #else | ||
75 | NULL; | ||
76 | #endif | ||
77 | |||
78 | #ifdef CONFIG_ETRAX_KGDB | ||
79 | static struct dbg_port *kgdb_port = | ||
80 | #if defined(CONFIG_ETRAX_KGDB_PORT0) | ||
81 | &ports[0]; | ||
82 | #elif defined(CONFIG_ETRAX_KGDB_PORT1) | ||
83 | &ports[1]; | ||
84 | #elif defined(CONFIG_ETRAX_KGDB_PORT2) | ||
85 | &ports[2]; | ||
86 | #elif defined(CONFIG_ETRAX_KGDB_PORT3) | ||
87 | &ports[3]; | ||
88 | #else | ||
89 | NULL; | ||
90 | #endif | ||
91 | #endif | ||
92 | |||
93 | #ifdef CONFIG_ETRAXFS_SIM | ||
94 | extern void print_str( const char *str ); | ||
95 | static char buffer[1024]; | ||
96 | static char msg[] = "Debug: "; | ||
97 | static int buffer_pos = sizeof(msg) - 1; | ||
98 | #endif | ||
99 | |||
100 | extern struct tty_driver *serial_driver; | ||
101 | |||
102 | static void | ||
103 | start_port(struct dbg_port* p) | ||
104 | { | ||
105 | if (!p) | ||
106 | return; | ||
107 | |||
108 | if (p->started) | ||
109 | return; | ||
110 | p->started = 1; | ||
111 | |||
112 | if (p->nbr == 1) | ||
113 | crisv32_pinmux_alloc_fixed(pinmux_ser1); | ||
114 | else if (p->nbr == 2) | ||
115 | crisv32_pinmux_alloc_fixed(pinmux_ser2); | ||
116 | else if (p->nbr == 3) | ||
117 | crisv32_pinmux_alloc_fixed(pinmux_ser3); | ||
118 | |||
119 | /* Set up serial port registers */ | ||
120 | reg_ser_rw_tr_ctrl tr_ctrl = {0}; | ||
121 | reg_ser_rw_tr_dma_en tr_dma_en = {0}; | ||
122 | |||
123 | reg_ser_rw_rec_ctrl rec_ctrl = {0}; | ||
124 | reg_ser_rw_tr_baud_div tr_baud_div = {0}; | ||
125 | reg_ser_rw_rec_baud_div rec_baud_div = {0}; | ||
126 | |||
127 | tr_ctrl.base_freq = rec_ctrl.base_freq = regk_ser_f29_493; | ||
128 | tr_dma_en.en = rec_ctrl.dma_mode = regk_ser_no; | ||
129 | tr_baud_div.div = rec_baud_div.div = 29493000 / p->baudrate / 8; | ||
130 | tr_ctrl.en = rec_ctrl.en = 1; | ||
131 | |||
132 | if (p->parity == 'O') | ||
133 | { | ||
134 | tr_ctrl.par_en = regk_ser_yes; | ||
135 | tr_ctrl.par = regk_ser_odd; | ||
136 | rec_ctrl.par_en = regk_ser_yes; | ||
137 | rec_ctrl.par = regk_ser_odd; | ||
138 | } | ||
139 | else if (p->parity == 'E') | ||
140 | { | ||
141 | tr_ctrl.par_en = regk_ser_yes; | ||
142 | tr_ctrl.par = regk_ser_even; | ||
143 | rec_ctrl.par_en = regk_ser_yes; | ||
144 | rec_ctrl.par = regk_ser_odd; | ||
145 | } | ||
146 | |||
147 | if (p->bits == 7) | ||
148 | { | ||
149 | tr_ctrl.data_bits = regk_ser_bits7; | ||
150 | rec_ctrl.data_bits = regk_ser_bits7; | ||
151 | } | ||
152 | |||
153 | REG_WR (ser, p->instance, rw_tr_baud_div, tr_baud_div); | ||
154 | REG_WR (ser, p->instance, rw_rec_baud_div, rec_baud_div); | ||
155 | REG_WR (ser, p->instance, rw_tr_dma_en, tr_dma_en); | ||
156 | REG_WR (ser, p->instance, rw_tr_ctrl, tr_ctrl); | ||
157 | REG_WR (ser, p->instance, rw_rec_ctrl, rec_ctrl); | ||
158 | } | ||
159 | |||
160 | /* No debug */ | ||
161 | #ifdef CONFIG_ETRAX_DEBUG_PORT_NULL | ||
162 | |||
163 | static void | ||
164 | console_write(struct console *co, const char *buf, unsigned int len) | ||
165 | { | ||
166 | return; | ||
167 | } | ||
168 | |||
169 | /* Target debug */ | ||
170 | #elif !defined(CONFIG_ETRAXFS_SIM) | ||
171 | |||
172 | static void | ||
173 | console_write_direct(struct console *co, const char *buf, unsigned int len) | ||
174 | { | ||
175 | int i; | ||
176 | reg_ser_r_stat_din stat; | ||
177 | reg_ser_rw_tr_dma_en tr_dma_en, old; | ||
178 | |||
179 | /* Switch to manual mode */ | ||
180 | tr_dma_en = old = REG_RD (ser, port->instance, rw_tr_dma_en); | ||
181 | if (tr_dma_en.en == regk_ser_yes) { | ||
182 | tr_dma_en.en = regk_ser_no; | ||
183 | REG_WR(ser, port->instance, rw_tr_dma_en, tr_dma_en); | ||
184 | } | ||
185 | |||
186 | /* Send data */ | ||
187 | for (i = 0; i < len; i++) { | ||
188 | /* LF -> CRLF */ | ||
189 | if (buf[i] == '\n') { | ||
190 | do { | ||
191 | stat = REG_RD (ser, port->instance, r_stat_din); | ||
192 | } while (!stat.tr_rdy); | ||
193 | REG_WR_INT (ser, port->instance, rw_dout, '\r'); | ||
194 | } | ||
195 | /* Wait until transmitter is ready and send.*/ | ||
196 | do { | ||
197 | stat = REG_RD (ser, port->instance, r_stat_din); | ||
198 | } while (!stat.tr_rdy); | ||
199 | REG_WR_INT (ser, port->instance, rw_dout, buf[i]); | ||
200 | } | ||
201 | |||
202 | /* Restore mode */ | ||
203 | if (tr_dma_en.en != old.en) | ||
204 | REG_WR(ser, port->instance, rw_tr_dma_en, old); | ||
205 | } | ||
206 | |||
207 | static void | ||
208 | console_write(struct console *co, const char *buf, unsigned int len) | ||
209 | { | ||
210 | if (!port) | ||
211 | return; | ||
212 | console_write_direct(co, buf, len); | ||
213 | } | ||
214 | |||
215 | |||
216 | |||
217 | #else | ||
218 | |||
219 | /* VCS debug */ | ||
220 | |||
221 | static void | ||
222 | console_write(struct console *co, const char *buf, unsigned int len) | ||
223 | { | ||
224 | char* pos; | ||
225 | pos = memchr(buf, '\n', len); | ||
226 | if (pos) { | ||
227 | int l = ++pos - buf; | ||
228 | memcpy(buffer + buffer_pos, buf, l); | ||
229 | memcpy(buffer, msg, sizeof(msg) - 1); | ||
230 | buffer[buffer_pos + l] = '\0'; | ||
231 | print_str(buffer); | ||
232 | buffer_pos = sizeof(msg) - 1; | ||
233 | if (pos - buf != len) { | ||
234 | memcpy(buffer + buffer_pos, pos, len - l); | ||
235 | buffer_pos += len - l; | ||
236 | } | ||
237 | } else { | ||
238 | memcpy(buffer + buffer_pos, buf, len); | ||
239 | buffer_pos += len; | ||
240 | } | ||
241 | } | ||
242 | |||
243 | #endif | ||
244 | |||
245 | int raw_printk(const char *fmt, ...) | ||
246 | { | ||
247 | static char buf[1024]; | ||
248 | int printed_len; | ||
249 | va_list args; | ||
250 | va_start(args, fmt); | ||
251 | printed_len = vsnprintf(buf, sizeof(buf), fmt, args); | ||
252 | va_end(args); | ||
253 | console_write(NULL, buf, strlen(buf)); | ||
254 | return printed_len; | ||
255 | } | ||
256 | |||
257 | void | ||
258 | stupid_debug(char* buf) | ||
259 | { | ||
260 | console_write(NULL, buf, strlen(buf)); | ||
261 | } | ||
262 | |||
263 | #ifdef CONFIG_ETRAX_KGDB | ||
264 | /* Use polling to get a single character from the kernel debug port */ | ||
265 | int | ||
266 | getDebugChar(void) | ||
267 | { | ||
268 | reg_ser_rs_status_data stat; | ||
269 | reg_ser_rw_ack_intr ack_intr = { 0 }; | ||
270 | |||
271 | do { | ||
272 | stat = REG_RD(ser, kgdb_instance, rs_status_data); | ||
273 | } while (!stat.data_avail); | ||
274 | |||
275 | /* Ack the data_avail interrupt. */ | ||
276 | ack_intr.data_avail = 1; | ||
277 | REG_WR(ser, kgdb_instance, rw_ack_intr, ack_intr); | ||
278 | |||
279 | return stat.data; | ||
280 | } | ||
281 | |||
282 | /* Use polling to put a single character to the kernel debug port */ | ||
283 | void | ||
284 | putDebugChar(int val) | ||
285 | { | ||
286 | reg_ser_r_status_data stat; | ||
287 | do { | ||
288 | stat = REG_RD (ser, kgdb_instance, r_status_data); | ||
289 | } while (!stat.tr_ready); | ||
290 | REG_WR (ser, kgdb_instance, rw_data_out, REG_TYPE_CONV(reg_ser_rw_data_out, int, val)); | ||
291 | } | ||
292 | #endif /* CONFIG_ETRAX_KGDB */ | ||
293 | |||
294 | static int __init | ||
295 | console_setup(struct console *co, char *options) | ||
296 | { | ||
297 | char* s; | ||
298 | |||
299 | if (options) { | ||
300 | port = &ports[co->index]; | ||
301 | port->baudrate = 115200; | ||
302 | port->parity = 'N'; | ||
303 | port->bits = 8; | ||
304 | port->baudrate = simple_strtoul(options, NULL, 10); | ||
305 | s = options; | ||
306 | while(*s >= '0' && *s <= '9') | ||
307 | s++; | ||
308 | if (*s) port->parity = *s++; | ||
309 | if (*s) port->bits = *s++ - '0'; | ||
310 | port->started = 0; | ||
311 | start_port(port); | ||
312 | } | ||
313 | return 0; | ||
314 | } | ||
315 | |||
316 | /* This is a dummy serial device that throws away anything written to it. | ||
317 | * This is used when no debug output is wanted. | ||
318 | */ | ||
319 | static struct tty_driver dummy_driver; | ||
320 | |||
321 | static int dummy_open(struct tty_struct *tty, struct file * filp) | ||
322 | { | ||
323 | return 0; | ||
324 | } | ||
325 | |||
326 | static void dummy_close(struct tty_struct *tty, struct file * filp) | ||
327 | { | ||
328 | } | ||
329 | |||
330 | static int dummy_write(struct tty_struct * tty, | ||
331 | const unsigned char *buf, int count) | ||
332 | { | ||
333 | return count; | ||
334 | } | ||
335 | |||
336 | static int | ||
337 | dummy_write_room(struct tty_struct *tty) | ||
338 | { | ||
339 | return 8192; | ||
340 | } | ||
341 | |||
342 | void __init | ||
343 | init_dummy_console(void) | ||
344 | { | ||
345 | memset(&dummy_driver, 0, sizeof(struct tty_driver)); | ||
346 | dummy_driver.driver_name = "serial"; | ||
347 | dummy_driver.name = "ttyS"; | ||
348 | dummy_driver.major = TTY_MAJOR; | ||
349 | dummy_driver.minor_start = 68; | ||
350 | dummy_driver.num = 1; /* etrax100 has 4 serial ports */ | ||
351 | dummy_driver.type = TTY_DRIVER_TYPE_SERIAL; | ||
352 | dummy_driver.subtype = SERIAL_TYPE_NORMAL; | ||
353 | dummy_driver.init_termios = tty_std_termios; | ||
354 | dummy_driver.init_termios.c_cflag = | ||
355 | B115200 | CS8 | CREAD | HUPCL | CLOCAL; /* is normally B9600 default... */ | ||
356 | dummy_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; | ||
357 | |||
358 | dummy_driver.open = dummy_open; | ||
359 | dummy_driver.close = dummy_close; | ||
360 | dummy_driver.write = dummy_write; | ||
361 | dummy_driver.write_room = dummy_write_room; | ||
362 | if (tty_register_driver(&dummy_driver)) | ||
363 | panic("Couldn't register dummy serial driver\n"); | ||
364 | } | ||
365 | |||
366 | static struct tty_driver* | ||
367 | crisv32_console_device(struct console* co, int *index) | ||
368 | { | ||
369 | if (port) | ||
370 | *index = port->nbr; | ||
371 | return port ? serial_driver : &dummy_driver; | ||
372 | } | ||
373 | |||
374 | static struct console sercons = { | ||
375 | name : "ttyS", | ||
376 | write: console_write, | ||
377 | read : NULL, | ||
378 | device : crisv32_console_device, | ||
379 | unblank : NULL, | ||
380 | setup : console_setup, | ||
381 | flags : CON_PRINTBUFFER, | ||
382 | index : -1, | ||
383 | cflag : 0, | ||
384 | next : NULL | ||
385 | }; | ||
386 | static struct console sercons0 = { | ||
387 | name : "ttyS", | ||
388 | write: console_write, | ||
389 | read : NULL, | ||
390 | device : crisv32_console_device, | ||
391 | unblank : NULL, | ||
392 | setup : console_setup, | ||
393 | flags : CON_PRINTBUFFER, | ||
394 | index : 0, | ||
395 | cflag : 0, | ||
396 | next : NULL | ||
397 | }; | ||
398 | |||
399 | static struct console sercons1 = { | ||
400 | name : "ttyS", | ||
401 | write: console_write, | ||
402 | read : NULL, | ||
403 | device : crisv32_console_device, | ||
404 | unblank : NULL, | ||
405 | setup : console_setup, | ||
406 | flags : CON_PRINTBUFFER, | ||
407 | index : 1, | ||
408 | cflag : 0, | ||
409 | next : NULL | ||
410 | }; | ||
411 | static struct console sercons2 = { | ||
412 | name : "ttyS", | ||
413 | write: console_write, | ||
414 | read : NULL, | ||
415 | device : crisv32_console_device, | ||
416 | unblank : NULL, | ||
417 | setup : console_setup, | ||
418 | flags : CON_PRINTBUFFER, | ||
419 | index : 2, | ||
420 | cflag : 0, | ||
421 | next : NULL | ||
422 | }; | ||
423 | static struct console sercons3 = { | ||
424 | name : "ttyS", | ||
425 | write: console_write, | ||
426 | read : NULL, | ||
427 | device : crisv32_console_device, | ||
428 | unblank : NULL, | ||
429 | setup : console_setup, | ||
430 | flags : CON_PRINTBUFFER, | ||
431 | index : 3, | ||
432 | cflag : 0, | ||
433 | next : NULL | ||
434 | }; | ||
435 | |||
436 | /* Register console for printk's, etc. */ | ||
437 | int __init | ||
438 | init_etrax_debug(void) | ||
439 | { | ||
440 | static int first = 1; | ||
441 | |||
442 | if (!first) { | ||
443 | unregister_console(&sercons); | ||
444 | register_console(&sercons0); | ||
445 | register_console(&sercons1); | ||
446 | register_console(&sercons2); | ||
447 | register_console(&sercons3); | ||
448 | init_dummy_console(); | ||
449 | return 0; | ||
450 | } | ||
451 | first = 0; | ||
452 | register_console(&sercons); | ||
453 | start_port(port); | ||
454 | |||
455 | #ifdef CONFIG_ETRAX_KGDB | ||
456 | start_port(kgdb_port); | ||
457 | #endif /* CONFIG_ETRAX_KGDB */ | ||
458 | return 0; | ||
459 | } | ||
460 | |||
461 | __initcall(init_etrax_debug); | ||