|
Message
From: György 'nog' Jeney<nog@s...>
Date: Sun May 29 17:53:25 CEST 2005
Subject: [openrisc] [or1ksim #101] UART: Move interrupt handling out of
uart_clock16 [3/6]
Hi,Changelog says it all. ChangeLog: * Move interrupt handling out of uart_clock16.
nog. -------------- next part -------------- --- ../or1ksim-patch/peripheral/16450.c 2005-05-29 15:25:02.000000000 +0200 +++ peripheral/16450.c 2005-05-29 15:36:46.000000000 +0200 @@ -58,6 +58,7 @@ void uart_check_vapi(void *dat); 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,181 @@ static unsigned long char_clks(int dll, 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) { + 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)) { + 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_int_rlsi(uart);
+ else if(uart->istat.ints & (1 << UART_IIR_RDI))
+ uart_int_rdi(uart);
+ else if(uart->istat.ints & (1 << UART_IIR_CTI))
+ uart_int_cti(uart);
+ else if(uart->istat.ints & (1 << UART_IIR_THRI))
+ uart_int_thri(uart);
+ else if(uart->istat.ints & (1 << UART_IIR_MSI))
+ uart_int_msi(uart);
+}
+
+/* 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 +398,7 @@ void uart_tx_send(void *dat)
*/
if (!uart->istat.txbuf_full) {
uart->regs.lsr |= UART_LSR_TXBUFE;
- uart->istat.thre_int = 1;
+ uart_int_thri(uart);
}
}
@@ -231,18 +407,24 @@ void uart_tx_send(void *dat)
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;
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 +553,7 @@ void uart_write_byte(oraddr_t addr, uint
} 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 +566,10 @@ void uart_write_byte(oraddr_t addr, uint
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 +577,13 @@ void uart_write_byte(oraddr_t addr, uint
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,21 +651,32 @@ uint8_t uart_read_byte(oraddr_t addr, vo
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;
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 +689,13 @@ uint8_t uart_read_byte(oraddr_t addr, vo
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 +847,6 @@ void uart_clock16 (void *dat)
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 +882,6 @@ void uart_reset(void *dat)
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 +889,7 @@ void uart_reset(void *dat)
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 +897,7 @@ void uart_reset(void *dat)
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;
--- ../or1ksim-patch/peripheral/16450.h 2005-05-29 15:25:02.000000000 +0200
+++ peripheral/16450.h 2005-05-29 15:27:12.000000000 +0200
@@ -59,10 +59,9 @@ struct dev_16450 {
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 */
|
 |