diff options
Diffstat (limited to 'arch/mn10300/kernel/gdb-io-serial.c')
-rw-r--r-- | arch/mn10300/kernel/gdb-io-serial.c | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/arch/mn10300/kernel/gdb-io-serial.c b/arch/mn10300/kernel/gdb-io-serial.c new file mode 100644 index 000000000..df5124274 --- /dev/null +++ b/arch/mn10300/kernel/gdb-io-serial.c @@ -0,0 +1,174 @@ +/* 16550 serial driver for gdbstub I/O + * + * Copyright (C) 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/console.h> +#include <linux/init.h> +#include <linux/nmi.h> + +#include <asm/pgtable.h> +#include <asm/gdb-stub.h> +#include <asm/exceptions.h> +#include <asm/serial-regs.h> +#include <unit/serial.h> +#include <asm/smp.h> + +/* + * initialise the GDB stub + */ +void gdbstub_io_init(void) +{ + u16 tmp; + + /* set up the serial port */ + GDBPORT_SERIAL_LCR = UART_LCR_WLEN8; /* 1N8 */ + GDBPORT_SERIAL_FCR = (UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT); + + FLOWCTL_CLEAR(DTR); + FLOWCTL_SET(RTS); + + gdbstub_io_set_baud(115200); + + /* we want to get serial receive interrupts */ + XIRQxICR(GDBPORT_SERIAL_IRQ) = 0; + tmp = XIRQxICR(GDBPORT_SERIAL_IRQ); + +#if CONFIG_GDBSTUB_IRQ_LEVEL == 0 + IVAR0 = EXCEP_IRQ_LEVEL0; +#elif CONFIG_GDBSTUB_IRQ_LEVEL == 1 + IVAR1 = EXCEP_IRQ_LEVEL1; +#elif CONFIG_GDBSTUB_IRQ_LEVEL == 2 + IVAR2 = EXCEP_IRQ_LEVEL2; +#elif CONFIG_GDBSTUB_IRQ_LEVEL == 3 + IVAR3 = EXCEP_IRQ_LEVEL3; +#elif CONFIG_GDBSTUB_IRQ_LEVEL == 4 + IVAR4 = EXCEP_IRQ_LEVEL4; +#elif CONFIG_GDBSTUB_IRQ_LEVEL == 5 + IVAR5 = EXCEP_IRQ_LEVEL5; +#else +#error "Unknown irq level for gdbstub." +#endif + + set_intr_stub(NUM2EXCEP_IRQ_LEVEL(CONFIG_GDBSTUB_IRQ_LEVEL), + gdbstub_io_rx_handler); + + XIRQxICR(GDBPORT_SERIAL_IRQ) &= ~GxICR_REQUEST; + XIRQxICR(GDBPORT_SERIAL_IRQ) = + GxICR_ENABLE | NUM2GxICR_LEVEL(CONFIG_GDBSTUB_IRQ_LEVEL); + tmp = XIRQxICR(GDBPORT_SERIAL_IRQ); + + GDBPORT_SERIAL_IER = UART_IER_RDI | UART_IER_RLSI; + + /* permit level 0 IRQs to take place */ + arch_local_change_intr_mask_level( + NUM2EPSW_IM(CONFIG_GDBSTUB_IRQ_LEVEL + 1)); +} + +/* + * set up the GDB stub serial port baud rate timers + */ +void gdbstub_io_set_baud(unsigned baud) +{ + unsigned value; + u8 lcr; + + value = 18432000 / 16 / baud; + + lcr = GDBPORT_SERIAL_LCR; + GDBPORT_SERIAL_LCR |= UART_LCR_DLAB; + GDBPORT_SERIAL_DLL = value & 0xff; + GDBPORT_SERIAL_DLM = (value >> 8) & 0xff; + GDBPORT_SERIAL_LCR = lcr; +} + +/* + * wait for a character to come from the debugger + */ +int gdbstub_io_rx_char(unsigned char *_ch, int nonblock) +{ + unsigned ix; + u8 ch, st; +#if defined(CONFIG_MN10300_WD_TIMER) + int cpu; +#endif + + *_ch = 0xff; + + if (gdbstub_rx_unget) { + *_ch = gdbstub_rx_unget; + gdbstub_rx_unget = 0; + return 0; + } + + try_again: + /* pull chars out of the buffer */ + ix = gdbstub_rx_outp; + barrier(); + if (ix == gdbstub_rx_inp) { + if (nonblock) + return -EAGAIN; +#ifdef CONFIG_MN10300_WD_TIMER + for (cpu = 0; cpu < NR_CPUS; cpu++) + watchdog_alert_counter[cpu] = 0; +#endif + goto try_again; + } + + ch = gdbstub_rx_buffer[ix++]; + st = gdbstub_rx_buffer[ix++]; + barrier(); + gdbstub_rx_outp = ix & 0x00000fff; + + if (st & UART_LSR_BI) { + gdbstub_proto("### GDB Rx Break Detected ###\n"); + return -EINTR; + } else if (st & (UART_LSR_FE | UART_LSR_OE | UART_LSR_PE)) { + gdbstub_proto("### GDB Rx Error (st=%02x) ###\n", st); + return -EIO; + } else { + gdbstub_proto("### GDB Rx %02x (st=%02x) ###\n", ch, st); + *_ch = ch & 0x7f; + return 0; + } +} + +/* + * send a character to the debugger + */ +void gdbstub_io_tx_char(unsigned char ch) +{ + FLOWCTL_SET(DTR); + LSR_WAIT_FOR(THRE); + /* FLOWCTL_WAIT_FOR(CTS); */ + + if (ch == 0x0a) { + GDBPORT_SERIAL_TX = 0x0d; + LSR_WAIT_FOR(THRE); + /* FLOWCTL_WAIT_FOR(CTS); */ + } + GDBPORT_SERIAL_TX = ch; + + FLOWCTL_CLEAR(DTR); +} + +/* + * send a character to the debugger + */ +void gdbstub_io_tx_flush(void) +{ + LSR_WAIT_FOR(TEMT); + LSR_WAIT_FOR(THRE); + FLOWCTL_CLEAR(DTR); +} |