kolibrios-gitea/contrib/other/uarm/pxa255_UART.c

589 lines
15 KiB
C
Raw Normal View History

#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);
}