a03882245a
git-svn-id: svn://kolibrios.org@8327 a494cfbc-eb01-0410-851d-a64ba20cac60
589 lines
15 KiB
C
Executable File
589 lines
15 KiB
C
Executable File
#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);
|
|
}
|