2008-02-08 04:19:31 -08:00
/* 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>
2009-04-10 14:33:48 +01:00
# include <unit/serial.h>
2010-10-27 17:28:55 +01:00
# include <asm/smp.h>
2008-02-08 04:19:31 -08:00
/*
* 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 ) ;
2010-10-27 17:28:55 +01:00
# if CONFIG_GDBSTUB_IRQ_LEVEL == 0
2008-02-08 04:19:31 -08:00
IVAR0 = EXCEP_IRQ_LEVEL0 ;
2010-10-27 17:28:55 +01:00
# 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 ) ;
2008-02-08 04:19:31 -08:00
XIRQxICR ( GDBPORT_SERIAL_IRQ ) & = ~ GxICR_REQUEST ;
2010-10-27 17:28:55 +01:00
XIRQxICR ( GDBPORT_SERIAL_IRQ ) =
GxICR_ENABLE | NUM2GxICR_LEVEL ( CONFIG_GDBSTUB_IRQ_LEVEL ) ;
2008-02-08 04:19:31 -08:00
tmp = XIRQxICR ( GDBPORT_SERIAL_IRQ ) ;
GDBPORT_SERIAL_IER = UART_IER_RDI | UART_IER_RLSI ;
/* permit level 0 IRQs to take place */
2010-12-07 17:41:40 +00:00
arch_local_change_intr_mask_level (
NUM2EPSW_IM ( CONFIG_GDBSTUB_IRQ_LEVEL + 1 ) ) ;
2008-02-08 04:19:31 -08:00
}
/*
* 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 ;
2010-10-27 17:28:55 +01:00
# if defined(CONFIG_MN10300_WD_TIMER)
int cpu ;
# endif
2008-02-08 04:19:31 -08:00
* _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 ;
2008-12-03 16:33:14 +00:00
barrier ( ) ;
2008-02-08 04:19:31 -08:00
if ( ix = = gdbstub_rx_inp ) {
if ( nonblock )
return - EAGAIN ;
# ifdef CONFIG_MN10300_WD_TIMER
2010-10-27 17:28:55 +01:00
for ( cpu = 0 ; cpu < NR_CPUS ; cpu + + )
watchdog_alert_counter [ cpu ] = 0 ;
# endif
2008-02-08 04:19:31 -08:00
goto try_again ;
}
ch = gdbstub_rx_buffer [ ix + + ] ;
st = gdbstub_rx_buffer [ ix + + ] ;
2008-12-03 16:33:14 +00:00
barrier ( ) ;
2008-02-08 04:19:31 -08:00
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 ) ;
}