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
|