X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fserial%2Fserial_txx9.c;h=7186a82c47595582dfe3de1dbac4b6bb283585ba;hb=refs%2Fheads%2Fvserver;hp=ee98a867bc6dabd5604763fb7c0c26518bdb57bf;hpb=76828883507a47dae78837ab5dec5a5b4513c667;p=linux-2.6.git diff --git a/drivers/serial/serial_txx9.c b/drivers/serial/serial_txx9.c index ee98a867b..7186a82c4 100644 --- a/drivers/serial/serial_txx9.c +++ b/drivers/serial/serial_txx9.c @@ -33,8 +33,11 @@ * 1.02 Cleanup. (import 8250.c changes) * 1.03 Fix low-latency mode. (import 8250.c changes) * 1.04 Remove usage of deprecated functions, cleanup. + * 1.05 More strict check in verify_port. Cleanup. + * 1.06 Do not insert a char caused previous overrun. + * Fix some spin_locks. + * Do not call uart_add_one_port for absent ports. */ -#include #if defined(CONFIG_SERIAL_TXX9_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) #define SUPPORT_SYSRQ @@ -57,7 +60,7 @@ #include #include -static char *serial_version = "1.04"; +static char *serial_version = "1.06"; static char *serial_name = "TX39/49 Serial driver"; #define PASS_LIMIT 256 @@ -65,12 +68,10 @@ static char *serial_name = "TX39/49 Serial driver"; #if !defined(CONFIG_SERIAL_TXX9_STDSERIAL) /* "ttyS" is used for standard serial driver */ #define TXX9_TTY_NAME "ttyTX" -#define TXX9_TTY_DEVFS_NAME "tttx/" #define TXX9_TTY_MINOR_START (64 + 64) /* ttyTX0(128), ttyTX1(129) */ #else /* acts like standard serial driver */ #define TXX9_TTY_NAME "ttyS" -#define TXX9_TTY_DEVFS_NAME "tts/" #define TXX9_TTY_MINOR_START 64 #endif #define TXX9_TTY_MAJOR TTY_MAJOR @@ -94,6 +95,8 @@ static char *serial_name = "TX39/49 Serial driver"; #define UART_NR 4 #endif +#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) + struct uart_txx9_port { struct uart_port port; @@ -210,7 +213,7 @@ static inline unsigned int sio_in(struct uart_txx9_port *up, int offset) { switch (up->port.iotype) { default: - return *(volatile u32 *)(up->port.membase + offset); + return __raw_readl(up->port.membase + offset); case UPIO_PORT: return inl(up->port.iobase + offset); } @@ -221,7 +224,7 @@ sio_out(struct uart_txx9_port *up, int offset, int value) { switch (up->port.iotype) { default: - *(volatile u32 *)(up->port.membase + offset) = value; + __raw_writel(value, up->port.membase + offset); break; case UPIO_PORT: outl(value, up->port.iobase + offset); @@ -259,34 +262,19 @@ sio_quot_set(struct uart_txx9_port *up, int quot) static void serial_txx9_stop_tx(struct uart_port *port) { struct uart_txx9_port *up = (struct uart_txx9_port *)port; - unsigned long flags; - - spin_lock_irqsave(&up->port.lock, flags); sio_mask(up, TXX9_SIDICR, TXX9_SIDICR_TIE); - spin_unlock_irqrestore(&up->port.lock, flags); } static void serial_txx9_start_tx(struct uart_port *port) { struct uart_txx9_port *up = (struct uart_txx9_port *)port; - unsigned long flags; - - spin_lock_irqsave(&up->port.lock, flags); sio_set(up, TXX9_SIDICR, TXX9_SIDICR_TIE); - spin_unlock_irqrestore(&up->port.lock, flags); } static void serial_txx9_stop_rx(struct uart_port *port) { struct uart_txx9_port *up = (struct uart_txx9_port *)port; - unsigned long flags; - - spin_lock_irqsave(&up->port.lock, flags); up->port.read_status_mask &= ~TXX9_SIDISR_RDIS; -#if 0 - sio_mask(up, TXX9_SIDICR, TXX9_SIDICR_RIE); -#endif - spin_unlock_irqrestore(&up->port.lock, flags); } static void serial_txx9_enable_ms(struct uart_port *port) @@ -295,19 +283,23 @@ static void serial_txx9_enable_ms(struct uart_port *port) } static inline void -receive_chars(struct uart_txx9_port *up, unsigned int *status, struct pt_regs *regs) +receive_chars(struct uart_txx9_port *up, unsigned int *status) { struct tty_struct *tty = up->port.info->tty; unsigned char ch; unsigned int disr = *status; int max_count = 256; char flag; + unsigned int next_ignore_status_mask; do { ch = sio_in(up, TXX9_SIRFIFO); flag = TTY_NORMAL; up->port.icount.rx++; + /* mask out RFDN_MASK bit added by previous overrun */ + next_ignore_status_mask = + up->port.ignore_status_mask & ~TXX9_SIDISR_RFDN_MASK; if (unlikely(disr & (TXX9_SIDISR_UBRK | TXX9_SIDISR_UPER | TXX9_SIDISR_UFER | TXX9_SIDISR_UOER))) { /* @@ -328,8 +320,17 @@ receive_chars(struct uart_txx9_port *up, unsigned int *status, struct pt_regs *r up->port.icount.parity++; else if (disr & TXX9_SIDISR_UFER) up->port.icount.frame++; - if (disr & TXX9_SIDISR_UOER) + if (disr & TXX9_SIDISR_UOER) { up->port.icount.overrun++; + /* + * The receiver read buffer still hold + * a char which caused overrun. + * Ignore next char by adding RFDN_MASK + * to ignore_status_mask temporarily. + */ + next_ignore_status_mask |= + TXX9_SIDISR_RFDN_MASK; + } /* * Mask off conditions which should be ingored. @@ -343,12 +344,13 @@ receive_chars(struct uart_txx9_port *up, unsigned int *status, struct pt_regs *r else if (disr & TXX9_SIDISR_UFER) flag = TTY_FRAME; } - if (uart_handle_sysrq_char(&up->port, ch, regs)) + if (uart_handle_sysrq_char(&up->port, ch)) goto ignore_char; uart_insert_char(&up->port, disr, TXX9_SIDISR_UOER, ch, flag); ignore_char: + up->port.ignore_status_mask = next_ignore_status_mask; disr = sio_in(up, TXX9_SIDISR); } while (!(disr & TXX9_SIDISR_UVALID) && (max_count-- > 0)); spin_unlock(&up->port.lock); @@ -389,7 +391,7 @@ static inline void transmit_chars(struct uart_txx9_port *up) serial_txx9_stop_tx(&up->port); } -static irqreturn_t serial_txx9_interrupt(int irq, void *dev_id, struct pt_regs *regs) +static irqreturn_t serial_txx9_interrupt(int irq, void *dev_id) { int pass_counter = 0; struct uart_txx9_port *up = dev_id; @@ -407,7 +409,7 @@ static irqreturn_t serial_txx9_interrupt(int irq, void *dev_id, struct pt_regs * } if (status & TXX9_SIDISR_RDIS) - receive_chars(up, &status, regs); + receive_chars(up, &status); if (status & TXX9_SIDISR_TDIS) transmit_chars(up); /* Clear TX/RX Int. Status */ @@ -450,14 +452,11 @@ static unsigned int serial_txx9_get_mctrl(struct uart_port *port) static void serial_txx9_set_mctrl(struct uart_port *port, unsigned int mctrl) { struct uart_txx9_port *up = (struct uart_txx9_port *)port; - unsigned long flags; - spin_lock_irqsave(&up->port.lock, flags); if (mctrl & TIOCM_RTS) sio_mask(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSSC); else sio_set(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSSC); - spin_unlock_irqrestore(&up->port.lock, flags); } static void serial_txx9_break_ctl(struct uart_port *port, int break_state) @@ -481,7 +480,7 @@ static int serial_txx9_startup(struct uart_port *port) /* * Clear the FIFO buffers and disable them. - * (they will be reeanbled in set_termios()) + * (they will be reenabled in set_termios()) */ sio_set(up, TXX9_SIFCR, TXX9_SIFCR_TFRST | TXX9_SIFCR_RFRST | TXX9_SIFCR_FRSTE); @@ -496,7 +495,7 @@ static int serial_txx9_startup(struct uart_port *port) sio_out(up, TXX9_SIDISR, 0); retval = request_irq(up->port.irq, serial_txx9_interrupt, - SA_SHIRQ, "serial_txx9", up); + IRQF_SHARED, "serial_txx9", up); if (retval) return retval; @@ -557,8 +556,8 @@ static void serial_txx9_shutdown(struct uart_port *port) } static void -serial_txx9_set_termios(struct uart_port *port, struct termios *termios, - struct termios *old) +serial_txx9_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) { struct uart_txx9_port *up = (struct uart_txx9_port *)port; unsigned int cval, fcr = 0; @@ -784,8 +783,14 @@ static void serial_txx9_config_port(struct uart_port *port, int uflags) static int serial_txx9_verify_port(struct uart_port *port, struct serial_struct *ser) { - if (ser->irq < 0 || - ser->baud_base < 9600 || ser->type != PORT_TXX9) + unsigned long new_port = ser->port; + if (HIGH_BITS_OFFSET) + new_port += (unsigned long)ser->port_high << HIGH_BITS_OFFSET; + if (ser->type != port->type || + ser->irq != port->irq || + ser->io_type != port->iotype || + new_port != port->iobase || + (unsigned long)ser->iomem_base != port->mapbase) return -EINVAL; return 0; } @@ -827,7 +832,8 @@ static void __init serial_txx9_register_ports(struct uart_driver *drv) up->port.line = i; up->port.ops = &serial_txx9_pops; - uart_add_one_port(drv, &up->port); + if (up->port.iobase || up->port.mapbase) + uart_add_one_port(drv, &up->port); } } @@ -854,6 +860,14 @@ static inline void wait_for_xmitr(struct uart_txx9_port *up) } } +static void serial_txx9_console_putchar(struct uart_port *port, int ch) +{ + struct uart_txx9_port *up = (struct uart_txx9_port *)port; + + wait_for_xmitr(up); + sio_out(up, TXX9_SITFIFO, ch); +} + /* * Print a string to the serial port trying not to disturb * any possible real use of the port... @@ -865,7 +879,6 @@ serial_txx9_console_write(struct console *co, const char *s, unsigned int count) { struct uart_txx9_port *up = &serial_txx9_ports[co->index]; unsigned int ier, flcr; - int i; /* * First save the UER then disable the interrupts @@ -879,22 +892,7 @@ serial_txx9_console_write(struct console *co, const char *s, unsigned int count) if (!(up->port.flags & UPF_CONS_FLOW) && (flcr & TXX9_SIFLCR_TES)) sio_out(up, TXX9_SIFLCR, flcr & ~TXX9_SIFLCR_TES); - /* - * Now, do each character - */ - for (i = 0; i < count; i++, s++) { - wait_for_xmitr(up); - - /* - * Send the character out. - * If a LF, also do CR... - */ - sio_out(up, TXX9_SITFIFO, *s); - if (*s == 10) { - wait_for_xmitr(up); - sio_out(up, TXX9_SITFIFO, 13); - } - } + uart_console_write(&up->port, s, count, serial_txx9_console_putchar); /* * Finally, wait for transmitter to become empty @@ -926,11 +924,6 @@ static int serial_txx9_console_setup(struct console *co, char *options) if (!port->ops) return -ENODEV; - /* - * Temporary fix. - */ - spin_lock_init(&port->lock); - /* * Disable UART interrupts, set DTR and RTS high * and set speed. @@ -975,7 +968,6 @@ console_initcall(serial_txx9_console_init); static struct uart_driver serial_txx9_reg = { .owner = THIS_MODULE, .driver_name = "serial_txx9", - .devfs_name = TXX9_TTY_DEVFS_NAME, .dev_name = TXX9_TTY_NAME, .major = TXX9_TTY_MAJOR, .minor = TXX9_TTY_MINOR_START, @@ -998,7 +990,6 @@ int __init early_serial_txx9_setup(struct uart_port *port) /** * serial_txx9_suspend_port - suspend one serial port * @line: serial line number - * @level: the level of port suspension, as per uart_suspend_port * * Suspend one serial port. */ @@ -1010,7 +1001,6 @@ static void serial_txx9_suspend_port(int line) /** * serial_txx9_resume_port - resume one serial port * @line: serial line number - * @level: the level of port resumption, as per uart_resume_port * * Resume one serial port. */ @@ -1041,11 +1031,10 @@ static int __devinit serial_txx9_register_port(struct uart_port *port) mutex_lock(&serial_txx9_mutex); for (i = 0; i < UART_NR; i++) { uart = &serial_txx9_ports[i]; - if (uart->port.type == PORT_UNKNOWN) + if (!(uart->port.iobase || uart->port.mapbase)) break; } if (i < UART_NR) { - uart_remove_one_port(&serial_txx9_reg, &uart->port); uart->port.iobase = port->iobase; uart->port.membase = port->membase; uart->port.irq = port->irq; @@ -1080,9 +1069,8 @@ static void __devexit serial_txx9_unregister_port(int line) uart->port.type = PORT_UNKNOWN; uart->port.iobase = 0; uart->port.mapbase = 0; - uart->port.membase = 0; + uart->port.membase = NULL; uart->port.dev = NULL; - uart_add_one_port(&serial_txx9_reg, &uart->port); mutex_unlock(&serial_txx9_mutex); } @@ -1198,8 +1186,11 @@ static void __exit serial_txx9_exit(void) #ifdef ENABLE_SERIAL_TXX9_PCI pci_unregister_driver(&serial_txx9_pci_driver); #endif - for (i = 0; i < UART_NR; i++) - uart_remove_one_port(&serial_txx9_reg, &serial_txx9_ports[i].port); + for (i = 0; i < UART_NR; i++) { + struct uart_txx9_port *up = &serial_txx9_ports[i]; + if (up->port.iobase || up->port.mapbase) + uart_remove_one_port(&serial_txx9_reg, &up->port); + } uart_unregister_driver(&serial_txx9_reg); }