2008-02-08 15:19:31 +03:00
/* MN10300 On-chip 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/tty.h>
# include <asm/pgtable.h>
# include <asm/gdb-stub.h>
# include <asm/exceptions.h>
2009-04-10 17:33:48 +04:00
# include <unit/clock.h>
2008-02-08 15:19:31 +03:00
# include "mn10300-serial.h"
# if defined(CONFIG_GDBSTUB_ON_TTYSM0)
struct mn10300_serial_port * const gdbstub_port = & mn10300_serial_port_sif0 ;
# elif defined(CONFIG_GDBSTUB_ON_TTYSM1)
struct mn10300_serial_port * const gdbstub_port = & mn10300_serial_port_sif1 ;
# else
struct mn10300_serial_port * const gdbstub_port = & mn10300_serial_port_sif2 ;
# endif
/*
* initialise the GDB stub I / O routines
*/
void __init gdbstub_io_init ( void )
{
uint16_t scxctr ;
int tmp ;
switch ( gdbstub_port - > clock_src ) {
case MNSCx_CLOCK_SRC_IOCLK :
gdbstub_port - > ioclk = MN10300_IOCLK ;
break ;
# ifdef MN10300_IOBCLK
case MNSCx_CLOCK_SRC_IOBCLK :
gdbstub_port - > ioclk = MN10300_IOBCLK ;
break ;
# endif
default :
BUG ( ) ;
}
/* set up the serial port */
gdbstub_io_set_baud ( 115200 ) ;
/* we want to get serial receive interrupts */
2010-10-27 20:28:55 +04:00
set_intr_level ( gdbstub_port - > rx_irq ,
2011-03-18 19:54:30 +03:00
NUM2GxICR_LEVEL ( CONFIG_DEBUGGER_IRQ_LEVEL ) ) ;
2010-10-27 20:28:55 +04:00
set_intr_level ( gdbstub_port - > tx_irq ,
2011-03-18 19:54:30 +03:00
NUM2GxICR_LEVEL ( CONFIG_DEBUGGER_IRQ_LEVEL ) ) ;
set_intr_stub ( NUM2EXCEP_IRQ_LEVEL ( CONFIG_DEBUGGER_IRQ_LEVEL ) ,
2010-10-27 20:28:55 +04:00
gdbstub_io_rx_handler ) ;
2008-02-08 15:19:31 +03:00
* gdbstub_port - > rx_icr | = GxICR_ENABLE ;
tmp = * gdbstub_port - > rx_icr ;
/* enable the device */
scxctr = SC01CTR_CLN_8BIT ; /* 1N8 */
switch ( gdbstub_port - > div_timer ) {
case MNSCx_DIV_TIMER_16BIT :
scxctr | = SC0CTR_CK_TM8UFLOW_8 ; /* == SC1CTR_CK_TM9UFLOW_8
= = SC2CTR_CK_TM10UFLOW_8 */
break ;
case MNSCx_DIV_TIMER_8BIT :
scxctr | = SC0CTR_CK_TM2UFLOW_8 ;
break ;
}
scxctr | = SC01CTR_TXE | SC01CTR_RXE ;
* gdbstub_port - > _control = scxctr ;
tmp = * gdbstub_port - > _control ;
/* permit level 0 IRQs only */
2010-12-07 20:41:40 +03:00
arch_local_change_intr_mask_level (
2011-03-18 19:54:30 +03:00
NUM2EPSW_IM ( CONFIG_DEBUGGER_IRQ_LEVEL + 1 ) ) ;
2008-02-08 15:19:31 +03:00
}
/*
* set up the GDB stub serial port baud rate timers
*/
void gdbstub_io_set_baud ( unsigned baud )
{
const unsigned bits = 10 ; /* 1 [start] + 8 [data] + 0 [parity] +
* 1 [ stop ] */
unsigned long ioclk = gdbstub_port - > ioclk ;
unsigned xdiv , tmp ;
uint16_t tmxbr ;
uint8_t tmxmd ;
if ( ! baud ) {
baud = 9600 ;
} else if ( baud = = 134 ) {
baud = 269 ; /* 134 is really 134.5 */
xdiv = 2 ;
}
try_alternative :
xdiv = 1 ;
switch ( gdbstub_port - > div_timer ) {
case MNSCx_DIV_TIMER_16BIT :
tmxmd = TM8MD_SRC_IOCLK ;
tmxbr = tmp = ( ioclk / ( baud * xdiv ) + 4 ) / 8 - 1 ;
if ( tmp > 0 & & tmp < = 65535 )
goto timer_okay ;
tmxmd = TM8MD_SRC_IOCLK_8 ;
tmxbr = tmp = ( ioclk / ( baud * 8 * xdiv ) + 4 ) / 8 - 1 ;
if ( tmp > 0 & & tmp < = 65535 )
goto timer_okay ;
tmxmd = TM8MD_SRC_IOCLK_32 ;
tmxbr = tmp = ( ioclk / ( baud * 32 * xdiv ) + 4 ) / 8 - 1 ;
if ( tmp > 0 & & tmp < = 65535 )
goto timer_okay ;
break ;
case MNSCx_DIV_TIMER_8BIT :
tmxmd = TM2MD_SRC_IOCLK ;
tmxbr = tmp = ( ioclk / ( baud * xdiv ) + 4 ) / 8 - 1 ;
if ( tmp > 0 & & tmp < = 255 )
goto timer_okay ;
tmxmd = TM2MD_SRC_IOCLK_8 ;
tmxbr = tmp = ( ioclk / ( baud * 8 * xdiv ) + 4 ) / 8 - 1 ;
if ( tmp > 0 & & tmp < = 255 )
goto timer_okay ;
tmxmd = TM2MD_SRC_IOCLK_32 ;
tmxbr = tmp = ( ioclk / ( baud * 32 * xdiv ) + 4 ) / 8 - 1 ;
if ( tmp > 0 & & tmp < = 255 )
goto timer_okay ;
break ;
}
/* as a last resort, if the quotient is zero, default to 9600 bps */
baud = 9600 ;
goto try_alternative ;
timer_okay :
gdbstub_port - > uart . timeout = ( 2 * bits * HZ ) / baud ;
gdbstub_port - > uart . timeout + = HZ / 50 ;
/* set the timer to produce the required baud rate */
switch ( gdbstub_port - > div_timer ) {
case MNSCx_DIV_TIMER_16BIT :
* gdbstub_port - > _tmxmd = 0 ;
* gdbstub_port - > _tmxbr = tmxbr ;
* gdbstub_port - > _tmxmd = TM8MD_INIT_COUNTER ;
* gdbstub_port - > _tmxmd = tmxmd | TM8MD_COUNT_ENABLE ;
break ;
case MNSCx_DIV_TIMER_8BIT :
* gdbstub_port - > _tmxmd = 0 ;
* ( volatile u8 * ) gdbstub_port - > _tmxbr = ( u8 ) tmxbr ;
* gdbstub_port - > _tmxmd = TM2MD_INIT_COUNTER ;
* gdbstub_port - > _tmxmd = tmxmd | TM2MD_COUNT_ENABLE ;
break ;
}
}
/*
* 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 20:28:55 +04:00
# if defined(CONFIG_MN10300_WD_TIMER)
int cpu ;
# endif
2008-02-08 15:19:31 +03: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-02-19 21:58:54 +03:00
barrier ( ) ;
2008-02-08 15:19:31 +03:00
if ( ix = = gdbstub_rx_inp ) {
if ( nonblock )
return - EAGAIN ;
# ifdef CONFIG_MN10300_WD_TIMER
2010-10-27 20:28:55 +04:00
for ( cpu = 0 ; cpu < NR_CPUS ; cpu + + )
watchdog_alert_counter [ cpu ] = 0 ;
# endif
2008-02-08 15:19:31 +03:00
goto try_again ;
}
ch = gdbstub_rx_buffer [ ix + + ] ;
st = gdbstub_rx_buffer [ ix + + ] ;
2008-02-19 21:58:54 +03:00
barrier ( ) ;
2008-02-08 15:19:31 +03:00
gdbstub_rx_outp = ix & ( PAGE_SIZE - 1 ) ;
st & = SC01STR_RXF | SC01STR_RBF | SC01STR_FEF | SC01STR_PEF |
SC01STR_OEF ;
/* deal with what we've got
* - note that the UART doesn ' t do BREAK - detection for us
*/
if ( st & SC01STR_FEF & & ch = = 0 ) {
switch ( gdbstub_port - > rx_brk ) {
case 0 : gdbstub_port - > rx_brk = 1 ; goto try_again ;
case 1 : gdbstub_port - > rx_brk = 2 ; goto try_again ;
case 2 :
gdbstub_port - > rx_brk = 3 ;
gdbstub_proto ( " ### GDB MNSERIAL Rx Break Detected "
" ### \n " ) ;
return - EINTR ;
default :
goto try_again ;
}
} else if ( st & SC01STR_FEF ) {
if ( gdbstub_port - > rx_brk )
goto try_again ;
gdbstub_proto ( " ### GDB MNSERIAL Framing Error ### \n " ) ;
return - EIO ;
} else if ( st & SC01STR_OEF ) {
if ( gdbstub_port - > rx_brk )
goto try_again ;
gdbstub_proto ( " ### GDB MNSERIAL Overrun Error ### \n " ) ;
return - EIO ;
} else if ( st & SC01STR_PEF ) {
if ( gdbstub_port - > rx_brk )
goto try_again ;
gdbstub_proto ( " ### GDB MNSERIAL Parity Error ### \n " ) ;
return - EIO ;
} else {
/* look for the tail-end char on a break run */
if ( gdbstub_port - > rx_brk = = 3 ) {
switch ( ch ) {
case 0xFF :
case 0xFE :
case 0xFC :
case 0xF8 :
case 0xF0 :
case 0xE0 :
case 0xC0 :
case 0x80 :
case 0x00 :
gdbstub_port - > rx_brk = 0 ;
goto try_again ;
default :
break ;
}
}
gdbstub_port - > rx_brk = 0 ;
gdbstub_io ( " ### 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 )
{
while ( * gdbstub_port - > _status & SC01STR_TBF )
continue ;
if ( ch = = 0x0a ) {
* ( u8 * ) gdbstub_port - > _txb = 0x0d ;
while ( * gdbstub_port - > _status & SC01STR_TBF )
continue ;
}
* ( u8 * ) gdbstub_port - > _txb = ch ;
}
/*
* flush the transmission buffers
*/
void gdbstub_io_tx_flush ( void )
{
while ( * gdbstub_port - > _status & ( SC01STR_TBF | SC01STR_TXF ) )
continue ;
}