/*  Copyright (C) 2007 NXP B.V.
    All Rights Reserved.
*//**
    @file   gdb_hook.c
    @brief  Uart connectivity to enable the use of KGDB

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Or, point your browser to http://www.gnu.org/copyleft/gpl.html

*/

/***********************************************
* INCLUDE FILES                                *
************************************************/

#include <asm/mach-pnx833x/pnx833x.h>
#include <linux/serial_pnx8xxx.h>

/***********************************************
* LOCAL MACROS                                 *
************************************************/

#define UART0 (unsigned char *)PNX833X_UART0_PORTS_START
#define UART1 (unsigned char *)PNX833X_UART1_PORTS_START

/***********************************************
* LOCAL TYPEDEFS                               *
************************************************/

/***********************************************
* STATIC FUNCTION PROTOTYPES                   *
************************************************/

/***********************************************
* STATIC DATA                                  *
************************************************/
/** Make sure that we setup console to correct uart 
    as prom_puChar is used early on before proper kernel init.*/
#ifdef CONFIG_SERIAL_PNX8XXX_TTYS0
static unsigned char * kgdbUart    = UART1;
static unsigned char * consoleUart = UART0;
#else
static unsigned char * kgdbUart    = UART0;
static unsigned char * consoleUart = UART1;
#endif

static volatile int delay_count;

/***********************************************
* EXPORTED DATA                                *
************************************************/

/***********************************************
* FUNCTION IMPLEMENTATION                      *
************************************************/

static unsigned int serial_in(unsigned char * baseAddress, int offset)
{
    return *((unsigned int volatile *)(baseAddress + offset));
}

static void serial_out(unsigned char * baseAddress, int offset,int value)
{
    *((unsigned int volatile *)(baseAddress + offset)) = value;
}

static void do_delay(void)
{
    int i;
    for(i=0; i<10000; i++)
    {
        delay_count++;
    }
}

static int putChar(unsigned char * baseAddress, char c)
{
    /* Wait for TX to be ready */
    while(((serial_in(baseAddress, PNX8XXX_FIFO) & PNX8XXX_UART_FIFO_TXFIFO) >> 16) > 15)
    {
        do_delay();
    }

    /* Send the next character */
    serial_out(baseAddress, PNX8XXX_FIFO, c);
    serial_out(baseAddress, PNX8XXX_ICLR, PNX8XXX_UART_INT_TX);

    return 1;
}

static char getChar(unsigned char * baseAddress)
{
    char output;

    /* Wait for RX to be ready */
    while ((serial_in(baseAddress, PNX8XXX_FIFO) & PNX8XXX_UART_FIFO_RXFIFO) == 0)
    {
        do_delay();
    }

    /* Get the character */
    output = serial_in(baseAddress, PNX8XXX_FIFO) & 0xFF;

    /* Move onto the next character in the buffer */
    serial_out(baseAddress, PNX8XXX_LCR, serial_in(baseAddress, PNX8XXX_LCR) | PNX8XXX_UART_LCR_RX_NEXT);
    serial_out(baseAddress, PNX8XXX_ICLR, PNX8XXX_UART_INT_RX);

    return output;
}

static void serialInit(unsigned char * baseAddress)
{
    serial_out(baseAddress, PNX8XXX_LCR, PNX8XXX_UART_LCR_8BIT | PNX8XXX_UART_LCR_TX_RST | PNX8XXX_UART_LCR_RX_RST);
    serial_out(baseAddress, PNX8XXX_MCR, PNX8XXX_UART_MCR_DTR | PNX8XXX_UART_MCR_RTS);
    serial_out(baseAddress, PNX8XXX_BAUD, 1); // 115200 Baud
    serial_out(baseAddress, PNX8XXX_CFG, 0x00060030);
    serial_out(baseAddress, PNX8XXX_ICLR, -1);
    serial_out(baseAddress, PNX8XXX_IEN, 0);
}

static void setupSerialOutput(void)
{
    static bool initialised = false;
    if (!initialised) 
    {
        serialInit(kgdbUart);
        serialInit(consoleUart);
        initialised = true;
    }
}

int rs_kgdb_hook(int tty_no, int speed) 
{
    kgdbUart    = tty_no ? UART1 : UART0;
    consoleUart = tty_no ? UART0 : UART1;

    setupSerialOutput();

    return speed;
}

int prom_putchar(char c)
{
    setupSerialOutput();
    return putChar(consoleUart, c);
}

char prom_getchar(void)
{
    setupSerialOutput();
    return getChar(consoleUart);
}

int putDebugChar(char c)
{
    setupSerialOutput();
    return putChar(kgdbUart, c);
}

char getDebugChar(void)
{
    setupSerialOutput();
    return getChar(kgdbUart);
}
