Logo Search packages:      
Sourcecode: linux version File versions  Download package

gdb-io-ttysm.c

/* 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/system.h>
#include <asm/gdb-stub.h>
#include <asm/exceptions.h>
#include <asm/unit/clock.h>
#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 */
      set_intr_level(gdbstub_port->rx_irq, GxICR_LEVEL_0);
      set_intr_level(gdbstub_port->tx_irq, GxICR_LEVEL_0);
      set_intr_stub(EXCEP_IRQ_LEVEL0, gdbstub_io_rx_handler);

      *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 */
      asm volatile(
            "     and %0,epsw \n"
            "     or %1,epsw  \n"
            :
            : "i"(~EPSW_IM), "i"(EPSW_IE|EPSW_IM_1)
            );
}

/*
 * 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;

      *_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
            watchdog_alert_counter = 0;
#endif /* CONFIG_MN10300_WD_TIMER */
            goto try_again;
      }

      ch = gdbstub_rx_buffer[ix++];
      st = gdbstub_rx_buffer[ix++];
      barrier();
      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;
}

Generated by  Doxygen 1.6.0   Back to index