URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
Compare Revisions
- This comparison shows the changes necessary to convert path
/
- from Rev 1501 to Rev 1502
- ↔ Reverse comparison
Rev 1501 → Rev 1502
/trunk/or1ksim/peripheral/16450.c
58,6 → 58,7
void uart_check_char(void *dat); |
static void uart_sched_recv_check(struct dev_16450 *uart); |
static void uart_vapi_cmd(void *dat); |
static void uart_clear_int(struct dev_16450 *uart, int intr); |
void uart_tx_send(void *dat); |
|
/* Number of clock cycles (one clock cycle is one call to the uart_clock()) |
84,6 → 85,189
return (char_clks * bauds_per_char) >> 1; |
} |
|
/*---------------------------------------------------[ Interrupt handling ]---*/ |
/* Signals the specified interrupt. If a higher priority interrupt is already |
* pending, do nothing */ |
static void uart_int_msi(void *dat) |
{ |
struct dev_16450 *uart = dat; |
|
uart->istat.ints |= 1 << UART_IIR_MSI; |
|
if(!(uart->regs.ier & UART_IER_MSI)) |
return; |
|
if((uart->regs.iir & UART_IIR_NO_INT) || (uart->regs.iir == UART_IIR_MSI)) { |
TRACE("Raiseing modem status interrupt\n"); |
uart_clear_int(uart, uart->regs.iir); |
|
uart->regs.iir = UART_IIR_MSI; |
SCHED_ADD(uart_int_msi, dat, UART_CLOCK_DIVIDER); |
report_interrupt(uart->irq); |
} |
} |
|
static void uart_int_thri(void *dat) |
{ |
struct dev_16450 *uart = dat; |
|
uart->istat.ints |= 1 << UART_IIR_THRI; |
|
if(!(uart->regs.ier & UART_IER_THRI)) |
return; |
|
if((uart->regs.iir & UART_IIR_NO_INT) || (uart->regs.iir == UART_IIR_MSI) || |
(uart->regs.iir == UART_IIR_THRI)) { |
TRACE("Raiseing transmitter holding register interrupt\n"); |
uart_clear_int(uart, uart->regs.iir); |
|
uart->regs.iir = UART_IIR_THRI; |
SCHED_ADD(uart_int_thri, dat, UART_CLOCK_DIVIDER); |
report_interrupt(uart->irq); |
} |
} |
|
static void uart_int_cti(void *dat) |
{ |
struct dev_16450 *uart = dat; |
|
uart->istat.ints |= 1 << UART_IIR_CTI; |
|
if(!(uart->regs.ier & UART_IER_RDI)) |
return; |
|
if((uart->regs.iir != UART_IIR_RLSI) && (uart->regs.iir != UART_IIR_RDI)) { |
TRACE("Raiseing character timeout interrupt\n"); |
uart_clear_int(uart, uart->regs.iir); |
|
uart->regs.iir = UART_IIR_CTI; |
SCHED_ADD(uart_int_cti, dat, UART_CLOCK_DIVIDER); |
report_interrupt(uart->irq); |
} |
} |
|
static void uart_int_rdi(void *dat) |
{ |
struct dev_16450 *uart = dat; |
|
uart->istat.ints |= 1 << UART_IIR_RDI; |
|
if(!(uart->regs.ier & UART_IER_RDI)) |
return; |
|
if(uart->regs.iir != UART_IIR_RLSI) { |
TRACE("Raiseing receiver data interrupt\n"); |
uart_clear_int(uart, uart->regs.iir); |
|
uart->regs.iir = UART_IIR_RDI; |
SCHED_ADD(uart_int_rdi, dat, UART_CLOCK_DIVIDER); |
report_interrupt(uart->irq); |
} |
} |
|
static void uart_int_rlsi(void *dat) |
{ |
struct dev_16450 *uart = dat; |
|
uart->istat.ints |= 1 << UART_IIR_RLSI; |
|
if(!(uart->regs.ier & UART_IER_RLSI)) |
return; |
|
uart_clear_int(uart, uart->regs.iir); |
|
TRACE("Raiseing receiver line status interrupt\n"); |
|
/* Highest priority interrupt */ |
uart->regs.iir = UART_IIR_RLSI; |
SCHED_ADD(uart_int_rlsi, dat, UART_CLOCK_DIVIDER); |
report_interrupt(uart->irq); |
} |
|
/* Checks to see if an RLSI interrupt is due and schedules one if need be */ |
static void uart_check_rlsi(void *dat) |
{ |
struct dev_16450 *uart = dat; |
|
if(uart->regs.lsr & (UART_LSR_OVRRUN | UART_LSR_PARITY | UART_LSR_FRAME | |
UART_LSR_BREAK)) |
uart_int_rlsi(uart); |
} |
|
/* Checks to see if an RDI interrupt is due and schedules one if need be */ |
static void uart_check_rdi(void *dat) |
{ |
struct dev_16450 *uart = dat; |
|
if(uart->istat.rxbuf_full >= UART_FIFO_TRIGGER(uart->regs.fcr >> 6)) |
uart_int_rdi(uart); |
} |
|
/* Raises the next highest priority interrupt */ |
static void uart_next_int(void *dat) |
{ |
struct dev_16450 *uart = dat; |
|
/* Interrupt detection in proper priority order. */ |
if((uart->istat.ints & (1 << UART_IIR_RLSI)) && |
(uart->regs.ier & UART_IER_RLSI)) |
uart_int_rlsi(uart); |
else if((uart->istat.ints & (1 << UART_IIR_RDI)) && |
(uart->regs.ier & UART_IER_RDI)) |
uart_int_rdi(uart); |
else if((uart->istat.ints & (1 << UART_IIR_CTI)) && |
(uart->regs.ier & UART_IER_RDI)) |
uart_int_cti(uart); |
else if((uart->istat.ints & (1 << UART_IIR_THRI)) && |
(uart->regs.ier & UART_IER_THRI)) |
uart_int_thri(uart); |
else if((uart->istat.ints & (1 << UART_IIR_MSI)) && |
(uart->regs.ier & UART_IER_MSI)) |
uart_int_msi(uart); |
else |
uart->regs.iir = UART_IIR_NO_INT; |
} |
|
/* Clears potentially pending interrupts */ |
static void uart_clear_int(struct dev_16450 *uart, int intr) |
{ |
uart->istat.ints &= ~(1 << intr); |
|
/* Short-circuit most likely case */ |
if(uart->regs.iir == UART_IIR_NO_INT) |
return; |
|
if(intr != uart->regs.iir) |
return; |
|
TRACE("Clearing interrupt 0x%x\n", intr); |
|
uart->regs.iir = UART_IIR_NO_INT; |
|
switch(intr) { |
case UART_IIR_RLSI: |
SCHED_FIND_REMOVE(uart_int_rlsi, uart); |
break; |
case UART_IIR_RDI: |
SCHED_FIND_REMOVE(uart_int_rdi, uart); |
break; |
case UART_IIR_CTI: |
SCHED_FIND_REMOVE(uart_int_cti, uart); |
break; |
case UART_IIR_THRI: |
SCHED_FIND_REMOVE(uart_int_thri, uart); |
break; |
case UART_IIR_MSI: |
SCHED_FIND_REMOVE(uart_int_msi, uart); |
break; |
} |
|
/* Schedule this job as there is no rush to send the next interrupt (the or. |
* code is probably still running with interrupts disabled and this function |
* is called from the uart_{read,write}_byte functions. */ |
SCHED_ADD(uart_next_int, uart, 0); |
} |
|
/*----------------------------------------------------[ Transmitter logic ]---*/ |
/* Sends the data in the shift register to the outside world */ |
static void send_char (struct dev_16450 *uart, int bits_send) |
222,7 → 406,7
*/ |
if (!uart->istat.txbuf_full) { |
uart->regs.lsr |= UART_LSR_TXBUFE; |
uart->istat.thre_int = 1; |
uart_int_thri(uart); |
} |
} |
|
231,10 → 415,14
static void uart_add_char (struct dev_16450 *uart, int ch) |
{ |
uart->regs.lsr |= UART_LSR_RDRDY; |
uart->istat.timeout_count = 0; |
uart_clear_int(uart, UART_IIR_CTI); |
SCHED_FIND_REMOVE(uart_int_cti, uart); |
SCHED_ADD(uart_int_cti, uart, |
uart->char_clks * UART_CHAR_TIMEOUT * UART_CLOCK_DIVIDER); |
|
if (uart->istat.rxbuf_full + 1 > uart->fifo_len) { |
uart->regs.lsr |= UART_LSR_OVRRUN | UART_LSR_RXERR; |
uart_int_rlsi(uart); |
} else { |
TRACE("add %02x\n", ch); |
uart->regs.rxbuf[uart->istat.rxbuf_head] = ch; |
241,8 → 429,10
uart->istat.rxbuf_head = (uart->istat.rxbuf_head + 1) % uart->fifo_len; |
if(!uart->istat.rxbuf_full++) { |
uart->regs.lsr |= ch >> 8; |
uart_check_rlsi(uart); |
} |
} |
uart_check_rdi(uart); |
} |
|
/* Called when a break sequence is about to start. It stops receiveing |
371,8 → 561,7
} else |
uart->regs.txbuf[uart->istat.txbuf_head] = value; |
|
if (uart->regs.iir & UART_IIR_THRI) |
uart->istat.thre_int = 0; |
uart_clear_int(uart, UART_IIR_THRI); |
break; |
case UART_FCR: |
uart->regs.fcr = value & UART_VALID_FCR; |
385,8 → 574,10
uart->istat.txbuf_full = 0; |
uart->regs.lsr |= UART_LSR_TXBUFE; |
|
// For FIFO-mode only, THRE interrupt is set when THR and FIFO are empty |
uart->istat.thre_int = (uart->fifo_len == 16); |
/* For FIFO-mode only, THRE interrupt is set when THR and FIFO are empty |
*/ |
if(uart->fifo_len == 16) |
SCHED_ADD(uart_int_thri, uart, 0); |
|
SCHED_FIND_REMOVE(uart_tx_send, uart); |
} |
394,10 → 585,13
uart->istat.rxbuf_head = uart->istat.rxbuf_tail = 0; |
uart->istat.rxbuf_full = 0; |
uart->regs.lsr &= ~UART_LSR_RDRDY; |
uart_clear_int(uart, UART_IIR_RDI); |
uart_clear_int(uart, UART_IIR_CTI); |
} |
break; |
case UART_IER: |
uart->regs.ier = value & UART_VALID_IER; |
SCHED_ADD(uart_next_int, uart, 0); |
break; |
case UART_LCR: |
if((uart->regs.lcr & UART_LCR_SBC) != (value & UART_LCR_SBC)) { |
465,13 → 659,23
TRACE("Reading %"PRIx8" out of RX FIFO\n", value); |
} else |
TRACE("Trying to read out of RX FIFO but it's empty!\n"); |
|
if(uart->istat.rxbuf_full) |
|
uart_clear_int(uart, UART_IIR_RDI); |
uart_clear_int(uart, UART_IIR_CTI); |
SCHED_FIND_REMOVE(uart_int_cti, uart); |
|
if(uart->istat.rxbuf_full) { |
uart->regs.lsr |= UART_LSR_RDRDY | uart->regs.rxbuf[uart->istat.rxbuf_tail] >> 8; |
else |
SCHED_ADD(uart_int_cti, uart, |
uart->char_clks * UART_CHAR_TIMEOUT * UART_CLOCK_DIVIDER); |
/* Since we're not allowed to raise interrupts from read/write functions |
* schedule them to run just after we have executed this read |
* instruction. */ |
SCHED_ADD(uart_check_rlsi, uart, 0); |
SCHED_ADD(uart_check_rdi, uart, 0); |
} else { |
uart->regs.lsr &= ~UART_LSR_RDRDY; |
|
uart->istat.timeout_count = 0; |
} |
break; |
case UART_IER: |
value = uart->regs.ier & UART_VALID_IER; |
478,8 → 682,9
break; |
case UART_IIR: |
value = (uart->regs.iir & UART_VALID_IIR) | 0xc0; |
if (uart->regs.iir & UART_IIR_THRI) |
uart->istat.thre_int = 0; |
/* Only clear the thri interrupt if it is the one we are repporting */ |
if(uart->regs.iir == UART_IIR_THRI) |
uart_clear_int(uart, UART_IIR_THRI); |
break; |
case UART_LCR: |
value = uart->regs.lcr & UART_VALID_LCR; |
492,10 → 697,13
uart->regs.lsr &= |
~(UART_LSR_OVRRUN | UART_LSR_BREAK | UART_LSR_PARITY |
| UART_LSR_FRAME | UART_LSR_RXERR); |
/* Clear potentially pending RLSI interrupt */ |
uart_clear_int(uart, UART_IIR_RLSI); |
break; |
case UART_MSR: |
value = uart->regs.msr & UART_VALID_MSR; |
uart->regs.msr = 0; |
uart_clear_int(uart, UART_IIR_MSI); |
break; |
case UART_SCR: |
value = uart->regs.scr; |
647,36 → 855,6
uart->regs.msr |= ((uart->regs.mcr & UART_MCR_RTS) << 3); |
uart->regs.msr |= ((uart->regs.mcr & UART_MCR_DTR) << 5); |
} |
|
if (uart->regs.lsr & UART_LSR_RDRDY) |
uart->istat.timeout_count++; |
|
/* Interrupt detection in proper priority order. */ |
uart->regs.iir = UART_IIR_NO_INT; |
if (uart->regs.ier & UART_IER_RLSI && /* Receiver LS */ |
uart->regs.lsr & (UART_LSR_OVRRUN | UART_LSR_PARITY |
| UART_LSR_FRAME | UART_LSR_BREAK)) { |
uart->regs.iir = UART_IIR_RLSI; |
} else if ((uart->regs.ier & UART_IER_RDI) /* RD available */ |
&& (uart->istat.rxbuf_full >= UART_FIFO_TRIGGER(uart->regs.fcr >> 6)) |
&& (uart->regs.lsr & UART_LSR_RDRDY)) { |
uart->regs.iir = UART_IIR_RDI; |
} else if ((uart->regs.ier & UART_IER_RDI) /* timeout */ |
&& (uart->istat.timeout_count >= UART_CHAR_TIMEOUT * uart->char_clks) |
&& (uart->istat.rxbuf_head != uart->istat.rxbuf_tail)) { |
uart->regs.iir = UART_IIR_CTI; |
} else if (uart->regs.ier & UART_IER_THRI && /* Transm. empty */ |
uart->istat.thre_int == 1) { |
uart->regs.iir = UART_IIR_THRI; |
} else if (uart->regs.ier & UART_IER_MSI && /* Modem status */ |
uart->regs.msr & (UART_MSR_DCTS | UART_MSR_DDSR |
| UART_MSR_TERI | UART_MSR_DDCD)) { |
uart->regs.iir = UART_IIR_MSI; |
} |
if (!(uart->regs.iir & UART_IIR_NO_INT)) { |
TRACE("\tuart->regs.iir = %i\t", uart->regs.iir); |
report_interrupt(uart->irq); |
} |
} |
|
/* Reset. It initializes all registers of all UART devices to zero values, |
712,12 → 890,6
|
uart->istat.txbuf_full = uart->istat.rxbuf_full = 0; |
|
uart->istat.thre_int = 0; |
uart->istat.timeout_count = 0; |
|
// For FIFO-mode only, THRE interrupt is set when both THR and FIFO are empty |
uart->istat.thre_int = (uart->fifo_len == 16); |
|
uart->char_clks = 0; |
|
uart->iregs.txser = 0; |
725,6 → 897,7
uart->iregs.loopback = 0; |
uart->istat.receiveing = 0; |
uart->istat.recv_break = 0; |
uart->istat.ints = 0; |
|
memset(uart->regs.txbuf, 0, sizeof(uart->regs.txbuf)); |
memset(uart->regs.rxbuf, 0, sizeof(uart->regs.rxbuf)); |
732,7 → 905,7
uart->regs.dll = 0; |
uart->regs.dlh = 0; |
uart->regs.ier = 0; |
uart->regs.iir = 0; |
uart->regs.iir = UART_IIR_NO_INT; |
uart->regs.fcr = 0; |
uart->regs.lcr = UART_LCR_RESET; |
uart->regs.mcr = 0; |
/trunk/or1ksim/peripheral/16450.h
59,10 → 59,9
int rxbuf_tail; |
unsigned int txbuf_full; |
unsigned int rxbuf_full; |
unsigned thre_int; |
unsigned timeout_count; |
int receiveing; /* Receiveing a char */ |
int recv_break; /* Receiveing a break */ |
int ints; /* Which interrupts are pending */ |
} istat; /* Internal status */ |
|
/* Clocks per char */ |