ia64/xen-unstable

changeset 5321:da199897c714

bitkeeper revision 1.1670 (42a1e657B8Qurf6JEQIMURchxp90FA)

Interrupt-driven serial transmit for 8250/16550 UARTs.
Signed-off-by: Keir Fraser <keir@xensource.com>
author kaf24@firebug.cl.cam.ac.uk
date Sat Jun 04 17:35:19 2005 +0000 (2005-06-04)
parents fb827128345c
children dd83337405e2
files xen/arch/x86/cdb.c xen/arch/x86/domain.c xen/arch/x86/traps.c xen/drivers/char/console.c xen/drivers/char/ns16550.c xen/drivers/char/serial.c xen/include/xen/console.h xen/include/xen/serial.h
line diff
     1.1 --- a/xen/arch/x86/cdb.c	Sat Jun 04 15:27:59 2005 +0000
     1.2 +++ b/xen/arch/x86/cdb.c	Sat Jun 04 17:35:19 2005 +0000
     1.3 @@ -358,6 +358,7 @@ int
     1.4  	local_irq_save(flags);
     1.5  
     1.6  	watchdog_disable();
     1.7 +	console_start_sync();
     1.8  
     1.9  	/* Shouldn't really do this, but otherwise we stop for no
    1.10  	   obvious reason, which is Bad */
    1.11 @@ -383,9 +384,13 @@ int
    1.12  			ASSERT(!local_irq_is_enabled());
    1.13  		}
    1.14  	}
    1.15 +
    1.16 +	console_end_sync();
    1.17  	watchdog_enable();
    1.18  	atomic_inc(&xendbg_running);
    1.19 +
    1.20  	local_irq_restore(flags);
    1.21 +
    1.22  	return 0;
    1.23  }
    1.24  
     2.1 --- a/xen/arch/x86/domain.c	Sat Jun 04 15:27:59 2005 +0000
     2.2 +++ b/xen/arch/x86/domain.c	Sat Jun 04 17:35:19 2005 +0000
     2.3 @@ -123,6 +123,9 @@ void machine_restart(char * __unused)
     2.4              safe_halt();
     2.5      }
     2.6  
     2.7 +    watchdog_disable();
     2.8 +    console_start_sync();
     2.9 +
    2.10      local_irq_enable();
    2.11  
    2.12      /* Ensure we are the boot CPU. */
    2.13 @@ -174,6 +177,7 @@ void __attribute__((noreturn)) __machine
    2.14  void machine_halt(void)
    2.15  {
    2.16      watchdog_disable();
    2.17 +    console_start_sync();
    2.18      smp_call_function(__machine_halt, NULL, 1, 0);
    2.19      __machine_halt(NULL);
    2.20  }
     3.1 --- a/xen/arch/x86/traps.c	Sat Jun 04 15:27:59 2005 +0000
     3.2 +++ b/xen/arch/x86/traps.c	Sat Jun 04 17:35:19 2005 +0000
     3.3 @@ -205,6 +205,7 @@ asmlinkage void fatal_trap(int trapnr, s
     3.4      };
     3.5  
     3.6      watchdog_disable();
     3.7 +    console_start_sync();
     3.8  
     3.9      show_registers(regs);
    3.10  
     4.1 --- a/xen/drivers/char/console.c	Sat Jun 04 15:27:59 2005 +0000
     4.2 +++ b/xen/drivers/char/console.c	Sat Jun 04 17:35:19 2005 +0000
     4.3 @@ -467,6 +467,16 @@ void console_force_lock(void)
     4.4      spin_lock(&console_lock);
     4.5  }
     4.6  
     4.7 +void console_start_sync(void)
     4.8 +{
     4.9 +    serial_start_sync(sercon_handle);
    4.10 +}
    4.11 +
    4.12 +void console_end_sync(void)
    4.13 +{
    4.14 +    serial_end_sync(sercon_handle);
    4.15 +}
    4.16 +
    4.17  void console_putc(char c)
    4.18  {
    4.19      serial_putc(sercon_handle, c);
     5.1 --- a/xen/drivers/char/ns16550.c	Sat Jun 04 15:27:59 2005 +0000
     5.2 +++ b/xen/drivers/char/ns16550.c	Sat Jun 04 17:35:19 2005 +0000
     5.3 @@ -101,16 +101,24 @@ static void ns_write_reg(struct ns16550 
     5.4  static void ns16550_interrupt(
     5.5      int irq, void *dev_id, struct cpu_user_regs *regs)
     5.6  {
     5.7 -    serial_rx_interrupt(dev_id, regs);
     5.8 +    struct serial_port *port = dev_id;
     5.9 +    struct ns16550 *uart = port->uart;
    5.10 +
    5.11 +    if ( (ns_read_reg(uart, IIR) & 7) == 2 )
    5.12 +        serial_tx_interrupt(port, regs);
    5.13 +    else
    5.14 +        serial_rx_interrupt(port, regs);
    5.15 +}
    5.16 +
    5.17 +static int ns16550_tx_empty(struct serial_port *port)
    5.18 +{
    5.19 +    struct ns16550 *uart = port->uart;
    5.20 +    return !!(ns_read_reg(uart, LSR) & LSR_THRE);
    5.21  }
    5.22  
    5.23  static void ns16550_putc(struct serial_port *port, char c)
    5.24  {
    5.25      struct ns16550 *uart = port->uart;
    5.26 -
    5.27 -    while ( !(ns_read_reg(uart, LSR) & LSR_THRE) )
    5.28 -        cpu_relax();
    5.29 -
    5.30      ns_write_reg(uart, THR, c);
    5.31  }
    5.32  
    5.33 @@ -150,6 +158,10 @@ static void ns16550_init_preirq(struct s
    5.34  
    5.35      /* Enable and clear the FIFOs. Set a large trigger threshold. */
    5.36      ns_write_reg(uart, FCR, FCR_ENABLE | FCR_CLRX | FCR_CLTX | FCR_TRG14);
    5.37 +
    5.38 +    /* Check this really is a 16550+. Otherwise we have no FIFOs. */
    5.39 +    if ( (ns_read_reg(uart, IIR) & 0xc0) == 0xc0 )
    5.40 +        port->tx_fifo_size = 16;
    5.41  }
    5.42  
    5.43  static void ns16550_init_postirq(struct serial_port *port)
    5.44 @@ -157,20 +169,19 @@ static void ns16550_init_postirq(struct 
    5.45      struct ns16550 *uart = port->uart;
    5.46      int rc;
    5.47  
    5.48 +    serial_async_transmit(port);
    5.49 +
    5.50      uart->irqaction.handler = ns16550_interrupt;
    5.51      uart->irqaction.name    = "ns16550";
    5.52      uart->irqaction.dev_id  = port;
    5.53      if ( (rc = setup_irq(uart->irq, &uart->irqaction)) != 0 )
    5.54          printk("ERROR: Failed to allocate na16550 IRQ %d\n", uart->irq);
    5.55  
    5.56 -    /* For sanity, clear the receive FIFO. */
    5.57 -    ns_write_reg(uart, FCR, FCR_ENABLE | FCR_CLRX | FCR_TRG14);
    5.58 -
    5.59      /* Master interrupt enable; also keep DTR/RTS asserted. */
    5.60      ns_write_reg(uart, MCR, MCR_OUT2 | MCR_DTR | MCR_RTS);
    5.61  
    5.62 -    /* Enable receive interrupts. */
    5.63 -    ns_write_reg(uart, IER, IER_ERDAI);
    5.64 +    /* Enable receive and transmit interrupts. */
    5.65 +    ns_write_reg(uart, IER, IER_ERDAI | IER_ETHREI);
    5.66  }
    5.67  
    5.68  #ifdef CONFIG_X86
    5.69 @@ -188,6 +199,7 @@ static struct uart_driver ns16550_driver
    5.70      .init_preirq  = ns16550_init_preirq,
    5.71      .init_postirq = ns16550_init_postirq,
    5.72      .endboot      = ns16550_endboot,
    5.73 +    .tx_empty     = ns16550_tx_empty,
    5.74      .putc         = ns16550_putc,
    5.75      .getc         = ns16550_getc
    5.76  };
     6.1 --- a/xen/drivers/char/serial.c	Sat Jun 04 15:27:59 2005 +0000
     6.2 +++ b/xen/drivers/char/serial.c	Sat Jun 04 17:35:19 2005 +0000
     6.3 @@ -42,8 +42,8 @@ void serial_rx_interrupt(struct serial_p
     6.4              fn = port->rx_hi;
     6.5          else if ( !(c & 0x80) && (port->rx_lo != NULL) )
     6.6              fn = port->rx_lo;
     6.7 -        else if ( (port->rxbufp - port->rxbufc) != RXBUFSZ )
     6.8 -            port->rxbuf[MASK_RXBUF_IDX(port->rxbufp++)] = c;            
     6.9 +        else if ( (port->rxbufp - port->rxbufc) != SERIAL_RXBUFSZ )
    6.10 +            port->rxbuf[MASK_SERIAL_RXBUF_IDX(port->rxbufp++)] = c;            
    6.11  
    6.12          spin_unlock_irqrestore(&port->lock, flags);
    6.13  
    6.14 @@ -56,6 +56,71 @@ void serial_rx_interrupt(struct serial_p
    6.15      spin_unlock_irqrestore(&port->lock, flags);
    6.16  }
    6.17  
    6.18 +void serial_tx_interrupt(struct serial_port *port, struct cpu_user_regs *regs)
    6.19 +{
    6.20 +    int i;
    6.21 +    unsigned long flags;
    6.22 +
    6.23 +    BUG_ON(!port->driver);
    6.24 +    BUG_ON(!port->driver->tx_empty);
    6.25 +    BUG_ON(!port->driver->putc);
    6.26 +
    6.27 +    spin_lock_irqsave(&port->lock, flags);
    6.28 +
    6.29 +    for ( i = 0; i < port->tx_fifo_size; i++ )
    6.30 +    {
    6.31 +        if ( port->txbufc == port->txbufp )
    6.32 +            break;
    6.33 +        port->driver->putc(
    6.34 +            port, port->txbuf[MASK_SERIAL_TXBUF_IDX(port->txbufc++)]);
    6.35 +    }
    6.36 +
    6.37 +    spin_unlock_irqrestore(&port->lock, flags);
    6.38 +}
    6.39 +
    6.40 +static void __serial_putc(struct serial_port *port, char c)
    6.41 +{
    6.42 +    int i;
    6.43 +
    6.44 +    if ( (port->txbuf != NULL) && !port->sync )
    6.45 +    {
    6.46 +        /* Interrupt-driven (asynchronous) transmitter. */
    6.47 +        if ( (port->txbufp - port->txbufc) == SERIAL_TXBUFSZ )
    6.48 +        {
    6.49 +            /* Buffer is full: we spin, but could alternatively drop chars. */
    6.50 +            while ( !port->driver->tx_empty(port) )
    6.51 +                cpu_relax();
    6.52 +            for ( i = 0; i < port->tx_fifo_size; i++ )
    6.53 +                port->driver->putc(
    6.54 +                    port, port->txbuf[MASK_SERIAL_TXBUF_IDX(port->txbufc++)]);
    6.55 +            port->txbuf[MASK_SERIAL_TXBUF_IDX(port->txbufp++)] = c;
    6.56 +        }
    6.57 +        else if ( ((port->txbufp - port->txbufc) == 0) &&
    6.58 +                  port->driver->tx_empty(port) )
    6.59 +        {
    6.60 +            /* Buffer and UART FIFO are both empty. */
    6.61 +            port->driver->putc(port, c);
    6.62 +        }
    6.63 +        else
    6.64 +        {
    6.65 +            /* Normal case: buffer the character. */
    6.66 +            port->txbuf[MASK_SERIAL_TXBUF_IDX(port->txbufp++)] = c;
    6.67 +        }
    6.68 +    }
    6.69 +    else if ( port->driver->tx_empty )
    6.70 +    {
    6.71 +        /* Synchronous finite-capacity transmitter. */
    6.72 +        while ( !port->driver->tx_empty(port) )
    6.73 +            cpu_relax();
    6.74 +        port->driver->putc(port, c);
    6.75 +    }
    6.76 +    else
    6.77 +    {
    6.78 +        /* Simple synchronous transmitter. */
    6.79 +        port->driver->putc(port, c);
    6.80 +    }
    6.81 +}
    6.82 +
    6.83  void serial_putc(int handle, char c)
    6.84  {
    6.85      struct serial_port *port = &com[handle & SERHND_IDX];
    6.86 @@ -67,14 +132,14 @@ void serial_putc(int handle, char c)
    6.87      spin_lock_irqsave(&port->lock, flags);
    6.88  
    6.89      if ( (c == '\n') && (handle & SERHND_COOKED) )
    6.90 -        port->driver->putc(port, '\r');
    6.91 +        __serial_putc(port, '\r');
    6.92  
    6.93      if ( handle & SERHND_HI )
    6.94          c |= 0x80;
    6.95      else if ( handle & SERHND_LO )
    6.96          c &= 0x7f;
    6.97  
    6.98 -    port->driver->putc(port, c);
    6.99 +    __serial_putc(port, c);
   6.100  
   6.101      spin_unlock_irqrestore(&port->lock, flags);
   6.102  }
   6.103 @@ -101,7 +166,7 @@ char serial_getc(int handle)
   6.104              
   6.105              if ( port->rxbufp != port->rxbufc )
   6.106              {
   6.107 -                c = port->rxbuf[MASK_RXBUF_IDX(port->rxbufc++)];
   6.108 +                c = port->rxbuf[MASK_SERIAL_RXBUF_IDX(port->rxbufc++)];
   6.109                  break;
   6.110              }
   6.111              
   6.112 @@ -201,6 +266,46 @@ void serial_force_unlock(int handle)
   6.113      struct serial_port *port = &com[handle & SERHND_IDX];
   6.114      if ( handle != -1 )
   6.115          port->lock = SPIN_LOCK_UNLOCKED;
   6.116 +    serial_start_sync(handle);
   6.117 +}
   6.118 +
   6.119 +void serial_start_sync(int handle)
   6.120 +{
   6.121 +    struct serial_port *port = &com[handle & SERHND_IDX];
   6.122 +    unsigned long flags;
   6.123 +
   6.124 +    if ( handle == -1 )
   6.125 +        return;
   6.126 +    
   6.127 +    spin_lock_irqsave(&port->lock, flags);
   6.128 +
   6.129 +    if ( port->sync++ == 0 )
   6.130 +    {
   6.131 +        while ( (port->txbufp - port->txbufc) != 0 )
   6.132 +        {
   6.133 +            while ( !port->driver->tx_empty(port) )
   6.134 +                cpu_relax();
   6.135 +            port->driver->putc(
   6.136 +                port, port->txbuf[MASK_SERIAL_TXBUF_IDX(port->txbufc++)]);
   6.137 +        }
   6.138 +    }
   6.139 +
   6.140 +    spin_unlock_irqrestore(&port->lock, flags);
   6.141 +}
   6.142 +
   6.143 +void serial_end_sync(int handle)
   6.144 +{
   6.145 +    struct serial_port *port = &com[handle & SERHND_IDX];
   6.146 +    unsigned long flags;
   6.147 +
   6.148 +    if ( handle == -1 )
   6.149 +        return;
   6.150 +    
   6.151 +    spin_lock_irqsave(&port->lock, flags);
   6.152 +
   6.153 +    port->sync--;
   6.154 +
   6.155 +    spin_unlock_irqrestore(&port->lock, flags);
   6.156  }
   6.157  
   6.158  void serial_init_preirq(void)
   6.159 @@ -229,8 +334,19 @@ void serial_endboot(void)
   6.160  
   6.161  void serial_register_uart(int idx, struct uart_driver *driver, void *uart)
   6.162  {
   6.163 +    /* Store UART-specific info. */
   6.164      com[idx].driver = driver;
   6.165      com[idx].uart   = uart;
   6.166 +
   6.167 +    /* Default is no transmit FIFO. */
   6.168 +    com[idx].tx_fifo_size = 1;
   6.169 +}
   6.170 +
   6.171 +void serial_async_transmit(struct serial_port *port)
   6.172 +{
   6.173 +    BUG_ON(!port->driver->tx_empty);
   6.174 +    if ( !port->txbuf )
   6.175 +        port->txbuf = (char *)alloc_xenheap_pages(get_order(SERIAL_TXBUFSZ));
   6.176  }
   6.177  
   6.178  /*
     7.1 --- a/xen/include/xen/console.h	Sat Jun 04 15:27:59 2005 +0000
     7.2 +++ b/xen/include/xen/console.h	Sat Jun 04 17:35:19 2005 +0000
     7.3 @@ -22,4 +22,7 @@ void console_endboot(int disable_vga);
     7.4  void console_force_unlock(void);
     7.5  void console_force_lock(void);
     7.6  
     7.7 +void console_start_sync(void);
     7.8 +void console_end_sync(void);
     7.9 +
    7.10  #endif /* __CONSOLE_H__ */
     8.1 --- a/xen/include/xen/serial.h	Sat Jun 04 15:27:59 2005 +0000
     8.2 +++ b/xen/include/xen/serial.h	Sat Jun 04 17:35:19 2005 +0000
     8.3 @@ -16,8 +16,12 @@ typedef void (*serial_rx_fn)(char, struc
     8.4  void serial_set_rx_handler(int handle, serial_rx_fn fn);
     8.5  
     8.6  /* Number of characters we buffer for a polling receiver. */
     8.7 -#define RXBUFSZ 32
     8.8 -#define MASK_RXBUF_IDX(_i) ((_i)&(RXBUFSZ-1))
     8.9 +#define SERIAL_RXBUFSZ 32
    8.10 +#define MASK_SERIAL_RXBUF_IDX(_i) ((_i)&(SERIAL_RXBUFSZ-1))
    8.11 +
    8.12 +/* Number of characters we buffer for an interrupt-driven transmitter. */
    8.13 +#define SERIAL_TXBUFSZ 16384
    8.14 +#define MASK_SERIAL_TXBUF_IDX(_i) ((_i)&(SERIAL_TXBUFSZ-1))
    8.15  
    8.16  struct uart_driver;
    8.17  
    8.18 @@ -25,10 +29,17 @@ struct serial_port {
    8.19      /* Uart-driver parameters. */
    8.20      struct uart_driver *driver;
    8.21      void               *uart;
    8.22 +    /* Number of characters the port can hold for transmit. */
    8.23 +    int                 tx_fifo_size;
    8.24 +    /* Transmit data buffer (interrupt-driven uart). */
    8.25 +    char               *txbuf;
    8.26 +    unsigned int        txbufp, txbufc;
    8.27 +    /* Force synchronous transmit. */
    8.28 +    int                 sync;
    8.29      /* Receiver callback functions (asynchronous receivers). */
    8.30      serial_rx_fn        rx_lo, rx_hi, rx;
    8.31      /* Receive data buffer (polling receivers). */
    8.32 -    char                rxbuf[RXBUFSZ];
    8.33 +    char                rxbuf[SERIAL_RXBUFSZ];
    8.34      unsigned int        rxbufp, rxbufc;
    8.35      /* Serial I/O is concurrency-safe. */
    8.36      spinlock_t          lock;
    8.37 @@ -40,9 +51,11 @@ struct uart_driver {
    8.38      void (*init_postirq)(struct serial_port *);
    8.39      /* Hook to clean up after Xen bootstrap (before domain 0 runs). */
    8.40      void (*endboot)(struct serial_port *);
    8.41 -    /* Put a char onto the serial line. */
    8.42 +    /* Transmit FIFO ready to receive up to @tx_fifo_size characters? */
    8.43 +    int  (*tx_empty)(struct serial_port *);
    8.44 +    /* Put a character onto the serial line. */
    8.45      void (*putc)(struct serial_port *, char);
    8.46 -    /* Get a char from the serial line: returns FALSE if no char available. */
    8.47 +    /* Get a character from the serial line: returns 0 if none available. */
    8.48      int  (*getc)(struct serial_port *, char *);
    8.49  };
    8.50  
    8.51 @@ -76,13 +89,23 @@ void serial_puts(int handle, const char 
    8.52  char serial_getc(int handle);
    8.53  
    8.54  /* Forcibly prevent serial lockup when the system is in a bad way. */
    8.55 +/* (NB. This also forces an implicit serial_start_sync()). */
    8.56  void serial_force_unlock(int handle);
    8.57  
    8.58 +/* Start/end a synchronous region (temporarily disable interrupt-driven tx). */
    8.59 +void serial_start_sync(int handle);
    8.60 +void serial_end_sync(int handle);
    8.61 +
    8.62 +/*
    8.63 + * Initialisation and helper functions for uart drivers.
    8.64 + */
    8.65  /* Register a uart on serial port @idx (e.g., @idx==0 is COM1). */
    8.66  void serial_register_uart(int idx, struct uart_driver *driver, void *uart);
    8.67 -
    8.68 -/* Driver helper function: process receive work in interrupt context. */
    8.69 +/* Place the serial port into asynchronous transmit mode. */
    8.70 +void serial_async_transmit(struct serial_port *port);
    8.71 +/* Process work in interrupt context. */
    8.72  void serial_rx_interrupt(struct serial_port *port, struct cpu_user_regs *regs);
    8.73 +void serial_tx_interrupt(struct serial_port *port, struct cpu_user_regs *regs);
    8.74  
    8.75  /*
    8.76   * Initialisers for individual uart drivers.