|
Message
From: György 'nog' Jeney<nog@s...>
Date: Sun May 29 17:51:57 CEST 2005
Subject: [openrisc] [or1ksim #98] UART: Move TX logic out of uart_clock16
[0/6]
Hi,This is the first of 7 patches that have the ultimate effect of removeing the hughe uart_clock16 function. I have ensured that the acv_uart test passes after each patch so hopefully there shouldn't be any regresions.
ChangeLog: * Move TX logic out of uart_clock16.
nog. -------------- next part -------------- --- /hd0/or1k/or1k/or1ksim/peripheral/16450.c 2005-05-29 11:54:30.000000000 +0200 +++ peripheral/16450.c 2005-05-29 14:16:38.000000000 +0200 @@ -52,6 +52,8 @@ DEFAULT_DEBUG_CHANNEL(uart); #define MIN(a,b) ((a) < (b) ? (a) : (b)) +void uart_tx_send(void *dat); + /* Number of clock cycles (one clock cycle is one call to the uart_clock()) before a single character is transmitted or received. */ static unsigned long char_clks(int dll, int dlh, int lcr) @@ -76,6 +78,148 @@ static unsigned long char_clks(int dll, return (char_clks * bauds_per_char) >> 1; } +/*----------------------------------------------------[ Transmitter logic ]---*/ +/* Sends the data in the shift register to the outside world */ +static void send_char (struct dev_16450 *uart, int bits_send) +{ + PRINTF ("%c", (char)uart->iregs.txser); + TRACE("TX \'%c\' via UART at %"PRIxADDR"\n", (char)uart->iregs.txser, + uart->baseaddr); + if (uart->regs.mcr & UART_MCR_LOOP) + uart->iregs.loopback = uart->iregs.txser; + else { + /* Send to either VAPI or to file */ + if (uart->vapi_id) { + int par, pe, fe, nbits; + int j, data; + unsigned long packet = 0; + + nbits = MIN (bits_send, (uart->regs.lcr & UART_LCR_WLEN8) + 5); + /* Encode a packet */ + packet = uart->iregs.txser & ((1 << nbits) - 1); + + /* Calculate parity */ + for (j = 0, par = 0; j < nbits; j++) + par ^= (packet >> j) & 1; + + if (uart->regs.lcr & UART_LCR_PARITY) { + if (uart->regs.lcr & UART_LCR_SPAR) { + packet |= 1 << nbits; + } else { + if (uart->regs.lcr & UART_LCR_EPAR) + packet |= par << nbits; + else + packet |= (par ^ 1) << nbits; + } + nbits++; + } + packet |= 1 << (nbits++); + if (uart->regs.lcr & UART_LCR_STOP) + packet |= 1 << (nbits++); + + /* Decode a packet */ + nbits = (uart->vapi.lcr & UART_LCR_WLEN8) + 5; + data = packet & ((1 << nbits) - 1); + + /* Calculate parity, including parity bit */ + for (j = 0, par = 0; j < nbits + 1; j++) + par ^= (packet >> j) & 1; + + if (uart->vapi.lcr & UART_LCR_PARITY) { + if (uart->vapi.lcr & UART_LCR_SPAR) { + pe = !((packet >> nbits) & 1); + } else { + if (uart->vapi.lcr & UART_LCR_EPAR) + pe = par != 0; + else + pe = par != 1; + } + nbits++; + } else + pe = 0; + + fe = ((packet >> (nbits++)) & 1) ^ 1; + if (uart->vapi.lcr & UART_LCR_STOP) + fe |= ((packet >> (nbits++)) & 1) ^ 1; + + TRACE ("lcr vapi %02x, uart %02x\n", uart->vapi.lcr, uart->regs.lcr); + data |= (uart->vapi.lcr << 8) | (pe << 16) | (fe << 17) | (uart->vapi.lcr << 8); + TRACE ("vapi_send (%08lx, %08x)\n", uart->vapi_id, data); + vapi_send (uart->vapi_id, data); + } else { + char buffer[1] = { uart->iregs.txser & 0xFF }; + channel_write(uart->channel, buffer, 1); + } + } +} + +/* Called when all the bits have been shifted out of the shift register */ +void uart_char_clock(void *dat)
+{
+ struct dev_16450 *uart = dat;
+
+ TRACE("Sending data in shift reg: 0x%02lx\n", uart->iregs.txser);
+ /* We've sent all bits */
+ send_char(uart, (uart->regs.lcr & UART_LCR_WLEN8) + 5);
+
+ if(!uart->istat.txbuf_full)
+ uart->regs.lsr |= UART_LSR_TXSERE;
+ else
+ uart_tx_send(uart);
+}
+
+/* Called when a break has been shifted out of the shift register */
+void uart_send_break(void *dat)
+{
+ struct dev_16450 *uart = dat;
+
+ TRACE("Sending break\n");
+#if 0
+ /* Send broken frame */
+ int nbits_sent = ((uart->regs.lcr & UART_LCR_WLEN8) + 5) * (uart->istat.txser_clks - 1) / uart->char_clks;
+ send_char(i, nbits_sent);
+#endif
+ /* Send one break signal */
+ vapi_send (uart->vapi_id, UART_LCR_SBC << 8);
+
+ /* Send the next char (if there is one) */
+ if(!uart->istat.txbuf_full)
+ uart->regs.lsr |= UART_LSR_TXSERE;
+ else
+ uart_tx_send(uart);
+}
+
+/* Scheduled whenever the TX buffer has characters in it and we aren't sending
+ * a character. */
+void uart_tx_send(void *dat)
+{
+ struct dev_16450 *uart = dat;
+
+ uart->iregs.txser = uart->regs.txbuf[uart->istat.txbuf_tail];
+ uart->istat.txbuf_tail = (uart->istat.txbuf_tail + 1) % uart->fifo_len;
+ uart->istat.txbuf_full--;
+ uart->regs.lsr &= ~UART_LSR_TXSERE;
+
+ TRACE("Moveing head of TX fifo (fill: %i) to shift reg 0x%02lx\n",
+ uart->istat.txbuf_full, uart->iregs.txser);
+
+ /* Schedules a char_clock to run in the correct amount of time */
+ if(!(uart->regs.lcr & UART_LCR_SBC)) {
+ SCHED_ADD(uart_char_clock, uart, uart->char_clks * UART_CLOCK_DIVIDER);
+ } else {
+ TRACE("Sending break not char\n");
+ SCHED_ADD(uart_send_break, uart, 0);
+ }
+
+ /* When UART is in either character mode, i.e. 16450 emulation mode, or FIFO
+ * mode, the THRE interrupt is raised when THR transitions from full to empty.
+ */
+ if (!uart->istat.txbuf_full) {
+ uart->regs.lsr |= UART_LSR_TXBUFE;
+ uart->istat.thre_int = 1;
+ }
+}
+
/* Set a specific UART register with value. */
void uart_write_byte(oraddr_t addr, uint8_t value, void *dat)
{
@@ -99,14 +243,15 @@ void uart_write_byte(oraddr_t addr, uint
switch (addr) {
case UART_TXBUF:
+ uart->regs.lsr &= ~UART_LSR_TXBUFE;
if (uart->istat.txbuf_full < uart->fifo_len) {
- uart->istat.txbuf_full++;
uart->regs.txbuf[uart->istat.txbuf_head] = value;
uart->istat.txbuf_head = (uart->istat.txbuf_head + 1) % uart->fifo_len;
+ if(!uart->istat.txbuf_full++ && (uart->regs.lsr & UART_LSR_TXSERE))
+ SCHED_ADD(uart_tx_send, uart, 0);
} else
uart->regs.txbuf[uart->istat.txbuf_head] = value;
- uart->regs.lsr &= ~(UART_LSR_TXSERE | UART_LSR_TXBUFE);
if (uart->regs.iir & UART_IIR_THRI)
uart->istat.thre_int = 0;
break;
@@ -123,6 +268,8 @@ void uart_write_byte(oraddr_t addr, uint
// For FIFO-mode only, THRE interrupt is set when THR and FIFO are empty
uart->istat.thre_int = (uart->fifo_len == 16);
+
+ SCHED_FIND_REMOVE(uart_tx_send, uart);
}
if (value & UART_FCR_RRXFI) {
uart->istat.rxbuf_head = uart->istat.rxbuf_tail = 0;
@@ -134,6 +281,19 @@ void uart_write_byte(oraddr_t addr, uint
uart->regs.ier = value & UART_VALID_IER;
break;
case UART_LCR:
+ if((uart->regs.lcr & UART_LCR_SBC) != (value & UART_LCR_SBC)) {
+ if((value & UART_LCR_SBC) && !(uart->regs.lsr & UART_LSR_TXSERE)) {
+ /* Schedule a job to send the break char */
+ SCHED_FIND_REMOVE(uart_char_clock, uart);
+ SCHED_ADD(uart_send_break, uart, 0);
+ }
+ if(!(value & UART_LCR_SBC) && !(uart->regs.lsr & UART_LSR_TXSERE)) {
+ /* Schedule a job to start sending characters */
+ SCHED_ADD(uart_tx_send, uart, 0);
+ /* Remove the uart_send_break job just in case it has not run yet */
+ SCHED_FIND_REMOVE(uart_char_clock, uart);
+ }
+ }
uart->regs.lcr = value & UART_VALID_LCR;
uart->char_clks = char_clks(uart->regs.dll, uart->regs.dlh, uart->regs.lcr);
break;
@@ -239,82 +399,6 @@ void uart_vapi_read (unsigned long id, u
}
}
-static void send_char (struct dev_16450 *uart, int bits_send)
-{
- PRINTF ("%c", (char)uart->iregs.txser);
- TRACE("TX \'%c\' via UART at %"PRIxADDR"...\n", (char)uart->iregs.txser,
- uart->baseaddr);
- if (uart->regs.mcr & UART_MCR_LOOP)
- uart->iregs.loopback = uart->iregs.txser;
- else {
- /* Send to either VAPI or to file */
- if (uart->vapi_id) {
- int par, pe, fe, nbits;
- int j, data;
- unsigned long packet = 0;
-
- nbits = MIN (bits_send, (uart->regs.lcr & UART_LCR_WLEN8) + 5);
- /* Encode a packet */
- packet = uart->iregs.txser & ((1 << nbits) - 1);
-
- /* Calculate parity */
- for (j = 0, par = 0; j < nbits; j++)
- par ^= (packet >> j) & 1;
-
- if (uart->regs.lcr & UART_LCR_PARITY) {
- if (uart->regs.lcr & UART_LCR_SPAR) {
- packet |= 1 << nbits;
- } else {
- if (uart->regs.lcr & UART_LCR_EPAR)
- packet |= par << nbits;
- else
- packet |= (par ^ 1) << nbits;
- }
- nbits++;
- }
- packet |= 1 << (nbits++);
- if (uart->regs.lcr & UART_LCR_STOP)
- packet |= 1 << (nbits++);
-
- /* Decode a packet */
- nbits = (uart->vapi.lcr & UART_LCR_WLEN8) + 5;
- data = packet & ((1 << nbits) - 1);
-
- /* Calculate parity, including parity bit */
- for (j = 0, par = 0; j < nbits + 1; j++)
- par ^= (packet >> j) & 1;
-
- if (uart->vapi.lcr & UART_LCR_PARITY) {
- if (uart->vapi.lcr & UART_LCR_SPAR) {
- pe = !((packet >> nbits) & 1);
- } else {
- if (uart->vapi.lcr & UART_LCR_EPAR)
- pe = par != 0;
- else
- pe = par != 1;
- }
- nbits++;
- } else
- pe = 0;
-
- fe = ((packet >> (nbits++)) & 1) ^ 1;
- if (uart->vapi.lcr & UART_LCR_STOP)
- fe |= ((packet >> (nbits++)) & 1) ^ 1;
-
- TRACE ("lcr vapi %02x, uart %02x\n", uart->vapi.lcr, uart->regs.lcr);
- data |= (uart->vapi.lcr << 8) | (pe << 16) | (fe << 17) | (uart->vapi.lcr << 8);
- PRINTF ("vapi_send (%08lx, %08x)\n", uart->vapi_id, data);
- TRACE ("vapi_send (%08lx, %08x)\n", uart->vapi_id, data);
- vapi_send (uart->vapi_id, data);
- } else {
- char buffer[1] = { uart->iregs.txser & 0xFF };
- channel_write(uart->channel, buffer, 1);
- }
- }
- uart->istat.txser_full = 0;
- uart->istat.txser_clks = 0;
-}
-
/* Adds a character to the FIFO */
void uart_add_char (struct dev_16450 *uart, int ch)
@@ -356,53 +440,6 @@ void uart_clock16 (void *dat)
uart->istat.break_set = 0;
}
- /***************** Transmit *****************/
- TRACE("\tuart->istat.txser_full = %i\n", uart->istat.txser_full);
- TRACE("\tuart->istat.txbuf_full = %i\n", uart->istat.txser_full);
- TRACE("\tuart->char_clks = %li\n", uart->char_clks);
- if (!uart->istat.txser_full) {
-// uart->regs.lsr |= UART_LSR_TXBUFE;
- if (uart->istat.txbuf_full) {
- uart->iregs.txser = uart->regs.txbuf[uart->istat.txbuf_tail];
- uart->istat.txbuf_tail = (uart->istat.txbuf_tail + 1) % uart->fifo_len;
- uart->istat.txser_full = 1;
- uart->istat.txbuf_full--;
- uart->regs.lsr &= ~UART_LSR_TXSERE;
-
- // When UART is in either character mode, i.e. 16450 emulation mode, or FIFO mode,
- // the THRE interrupt is raised when THR transitions from full to empty.
- if (!uart->istat.txbuf_full) {
- uart->istat.thre_int = 1;
- uart->regs.lsr |= UART_LSR_TXBUFE;
- }
- } else {
- uart->regs.lsr |= UART_LSR_TXSERE;
- }
- } else if (uart->char_clks <= uart->istat.txser_clks++) {
- send_char(uart, (uart->regs.lcr & UART_LCR_WLEN8) + 5); /* We've sent all bits */
- } else {
- /* We are still sending char here*/
-
- /* Check if we set the break bit */
- if (uart->regs.lcr & UART_LCR_SBC) {
- if (!uart->vapi.break_sent) {
-#if 0
- /* Send broken frame */
- int nbits_sent = ((uart->regs.lcr & UART_LCR_WLEN8) + 5) * (uart->istat.txser_clks - 1) / uart->char_clks;
- send_char(i, nbits_sent);
-#endif
- /* Send one break signal */
- vapi_send (uart->vapi_id, UART_LCR_SBC << 8);
- uart->vapi.break_sent = 1;
- }
- /* mark as character was sent */
- uart->istat.txser_full = 0;
- uart->istat.txser_clks = 0;
- } else
- uart->vapi.break_sent = 0;
-
- }
-
/***************** Receive *****************/
/* Is there a break? */
@@ -603,10 +640,10 @@ void uart_reset(void *dat)
uart->istat.rxbuf_head = uart->istat.rxbuf_tail = 0;
uart->istat.txbuf_head = uart->istat.txbuf_tail = 0;
- uart->istat.txser_full = uart->istat.rxser_full = 0;
+ uart->istat.rxser_full = 0;
uart->istat.txbuf_full = uart->istat.rxbuf_full = 0;
- uart->istat.txser_clks = uart->istat.rxser_clks = 0;
+ uart->istat.rxser_clks = 0;
uart->istat.thre_int = 0;
uart->istat.break_set = 0;
@@ -632,13 +669,12 @@ void uart_reset(void *dat)
uart->regs.fcr = 0;
uart->regs.lcr = UART_LCR_RESET;
uart->regs.mcr = 0;
- uart->regs.lsr = 0;
+ uart->regs.lsr = UART_LSR_TXBUFE | UART_LSR_TXSERE;
uart->regs.msr = 0;
uart->regs.scr = 0;
uart->vapi.cur_break = uart->vapi.cur_break_cnt = uart->vapi.next_break = 0;
uart->vapi.next_break_cnt = -1;
- uart->vapi.break_sent = 0;
uart->vapi.skew = 0;
uart->vapi.lcr = 0;
uart->vapi.dll = 0;
@@ -676,9 +712,8 @@ void uart_status(void *dat)
PRINTF("\nInternal status (sim debug):\n");
PRINTF("char_clks: %ld\n", uart->char_clks);
- PRINTF("rxser_clks: %ld txser_clks: %ld\n", uart->istat.rxser_clks,
- uart->istat.txser_clks);
- PRINTF("rxser: %d txser: %d\n", uart->istat.rxser_full, uart->istat.txser_full);
+ PRINTF("rxser_clks: %ld\n", uart->istat.rxser_clks);
+ PRINTF("rxser: %d\n", uart->istat.rxser_full);
PRINTF("rxbuf_full: %d txbuf_full: %d\n", uart->istat.rxbuf_full, uart->istat.txbuf_full);
PRINTF("Using IRQ%i\n", uart->irq);
if (uart->vapi_id)
--- /hd0/or1k/or1k/or1ksim/peripheral/16450.h 2005-03-31 16:39:43.000000000 +0200
+++ peripheral/16450.h 2005-05-29 13:21:49.000000000 +0200
@@ -57,13 +57,11 @@ struct dev_16450 {
int txbuf_tail;
int rxbuf_head;
int rxbuf_tail;
- unsigned int txser_full;
unsigned int rxser_full;
unsigned int txbuf_full;
unsigned int rxbuf_full;
unsigned thre_int;
unsigned break_set;
- unsigned long txser_clks;
unsigned long rxser_clks;
unsigned timeout_count;
} istat; /* Internal status */
@@ -81,7 +79,6 @@ struct dev_16450 {
int next_break_cnt;
int cur_break;
int cur_break_cnt;
- int break_sent;
} vapi;
/* Required by VAPI - circular buffer */
|
 |