/*
 **********************************************************************************************
 * Renesas RX62N Serial Communications interface simulator  
 *
 * State: Not implemented
 *
 * Copyright 2012 Jochen Karrer. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are
 * permitted provided that the following conditions are met:
 *
 *   1. Redistributions of source code must retain the above copyright notice, this list of
 *       conditions and the following disclaimer.
 *
 *    2. Redistributions in binary form must reproduce the above copyright notice, this list
 *       of conditions and the following disclaimer in the documentation and/or other materials
 *       provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY Jochen Karrer ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * The views and conclusions contained in the software and documentation are those of the
 * authors and should not be interpreted as representing official policies, either expressed
 * or implied, of Jochen Karrer.
 *
 **********************************************************************************************
 */

#include "cycletimer.h"
#include "bus.h"
#include "signode.h"
#include "sgstring.h"
#include "serial.h"
#include "cpu_rx.h"

#if 0
#define dbgprintf(x...) { fprintf(stderr,x); }
#else
#define dbgprintf(x...)
#endif

#define SCI_SMR(base)	((base) + 0x00)
#define 	SMR_CM		(1 << 7)
#define 	SMR_CHR		(1 << 6)
#define		SMR_PE		(1 << 5)
#define		SMR_PM		(1 << 4)
#define 	SMR_STOP	(1 << 3)
#define 	SMR_MP		(1 << 2)
#define		SMR_CKS_MSK	(3)
#define		SMR_CKS_SHIFT	(0)
#define			SMR_CKS_PCLK	0
#define			SMR_CKS_PCLK4	1
#define			SMR_CKS_PCLK16	2
#define			SMR_CKS_PCLK64	3
#define SCI_BRR(base)	((base) + 0x01)
#define SCI_SCR(base)	((base) + 0x02)
#define		SCR_TIE		(1 << 7)
#define		SCR_RIE		(1 << 6)
#define		SCR_TE		(1 << 5)
#define		SCR_RE		(1 << 4)
#define		SCR_MPIE	(1 << 3)
#define 	SCR_TEIE	(1 << 2)
#define		SCR_CKE_MSK	(3)
#define		SCR_CKE_SHIFT	(0)

#define SCI_TDR(base)	((base) + 0x03)
#define SCI_SSR(base)	((base) + 0x04)
#define		SSR_TDRE	(1 << 7)
#define		SSR_RDRF	(1 << 6)
#define		SSR_ORER	(1 << 5)
#define		SSR_FER		(1 << 4)
#define		SSR_PER		(1 << 3)
#define		SSR_TEND	(1 << 2)
#define		SSR_MPB		(1 << 1)
#define		SSR_MPBT	(1 << 0)

#define SCI_RDR(base)	((base) + 0x05)
#define SCI_SCMR(base)  ((base) + 0x06)
#define		SCMR_BCP2	(1 << 7)
#define		SCMR_SDIR	(1 << 3)
#define		SCMR_SINV	(1 << 2)
#define		SCMR_SMIF	(1 << 0)

#define SCI_SEMR(base)	((base) + 0x07)
#define		SEMR_ABCS	(1 << 4)
#define		SEMR_ACS0	(1 << 0)

typedef struct SCI {
	BusDevice bdev;
	UartPort *backend;
        //Spi_Device *spidev;
	const char *name;
	uint8_t regSMR;
	uint8_t regBRR;
	uint8_t regSCR;
	uint8_t regTDR;
	uint8_t regSSR;
	uint8_t regRDR;
	uint8_t regSCMR;
	uint8_t regSEMR;
	uint16_t tx_shift_reg;
	
	SigNode *sigIrqERI;
	SigNode *sigIrqRXI;
	SigNode *sigIrqTXI;
	SigNode *sigIrqTEI;

	CycleTimer rxBaudTimer;
	uint32_t byte_time;
} SCI;

static inline void
post_rx_interrupt(SCI *sci) 
{
	SigNode_Set(sci->sigIrqRXI,SIG_LOW);
}

static inline void
unpost_rx_interrupt(SCI *sci) 
{
	SigNode_Set(sci->sigIrqRXI,SIG_HIGH);
}

static inline void
post_err_interrupt(SCI *sci) 
{
	SigNode_Set(sci->sigIrqERI,SIG_LOW);
}

static inline void
unpost_err_interrupt(SCI *sci) 
{
	SigNode_Set(sci->sigIrqERI,SIG_HIGH);
}

static void
serial_input(void *eventData) {
	SCI *sci = eventData;
	UartChar c;
        int count = SerialDevice_Read(sci->backend,&c,1);
        if(count == 1) {
		if(sci->regSSR & SSR_RDRF) {
			sci->regSSR |= SSR_ORER;
			fprintf(stderr,"Overflow char %02x\n",c);
		}
		sci->regRDR = c;
		sci->regSSR |= SSR_RDRF;
		post_rx_interrupt(sci);
		SerialDevice_StopRx(sci->backend);
		CycleTimer_Mod(&sci->rxBaudTimer,sci->byte_time);
	}
}

/**
 *************************************************************************
 *************************************************************************
 */
static void
post_tx_interrupt(SCI *sci) 
{
	SigNode_Set(sci->sigIrqTXI,SIG_LOW);
}

static void
post_te_interrupt(SCI *sci) 
{
	SigNode_Set(sci->sigIrqTEI,SIG_LOW);
}

static void
unpost_tx_interrupt(SCI *sci) 
{
	SigNode_Set(sci->sigIrqTXI,SIG_HIGH);
}

static void
unpost_te_interrupt(SCI *sci) 
{
	SigNode_Set(sci->sigIrqTEI,SIG_HIGH);
}



/**
 *********************************************************************
 * \fn static void serial_output(void *eventData); 
 *********************************************************************
 */
static void
serial_output(void *eventData) {
	SCI *sci = eventData;
	int count;
	UartChar data;
	data = sci->tx_shift_reg;
	dbgprintf("Serial output called with %02x\n",data);
	count = SerialDevice_Write(sci->backend,&data,1);
	if(count <= 0) {
		return;
	}
	/* 
	 *******************************************************************
	 * If TDRE is empty then shift register can not be refilled.
         * Trigger a transmitter empty interrupt and set TEND.
	 *******************************************************************
	 */
	if(sci->regSSR & SSR_TDRE) {
		sci->regSSR |= SSR_TEND;
		SerialDevice_StopTx(sci->backend);
		if(sci->regSCR & SCR_TEIE) {
			post_te_interrupt(sci);
		}
	} else {
		sci->tx_shift_reg = sci->regTDR;
		sci->regSSR |= SSR_TDRE;
		if(sci->regSCR & SCR_TIE) {
			dbgprintf("Post TIE, sr now 0x%02x\n",sci->tx_shift_reg);
			post_tx_interrupt(sci);
			unpost_tx_interrupt(sci);
		}
	}
}

static void
rx_baud_timer(void *eventData) {
	SCI *sci = eventData;
	SerialDevice_StartRx(sci->backend);
}

static uint32_t
smr_read(void *clientData,uint32_t address,int rqlen)
{
	SCI *sci = clientData;
	return sci->regSMR;
}

static void
smr_write(void *clientData,uint32_t value,uint32_t address,int rqlen)
{
	fprintf(stderr,"RX SCI write reg SMR not implemented\n");
}

static uint32_t
brr_read(void *clientData,uint32_t address,int rqlen)
{
	SCI *sci = clientData;
	return sci->regBRR;
}

static void
brr_write(void *clientData,uint32_t value,uint32_t address,int rqlen)
{
	fprintf(stderr,"RX SCI write reg BRR not implemented\n");
}

static uint32_t
scr_read(void *clientData,uint32_t address,int rqlen)
{
	SCI *sci = clientData;
	return sci->regSCR;
}

static void
scr_write(void *clientData,uint32_t value,uint32_t address,int rqlen)
{
	SCI *sci = clientData;
	uint8_t diff = value ^ sci->regSCR;
	sci->regSCR = value;
	if(sci->regSCR & SCR_RE) {
		//SerialDevice_StartRx(sci->backend);
		CycleTimer_Mod(&sci->rxBaudTimer,sci->byte_time);
	} else {
		SerialDevice_StopRx(sci->backend);
		CycleTimer_Remove(&sci->rxBaudTimer);
	}
	/* On Clearing TE SSR_TEND is set */
	if((diff & SCR_TE) && !(value & SCR_TE)) {
		sci->regSSR |= SSR_TEND;
		unpost_te_interrupt(sci);
	}
	fprintf(stderr,"RX SCI write reg SCR not implemented\n");
}

static uint32_t
tdr_read(void *clientData,uint32_t address,int rqlen)
{
	SCI *sci = clientData;
	return sci->regTDR;
}

static void
tdr_write(void *clientData,uint32_t value,uint32_t address,int rqlen)
{
	SCI *sci = clientData;
	sci->regTDR = value;	
	if((sci->regSCR & SCR_TE) && (sci->regSSR & SSR_TDRE)) {
		if(sci->regSSR & SSR_TEND) {
			dbgprintf("TDR: Starting tx: 0x%02x\n",value);
			/* Transmited to shift register immediately */
			sci->tx_shift_reg = value;
			if(sci->regSCR & SCR_TIE) {
				post_tx_interrupt(sci);
				unpost_tx_interrupt(sci);
			}
			sci->regSSR &= ~SSR_TEND;
			unpost_te_interrupt(sci);
			SerialDevice_StartTx(sci->backend);
		} else {
			dbgprintf("TDR: %02x, PC: %08x\n",value,RX_REG_PC);
			sci->regSSR &= ~SSR_TDRE;
		}
	} else {
		fprintf(stderr,"SCI TDR: transmitter disabled or TDR busy\n");
	}
}

static uint32_t
ssr_read(void *clientData,uint32_t address,int rqlen)
{
	SCI *sci = clientData;
	return sci->regSSR;
}

static void
ssr_write(void *clientData,uint32_t value,uint32_t address,int rqlen)
{
	SCI *sci = clientData;
	uint8_t clearmask;
	//fprintf(stdout,"SSR: from: %02x ",sci->regSSR);
	sci->regSSR = sci->regSSR & (SSR_TEND | SSR_TDRE | SSR_RDRF | SSR_MPB);
	clearmask = SSR_PER | SSR_FER | SSR_ORER;
	sci->regSSR = (sci->regSSR & ~clearmask) | (value & clearmask);
	if(value & SSR_MPBT) {
		sci->regSSR |= SSR_MPBT;
	} 
	if(sci->regSSR & (SSR_PER | SSR_FER | SSR_ORER)) {
		post_err_interrupt(sci);
	} else {
		unpost_err_interrupt(sci);
	}
	//fprintf(stdout,"to: %02x, value %02x\n",sci->regSSR,value);
}

static uint32_t
rdr_read(void *clientData,uint32_t address,int rqlen)
{
	SCI *sci = clientData;
	sci->regSSR &= ~SSR_RDRF;
	unpost_rx_interrupt(sci);
	return sci->regRDR;
}

static void
rdr_write(void *clientData,uint32_t value,uint32_t address,int rqlen)
{
	fprintf(stderr,"RX SCI write reg RDR not implemented\n");
}

static uint32_t
scmr_read(void *clientData,uint32_t address,int rqlen)
{
	SCI *sci = clientData;
	return sci->regSCMR;
}

static void
scmr_write(void *clientData,uint32_t value,uint32_t address,int rqlen)
{
	fprintf(stderr,"RX SCI write reg SCMR not implemented\n");
}

static uint32_t
semr_read(void *clientData,uint32_t address,int rqlen)
{
	SCI *sci = clientData;
	return sci->regSEMR;
}

static void
semr_write(void *clientData,uint32_t value,uint32_t address,int rqlen)
{
	fprintf(stderr,"RX SCI write reg SEMR not implemented\n");
}

/**
 *************************************************************************************
 * \fn static void SCI_Map(void *owner,uint32_t base,uint32_t mask,uint32_t mapflags)
 * Map the Serial Interface to the memory space.
 *************************************************************************************
 */

static void
SCI_Map(void *owner,uint32_t base,uint32_t mask,uint32_t mapflags)
{
	SCI *sci = (SCI *) owner;
        IOH_New8(SCI_SMR(base),smr_read,smr_write,sci);
        IOH_New8(SCI_BRR(base),brr_read,brr_write,sci);
        IOH_New8(SCI_SCR(base),scr_read,scr_write,sci);
        IOH_New8(SCI_TDR(base),tdr_read,tdr_write,sci);
        IOH_New8(SCI_SSR(base),ssr_read,ssr_write,sci);
        IOH_New8(SCI_RDR(base),rdr_read,rdr_write,sci);
        IOH_New8(SCI_SCMR(base),scmr_read,scmr_write,sci);
        IOH_New8(SCI_SEMR(base),semr_read,semr_write,sci);
}

/**
 *******************************************************************************
 * \fn static void SCI_Unmap(void *owner,uint32_t base,uint32_t mask)
 * Unmap the Serial Interface from the memory space.
 *******************************************************************************
 */
static void
SCI_Unmap(void *owner,uint32_t base,uint32_t mask)
{

        IOH_Delete8(SCI_SMR(base));
        IOH_Delete8(SCI_BRR(base));
        IOH_Delete8(SCI_SCR(base));
        IOH_Delete8(SCI_TDR(base));
        IOH_Delete8(SCI_SSR(base));
        IOH_Delete8(SCI_RDR(base));
        IOH_Delete8(SCI_SCMR(base));
        IOH_Delete8(SCI_SEMR(base));
}

/**
 ***********************************************************************
 * BusDevice * SCI_New(const char *name) 
 * Create a new Serial interface.
 ***********************************************************************
 */
BusDevice *
SCI_New(const char *name) 
{		
	SCI *sci = sg_new(SCI);
	sci->name = sg_strdup(name);
        sci->bdev.first_mapping=NULL;
        sci->bdev.Map = SCI_Map;
        sci->bdev.UnMap = SCI_Unmap;
        sci->bdev.owner = sci;
        sci->bdev.hw_flags=MEM_FLAG_WRITABLE | MEM_FLAG_READABLE;
	sci->backend = Uart_New(name,serial_input,serial_output,NULL,sci);
	sci->byte_time = 9600;
	CycleTimer_Init(&sci->rxBaudTimer,rx_baud_timer,sci);
	sci->regSMR = 0;
	sci->regBRR = 0xFF;
	sci->regSCR = 0x00;
	sci->regTDR = 0xFF;
	sci->regSSR = 0x84;
	sci->regRDR = 0x00;
	sci->regSCMR = 0xF2;
	sci->regSEMR = 0x00;
	sci->sigIrqERI = SigNode_New("%s.irqERI",name);
	sci->sigIrqRXI = SigNode_New("%s.irqRXI",name);
	sci->sigIrqTXI = SigNode_New("%s.irqTXI",name);
	sci->sigIrqTEI = SigNode_New("%s.irqTEI",name);
	if(!sci->sigIrqERI || !sci->sigIrqRXI 
	   || !sci->sigIrqTXI || !sci->sigIrqTEI) 
	{
		fprintf(stderr,"Can not create Interrupt lines for %s\n",name);
		exit(1);
	} 
	fprintf(stderr,"Created Serial Communication Interface \"%s\"\n",name);
	return &sci->bdev;
}
