[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] Interrupt-driven serial transmit for 8250/16550 UARTs.
ChangeSet 1.1670, 2005/06/04 18:35:19+01:00, kaf24@xxxxxxxxxxxxxxxxxxxx Interrupt-driven serial transmit for 8250/16550 UARTs. Signed-off-by: Keir Fraser <keir@xxxxxxxxxxxxx> arch/x86/cdb.c | 5 + arch/x86/domain.c | 4 + arch/x86/traps.c | 1 drivers/char/console.c | 10 +++ drivers/char/ns16550.c | 32 ++++++++---- drivers/char/serial.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++-- include/xen/console.h | 3 + include/xen/serial.h | 37 +++++++++++--- 8 files changed, 196 insertions(+), 22 deletions(-) diff -Nru a/xen/arch/x86/cdb.c b/xen/arch/x86/cdb.c --- a/xen/arch/x86/cdb.c 2005-06-04 14:02:05 -04:00 +++ b/xen/arch/x86/cdb.c 2005-06-04 14:02:05 -04:00 @@ -358,6 +358,7 @@ local_irq_save(flags); watchdog_disable(); + console_start_sync(); /* Shouldn't really do this, but otherwise we stop for no obvious reason, which is Bad */ @@ -383,9 +384,13 @@ ASSERT(!local_irq_is_enabled()); } } + + console_end_sync(); watchdog_enable(); atomic_inc(&xendbg_running); + local_irq_restore(flags); + return 0; } diff -Nru a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c --- a/xen/arch/x86/domain.c 2005-06-04 14:02:06 -04:00 +++ b/xen/arch/x86/domain.c 2005-06-04 14:02:06 -04:00 @@ -123,6 +123,9 @@ safe_halt(); } + watchdog_disable(); + console_start_sync(); + local_irq_enable(); /* Ensure we are the boot CPU. */ @@ -174,6 +177,7 @@ void machine_halt(void) { watchdog_disable(); + console_start_sync(); smp_call_function(__machine_halt, NULL, 1, 0); __machine_halt(NULL); } diff -Nru a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c --- a/xen/arch/x86/traps.c 2005-06-04 14:02:05 -04:00 +++ b/xen/arch/x86/traps.c 2005-06-04 14:02:05 -04:00 @@ -205,6 +205,7 @@ }; watchdog_disable(); + console_start_sync(); show_registers(regs); diff -Nru a/xen/drivers/char/console.c b/xen/drivers/char/console.c --- a/xen/drivers/char/console.c 2005-06-04 14:02:05 -04:00 +++ b/xen/drivers/char/console.c 2005-06-04 14:02:05 -04:00 @@ -467,6 +467,16 @@ spin_lock(&console_lock); } +void console_start_sync(void) +{ + serial_start_sync(sercon_handle); +} + +void console_end_sync(void) +{ + serial_end_sync(sercon_handle); +} + void console_putc(char c) { serial_putc(sercon_handle, c); diff -Nru a/xen/drivers/char/ns16550.c b/xen/drivers/char/ns16550.c --- a/xen/drivers/char/ns16550.c 2005-06-04 14:02:06 -04:00 +++ b/xen/drivers/char/ns16550.c 2005-06-04 14:02:06 -04:00 @@ -101,16 +101,24 @@ static void ns16550_interrupt( int irq, void *dev_id, struct cpu_user_regs *regs) { - serial_rx_interrupt(dev_id, regs); + struct serial_port *port = dev_id; + struct ns16550 *uart = port->uart; + + if ( (ns_read_reg(uart, IIR) & 7) == 2 ) + serial_tx_interrupt(port, regs); + else + serial_rx_interrupt(port, regs); } -static void ns16550_putc(struct serial_port *port, char c) +static int ns16550_tx_empty(struct serial_port *port) { struct ns16550 *uart = port->uart; + return !!(ns_read_reg(uart, LSR) & LSR_THRE); +} - while ( !(ns_read_reg(uart, LSR) & LSR_THRE) ) - cpu_relax(); - +static void ns16550_putc(struct serial_port *port, char c) +{ + struct ns16550 *uart = port->uart; ns_write_reg(uart, THR, c); } @@ -150,6 +158,10 @@ /* Enable and clear the FIFOs. Set a large trigger threshold. */ ns_write_reg(uart, FCR, FCR_ENABLE | FCR_CLRX | FCR_CLTX | FCR_TRG14); + + /* Check this really is a 16550+. Otherwise we have no FIFOs. */ + if ( (ns_read_reg(uart, IIR) & 0xc0) == 0xc0 ) + port->tx_fifo_size = 16; } static void ns16550_init_postirq(struct serial_port *port) @@ -157,20 +169,19 @@ struct ns16550 *uart = port->uart; int rc; + serial_async_transmit(port); + uart->irqaction.handler = ns16550_interrupt; uart->irqaction.name = "ns16550"; uart->irqaction.dev_id = port; if ( (rc = setup_irq(uart->irq, &uart->irqaction)) != 0 ) printk("ERROR: Failed to allocate na16550 IRQ %d\n", uart->irq); - /* For sanity, clear the receive FIFO. */ - ns_write_reg(uart, FCR, FCR_ENABLE | FCR_CLRX | FCR_TRG14); - /* Master interrupt enable; also keep DTR/RTS asserted. */ ns_write_reg(uart, MCR, MCR_OUT2 | MCR_DTR | MCR_RTS); - /* Enable receive interrupts. */ - ns_write_reg(uart, IER, IER_ERDAI); + /* Enable receive and transmit interrupts. */ + ns_write_reg(uart, IER, IER_ERDAI | IER_ETHREI); } #ifdef CONFIG_X86 @@ -188,6 +199,7 @@ .init_preirq = ns16550_init_preirq, .init_postirq = ns16550_init_postirq, .endboot = ns16550_endboot, + .tx_empty = ns16550_tx_empty, .putc = ns16550_putc, .getc = ns16550_getc }; diff -Nru a/xen/drivers/char/serial.c b/xen/drivers/char/serial.c --- a/xen/drivers/char/serial.c 2005-06-04 14:02:06 -04:00 +++ b/xen/drivers/char/serial.c 2005-06-04 14:02:06 -04:00 @@ -42,8 +42,8 @@ fn = port->rx_hi; else if ( !(c & 0x80) && (port->rx_lo != NULL) ) fn = port->rx_lo; - else if ( (port->rxbufp - port->rxbufc) != RXBUFSZ ) - port->rxbuf[MASK_RXBUF_IDX(port->rxbufp++)] = c; + else if ( (port->rxbufp - port->rxbufc) != SERIAL_RXBUFSZ ) + port->rxbuf[MASK_SERIAL_RXBUF_IDX(port->rxbufp++)] = c; spin_unlock_irqrestore(&port->lock, flags); @@ -56,6 +56,71 @@ spin_unlock_irqrestore(&port->lock, flags); } +void serial_tx_interrupt(struct serial_port *port, struct cpu_user_regs *regs) +{ + int i; + unsigned long flags; + + BUG_ON(!port->driver); + BUG_ON(!port->driver->tx_empty); + BUG_ON(!port->driver->putc); + + spin_lock_irqsave(&port->lock, flags); + + for ( i = 0; i < port->tx_fifo_size; i++ ) + { + if ( port->txbufc == port->txbufp ) + break; + port->driver->putc( + port, port->txbuf[MASK_SERIAL_TXBUF_IDX(port->txbufc++)]); + } + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void __serial_putc(struct serial_port *port, char c) +{ + int i; + + if ( (port->txbuf != NULL) && !port->sync ) + { + /* Interrupt-driven (asynchronous) transmitter. */ + if ( (port->txbufp - port->txbufc) == SERIAL_TXBUFSZ ) + { + /* Buffer is full: we spin, but could alternatively drop chars. */ + while ( !port->driver->tx_empty(port) ) + cpu_relax(); + for ( i = 0; i < port->tx_fifo_size; i++ ) + port->driver->putc( + port, port->txbuf[MASK_SERIAL_TXBUF_IDX(port->txbufc++)]); + port->txbuf[MASK_SERIAL_TXBUF_IDX(port->txbufp++)] = c; + } + else if ( ((port->txbufp - port->txbufc) == 0) && + port->driver->tx_empty(port) ) + { + /* Buffer and UART FIFO are both empty. */ + port->driver->putc(port, c); + } + else + { + /* Normal case: buffer the character. */ + port->txbuf[MASK_SERIAL_TXBUF_IDX(port->txbufp++)] = c; + } + } + else if ( port->driver->tx_empty ) + { + /* Synchronous finite-capacity transmitter. */ + while ( !port->driver->tx_empty(port) ) + cpu_relax(); + port->driver->putc(port, c); + } + else + { + /* Simple synchronous transmitter. */ + port->driver->putc(port, c); + } +} + void serial_putc(int handle, char c) { struct serial_port *port = &com[handle & SERHND_IDX]; @@ -67,14 +132,14 @@ spin_lock_irqsave(&port->lock, flags); if ( (c == '\n') && (handle & SERHND_COOKED) ) - port->driver->putc(port, '\r'); + __serial_putc(port, '\r'); if ( handle & SERHND_HI ) c |= 0x80; else if ( handle & SERHND_LO ) _______________________________________________ Xen-changelog mailing list Xen-changelog@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-changelog
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |