#include "pxa255_UART.h"
#include "mem.h"



//TODO: signal handler for Ctl+C and send break to fifo :)
//todo: recalc ints after eveyr write and every call to "process" from SoC


#define UART_IER_DMAE		0x80	//DMA enable
#define UART_IER_UUE		0x40	//Uart unit enable
#define UART_IER_NRZE		0x20	//NRZI enable
#define UART_IER_RTOIE		0x10	//transmit timeout interrupt enable
#define UART_IER_MIE		0x08	//modem interrupt enable
#define UART_IER_RLSE		0x04	//receiver line status interrupt enable
#define UART_IER_TIE		0x02	//transmit data request interrupt enable
#define UART_IER_RAVIE		0x01	//receiver data available interrupt enable

#define UART_IIR_FIFOES		0xC0	//fifo enable status
#define UART_IIR_TOD		0x08	//character timout interrupt pending
#define UART_IIR_RECV_ERR	0x06	//receive error(overrun, parity, framing, break)
#define UART_IIR_RECV_DATA	0x04	//receive data available
#define UART_IIR_RCV_TIMEOUT	0x0C	//receive data in buffer and been a while since we've seen more
#define UART_IIR_SEND_DATA	0x02	//transmit fifo requests data
#define UART_IIR_MODEM_STAT	0x00	//modem lines changed state(CTS, DSR, DI, DCD)
#define UART_IIR_NOINT		0x01	//no interrupt pending


#define UART_FCR_ITL_MASK	0xC0	//mask for ITL part of FCR
#define UART_FCR_ITL_1		0x00	//interrupt when >=1 byte in recv fifo
#define UART_FCR_ITL_8		0x40	//interrupt when >=8 byte in recv fifo
#define UART_FCR_ITL_16		0x80	//interrupt when >=16 byte in recv fifo
#define UART_FCR_ITL_32		0xC0	//interrupt when >=32 byte in recv fifo
#define UART_FCR_RESETTF	0x04	//reset tranmitter fifo
#define UART_FCR_RESETRF	0x02	//reset receiver fifo
#define UART_FCR_TRFIFOE	0x01	//transmit and receive fifo enable

#define UART_LCR_DLAB		0x80	//divisor latch access bit
#define UART_LCR_SB		0x40	//send break
#define UART_LCR_STKYP		0x20	//sticky parity (send parity bit but dont care what value)
#define UART_LCR_EPS		0x10	//even parity select
#define UART_LCR_PEN		0x08	//parity enable
#define UART_LCR_STB		0x04	//stop bits (1 = 2, 0 = 1)
#define UART_LCR_WLS_MASK	0x03	//mask for WLS values
#define UART_LCR_WLS_8		0x03	//8 bit words
#define UART_LCR_WLS_7		0x02	//7 bit words
#define UART_LCR_WLS_6		0x01	//6 bit words
#define UART_LCR_WLS_5		0x00	//5 bit words

#define UART_LSR_FIFOE		0x80	//fifo contails an error (framing, parity, or break)
#define UART_LSR_TEMT		0x40	//tranmitter empty (shift reg is empty and no more byte sin fifo/no byte in holding reg)
#define UART_LSR_TDRQ		0x20	//transmitter data request (see docs)
#define UART_LSR_BI		0x10	//send when char at front of fifo (or in holding reg) was a break char (chr reads as zero by itself)
#define UART_LSR_FE		0x08	//same as above, but for framing errors
#define UART_LSR_PE		0x04	//dame as above, but for parity errors
#define UART_LSR_OE		0x02	//recv fifo overran
#define UART_LSR_DR		0x01	//byte received

#define UART_MCR_LOOP		0x10	//loop modem control lines (not full loopback)
#define UART_MCR_OUT2		0x08	//when loop is 0 enables or disables UART interrupts
#define UART_MCR_OUT1		0x04	//force RI to 1
#define UART_MCR_RTS		0x02	//1 -> nRTS is 0
#define UART_MCR_DTR		0x01	//0 -> nDTR is 0

#define UART_MSR_DCD		0x80
#define UART_MSR_RI		0x40
#define UART_MSR_DSR		0x20
#define UART_MSR_CTS		0x10
#define UART_MSR_DDCD		0x08	//dcd changed since last read
#define UART_MSR_TERI		0x04	//ri has changed from 0 to 1 since last read
#define UART_MSR_DDSR		0x02	//dsr changed since last read
#define UART_MSR_DCTS		0x01	//cts changed since last read



static void pxa255uartPrvRecalc(Pxa255uart* uart);


static void pxa255uartPrvIrq(Pxa255uart* uart, Boolean raise){
	
	pxa255icInt(uart->ic, uart->irq, !(uart->MCR & UART_MCR_LOOP) && (uart->MCR & UART_MCR_OUT2) && raise/* only raise if ints are enabled */);
}

static UInt16 pxa255uartPrvDefaultRead(_UNUSED_ void* userData){			//these are special funcs since they always get their own userData - the uart pointer :)
	
	return UART_CHAR_NONE;	//we read nothing..as always
}

static void pxa255uartPrvDefaultWrite(_UNUSED_ UInt16 chr, _UNUSED_ void* userData){	//these are special funcs since they always get their own userData - the uart pointer :)
	
	//nothing to do here
}

static UInt16 pxa255uartPrvGetchar(Pxa255uart* uart){
	
	Pxa255UartReadF func = uart->readF;
	void* data = (func == pxa255uartPrvDefaultRead) ? uart : uart->accessFuncsData;
	
	return func(data);
} 

static void pxa255uartPrvPutchar(Pxa255uart* uart, UInt16 chr){
	
	Pxa255UartWriteF func = uart->writeF;
	void* data = (func == pxa255uartPrvDefaultWrite) ? uart : uart->accessFuncsData;
	
	func(chr, data);
}

UInt8 pxa255uartPrvFifoUsed(UartFifo* fifo){	//return num spots used
	
	UInt8 v;
	
	if(fifo->read == UART_FIFO_EMPTY) return 0;
	v = fifo->write + UART_FIFO_DEPTH - fifo->read;
	if(v > UART_FIFO_DEPTH) v -=UART_FIFO_DEPTH;
	
	return v;
}

void pxa255uartPrvFifoFlush(UartFifo* fifo){
	
	fifo->read = UART_FIFO_EMPTY;
	fifo->write = UART_FIFO_EMPTY;
}

Boolean pxa255uartPrvFifoPut(UartFifo* fifo, UInt16 val){	//return success
	
	if(fifo->read == UART_FIFO_EMPTY){
		
		fifo->read = 0;
		fifo->write = 1;
		fifo->buf[0] = val;	
	}
	else if(fifo->read != fifo->write){	//only if not full
		
		fifo->buf[fifo->write++] = val;
		if(fifo->write == UART_FIFO_DEPTH) fifo->write = 0;
	}
	else return false;
	
	return true;
}

UInt16 pxa255uartPrvFifoGet(UartFifo* fifo){
	
	UInt16 ret;
	
	if(fifo->read == UART_FIFO_EMPTY){
		
		ret = 0xFFFF;	//why not?
	}
	else{
		
		ret = fifo->buf[fifo->read++];
		if(fifo->read == UART_FIFO_DEPTH) fifo->read = 0;
		
		if(fifo->read == fifo->write){	//it is now empty
			
			fifo->read = UART_FIFO_EMPTY;
			fifo->write = UART_FIFO_EMPTY;
		}
	}
	
	return ret;
}

UInt16 pxa255uartPrvFifoPeekNth(UartFifo* fifo, UInt8 n){
	
	UInt16 ret;
	
	
	if(fifo->read == UART_FIFO_EMPTY){
		
		ret = 0xFFFF;	//why not?
	}
	else{
		
		n += fifo->read;
		if(n >= UART_FIFO_DEPTH) n-= UART_FIFO_DEPTH;
		ret = fifo->buf[n];
	}
	
	return ret;
}

UInt16 pxa255uartPrvFifoPeek(UartFifo* fifo){
	
	return pxa255uartPrvFifoPeekNth(fifo, 0);
}


static void sendVal(Pxa255uart* uart, UInt16 v){
	
	if(uart->LSR & UART_LSR_TEMT){	//if transmit, put in shift register immediately if it's idle
			
		uart->transmitShift = v;
		uart->LSR &=~ UART_LSR_TEMT;	
	}
	else if(uart->FCR & UART_FCR_TRFIFOE){	//put in tx fifo if in fifo mode
		
		pxa255uartPrvFifoPut(&uart->TX, v);
		if(pxa255uartPrvFifoUsed(&uart->TX) > UART_FIFO_DEPTH / 2){	//we go went below half-full buffer - set appropriate bit...
			
			uart->LSR &=~ UART_LSR_TDRQ;
		}
	}
	else if(uart->LSR & UART_LSR_TDRQ){	//send without fifo if in polled mode
			
		uart->transmitHolding = v;
		uart->LSR &=~ UART_LSR_TDRQ;
	}
	else{
		
		//nothing to do - buffer is full so we ignore this request
	}	
}

static Boolean pxa255uartPrvMemAccessF(void* userData, UInt32 pa, UInt8 size, Boolean write, void* buf){

	Pxa255uart* uart = userData;
	Boolean DLAB = (uart->LCR & UART_LCR_DLAB) != 0;
	Boolean recalcValues = false;
	UInt8 t, val = 0;
	
	if(size != 4 && size != 1) {
		err_str(__FILE__ ": Unexpected ");
	//	err_str(write ? "write" : "read");
	//	err_str(" of ");
	//	err_dec(size);
	//	err_str(" bytes to 0x");
	//	err_hex(pa);
	//	err_str("\r\n");
		return true;		//we do not support non-word accesses
	}
	
	pa = (pa - uart->baseAddr) >> 2;
	
	if(write){
		recalcValues = true;
		val = (size == 1) ? *(UInt8*)buf : *(UInt32*)buf;
		
		switch(pa){
			case 0:
				if(DLAB){				//if DLAB - set "baudrate"...
					uart->DLL = val;
					recalcValues = false;
				}
				else{
					
					sendVal(uart, val);
				}
				break;
			
			case 1:
				if(DLAB){
					
					uart->DLH = val;
					recalcValues = false;
				}
				else{
					t = uart->IER ^ val;
				
					if(t & UART_IER_DMAE){
						
						err_str("pxa255UART: DMA mode cannot be enabled");
						t &=~ UART_IER_DMAE;	//undo the change
					}
					
					if(t & UART_IER_UUE){
						
						if(val & UART_IER_UUE){
							
							uart->LSR = UART_LSR_TEMT | UART_LSR_TDRQ;
							uart->MSR = UART_MSR_CTS;
						}	
					}
				
					uart->IER ^= t;
				}
				break;
			
			case 2:
				t = uart->FCR ^ val;
				if(t & UART_FCR_TRFIFOE){
					if(val & UART_FCR_TRFIFOE){			//fifos are now on - perform other actions as requested
						
						if(val & UART_FCR_RESETRF){
							
							pxa255uartPrvFifoFlush(&uart->RX);	//clear the RX fifo now
						}
						if(val & UART_FCR_RESETTF){
							
							pxa255uartPrvFifoFlush(&uart->TX);	//clear the TX fifo now
							uart->LSR = UART_LSR_TEMT | UART_LSR_TDRQ;
						}
						uart->IIR = UART_IIR_FIFOES  |UART_IIR_NOINT;
					}
					else{
						pxa255uartPrvFifoFlush(&uart->TX);
						pxa255uartPrvFifoFlush(&uart->RX);
						uart->LSR = UART_LSR_TEMT | UART_LSR_TDRQ;
						uart->IIR = UART_IIR_NOINT;
					}
				}
				uart->FCR = val;
				break;
			
			case 3:
				t = uart->LCR ^ val;
				if(t & UART_LCR_SB){
					if(val & UART_LCR_SB){	//break set (tx line pulled low)
				
				
					}
					else{			//break cleared (tx line released)
						
						sendVal(uart, UART_CHAR_BREAK);
					}
				}
				uart->LCR = val;
				break;
			
			case 4:
				uart->MCR = val;
				break;
			
			case 7:
				uart->SPR = val;
				break;
			
			case 8:
				uart->ISR = val;
				if(val & 3){
					err_str("UART: IrDA mode set on UART\n");
				}
				break;
		}
	}
	else{
		switch(pa){
			case 0:
				if(DLAB) val = uart->DLL;
				else if(!(uart->LSR & UART_LSR_DR)){	//no data-> too bad
						
					val = 0;	
				}
				else if(uart->FCR & UART_FCR_TRFIFOE){	//fifo mode -> read fifo
					
					val = pxa255uartPrvFifoGet(&uart->RX);
					if(!pxa255uartPrvFifoUsed(&uart->RX)) uart->LSR &=~ UART_LSR_DR;
					recalcValues = true;		//error bits might have changed
				}
				else{					//polled mode -> read rx polled reg
					
					val = uart->receiveHolding;
					uart->LSR &=~ UART_LSR_DR;
				}
				break;
			
			case 1:
				if(DLAB) val = uart->DLH;
				else val = uart->IER;
				break;
			
			case 2:
				val = uart->IIR;
				break;
			
			case 3:
				val = uart->LCR;
				break;
			
			case 4:
				val = uart->MCR;
				break;
			
			case 5:
				val = uart->LSR;
				break;
			
			case 6:
				val = uart->MSR;
				break;
			
			case 7:
				val = uart->SPR;
				break;
			
			case 8:
				val = uart->ISR;
				break;
		}
		if(size == 1) *(UInt8*)buf = val;
		else *(UInt32*)buf = val;
	}
	
	if(recalcValues) pxa255uartPrvRecalc(uart);
	
	return true;
}

void pxa255uartSetFuncs(Pxa255uart* uart, Pxa255UartReadF readF, Pxa255UartWriteF writeF, void* userData){
	
	if(!readF) readF = pxa255uartPrvDefaultRead;		//these are special funcs since they get their own private data - the uart :)
	if(!writeF) writeF = pxa255uartPrvDefaultWrite;
	
	uart->readF = readF;
	uart->writeF = writeF;
	uart->accessFuncsData = userData;
}

Boolean pxa255uartInit(Pxa255uart* uart, ArmMem* physMem, Pxa255ic* ic, UInt32 baseAddr, UInt8 irq){
	
	__mem_zero(uart, sizeof(Pxa255uart));
	uart->ic = ic;
	uart->irq = irq;
	uart->baseAddr = baseAddr;
	uart->IIR = UART_IIR_NOINT;
	uart->IER = UART_IER_UUE | UART_IER_NRZE; 		//uart on
	uart->LSR = UART_LSR_TEMT | UART_LSR_TDRQ;
	uart->MSR = UART_MSR_CTS;
	pxa255uartPrvFifoFlush(&uart->TX);
	pxa255uartPrvFifoFlush(&uart->RX);
	
	
	pxa255uartSetFuncs(uart, NULL, NULL, NULL);
	
	return memRegionAdd(physMem, baseAddr, PXA255_UART_SIZE, pxa255uartPrvMemAccessF, uart);
}

void pxa255uartProcess(Pxa255uart* uart){		//send and rceive up to one character
	
	UInt8 t;
	UInt16 v;
	
	//first process sending (if any)
	if(!(uart->LSR & UART_LSR_TEMT)){
		
		pxa255uartPrvPutchar(uart, uart->transmitShift);
		
		if(uart->FCR & UART_FCR_TRFIFOE){	//fifo mode
			
			t = pxa255uartPrvFifoUsed(&uart->TX);
			
			if(t--){
				
				uart->transmitShift = pxa255uartPrvFifoGet(&uart->TX);
				if(t <= UART_FIFO_DEPTH / 2) uart->LSR |= UART_LSR_TDRQ;	//above half full - clear TDRQ bit
			}
			else{
				
				uart->LSR |= UART_LSR_TEMT;
			}
		}
		else if (uart->LSR & UART_LSR_TDRQ){
			
			uart->LSR |= UART_LSR_TEMT;
		}
		else{
			
			uart->transmitShift = uart->transmitHolding;
			uart->LSR |= UART_LSR_TDRQ;
		}
	}
	
	//now process receiving
	v = pxa255uartPrvGetchar(uart);
	if(v != UART_CHAR_NONE){
		
		uart->cyclesSinceRecv = 0;
		uart->LSR |= UART_LSR_DR;
		
		if(uart->FCR & UART_FCR_TRFIFOE){	//fifo mode
		
			if(!pxa255uartPrvFifoPut(&uart->RX, v)){
				
				uart->LSR |= UART_LSR_OE;	
			}
		}
		else{
			
			if(uart->LSR & UART_LSR_DR) uart->LSR |= UART_LSR_OE;
			else uart->receiveHolding = v;
		}
	}
	else if(uart->cyclesSinceRecv <= 4){
		uart->cyclesSinceRecv++;
	}
	
	pxa255uartPrvRecalc(uart);
}

static void pxa255uartPrvRecalcCharBits(Pxa255uart* uart, UInt16 c){
	
	if(c & UART_CHAR_BREAK) uart->LSR |= UART_LSR_BI;
	if(c & UART_CHAR_FRAME_ERR) uart->LSR |= UART_LSR_FE;
	if(c & UART_CHAR_PAR_ERR) uart->LSR |= UART_LSR_PE;
}

static void pxa255uartPrvRecalc(Pxa255uart* uart){
	
	Boolean errorSet = false;
	UInt8 v;
	
	uart->LSR &=~ UART_LSR_FIFOE;
	uart->IIR &= UART_IIR_FIFOES;	//clear all other bits... 
	uart->LSR &=~ (UART_LSR_BI | UART_LSR_FE | UART_LSR_PE);
	
	if(uart->FCR & UART_FCR_TRFIFOE){	//fifo mode
		
		
		//check rx fifo for errors
		for(v = 0; v < pxa255uartPrvFifoUsed(&uart->RX); v++){
			
			if((pxa255uartPrvFifoPeekNth(&uart->RX, v) >> 8) && (uart->IER & UART_IER_RLSE)){
				
				uart->LSR |= UART_LSR_FIFOE;
				uart->IIR |= UART_IIR_RECV_ERR;
				errorSet = true;
				break;
			}
		}
		
		v = pxa255uartPrvFifoUsed(&uart->RX);
		if(v){
			pxa255uartPrvRecalcCharBits(uart, pxa255uartPrvFifoPeek(&uart->RX));
		}
		
		switch(uart->FCR & UART_FCR_ITL_MASK){
			
			case UART_FCR_ITL_1:
				v = v >= 1;
				break;
			
			case UART_FCR_ITL_8:
				v = v >= 8;
				break;
			
			case UART_FCR_ITL_16:
				v = v >= 16;
				break;
			
			case UART_FCR_ITL_32:
				v = v >= 32;
				break;
		}
		if(v && (uart->IER & UART_IER_RAVIE) && !errorSet){
			
			errorSet = true;
			uart->IIR |= UART_IIR_RECV_DATA;
		}
		if(pxa255uartPrvFifoUsed(&uart->RX) && uart->cyclesSinceRecv >= 4 && (uart->IER & UART_IER_RAVIE) && !errorSet){
			
			errorSet = true;
			uart->IIR |= UART_IIR_RCV_TIMEOUT;	
		}
	}
	else{		//polling mode
		
		UInt16 c = uart->receiveHolding;
		
		if(uart->LSR & UART_LSR_DR){
			
			pxa255uartPrvRecalcCharBits(uart, c);
			
			if((c >> 8) && !errorSet && (uart->IER & UART_IER_RLSE)){
				
				uart->IIR |= UART_IIR_RECV_ERR;
				errorSet = true;
			}
			else if(!errorSet && (uart->IER & UART_IER_RAVIE)){
				
				uart->IIR |= UART_IIR_RECV_DATA;
				errorSet = true;
			}
		}
	}
	
	if(uart->LSR & UART_LSR_TDRQ && !errorSet && (uart->IER & UART_IER_TIE)){
			
		errorSet = true;
		uart->IIR |= UART_IIR_SEND_DATA;
	}
	
	if(!errorSet) uart->IIR |= UART_IIR_NOINT;
	pxa255uartPrvIrq(uart, errorSet);
}
