From: Ralf Baechle Replace the old drivers/char/ serial driver for the Toshiba TX series of SOCs with a modern style drivers/serial/ one. Signed-off-by: Andrew Morton --- /dev/null | 1073 -------------------------------- 25-akpm/drivers/char/Kconfig | 16 25-akpm/drivers/char/Makefile | 1 25-akpm/drivers/serial/Kconfig | 14 25-akpm/drivers/serial/serial_txx9.c | 1171 +++++++++++++++++++++++++++++++++++ 25-akpm/include/linux/pci_ids.h | 1 25-akpm/include/linux/serial_core.h | 3 7 files changed, 1189 insertions(+), 1090 deletions(-) diff -puN drivers/char/Kconfig~mips-txx9-serieal-driver-rewrite drivers/char/Kconfig --- 25/drivers/char/Kconfig~mips-txx9-serieal-driver-rewrite 2005-01-29 11:25:59.127880504 -0800 +++ 25-akpm/drivers/char/Kconfig 2005-01-29 11:25:59.153876552 -0800 @@ -348,22 +348,6 @@ config ISTALLION To compile this driver as a module, choose M here: the module will be called istallion. -config SERIAL_TX3912 - bool "TX3912/PR31700 serial port support" - depends on SERIAL_NONSTANDARD && MIPS && BROKEN_ON_SMP - help - The TX3912 is a Toshiba RISC processor based on the MIPS 3900 core; - see . - Say Y here to enable kernel support for the on-board serial port. - -config SERIAL_TX3912_CONSOLE - bool "Console on TX3912/PR31700 serial port" - depends on SERIAL_TX3912 - help - The TX3912 is a Toshiba RISC processor based on the MIPS 3900 core; - see . - Say Y here to direct console I/O to the on-board serial port. - config AU1000_UART bool "Enable Au1000 UART Support" depends on SERIAL_NONSTANDARD && MIPS diff -puN drivers/char/Makefile~mips-txx9-serieal-driver-rewrite drivers/char/Makefile --- 25/drivers/char/Makefile~mips-txx9-serieal-driver-rewrite 2005-01-29 11:25:59.128880352 -0800 +++ 25-akpm/drivers/char/Makefile 2005-01-29 11:25:59.153876552 -0800 @@ -20,7 +20,6 @@ obj-$(CONFIG_ESPSERIAL) += esp.o obj-$(CONFIG_MVME147_SCC) += generic_serial.o vme_scc.o obj-$(CONFIG_MVME162_SCC) += generic_serial.o vme_scc.o obj-$(CONFIG_BVME6000_SCC) += generic_serial.o vme_scc.o -obj-$(CONFIG_SERIAL_TX3912) += generic_serial.o serial_tx3912.o obj-$(CONFIG_ROCKETPORT) += rocket.o obj-$(CONFIG_SERIAL167) += serial167.o obj-$(CONFIG_CYCLADES) += cyclades.o diff -L drivers/char/serial_tx3912.c -puN drivers/char/serial_tx3912.c~mips-txx9-serieal-driver-rewrite /dev/null --- 25/drivers/char/serial_tx3912.c +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,981 +0,0 @@ -/* - * drivers/char/serial_tx3912.c - * - * Copyright (C) 1999 Harald Koerfgen - * Copyright (C) 2000 Jim Pick - * Copyright (C) 2001 Steven J. Hill (sjhill@realitydiluted.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Serial driver for TMPR3912/05 and PR31700 processors - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "serial_tx3912.h" - -/* - * Forward declarations for serial routines - */ -static void rs_disable_tx_interrupts (void * ptr); -static void rs_enable_tx_interrupts (void * ptr); -static void rs_disable_rx_interrupts (void * ptr); -static void rs_enable_rx_interrupts (void * ptr); -static int rs_get_CD (void * ptr); -static void rs_shutdown_port (void * ptr); -static int rs_set_real_termios (void *ptr); -static int rs_chars_in_buffer (void * ptr); - -/* - * Used by generic serial driver to access hardware - */ -static struct real_driver rs_real_driver = { - .disable_tx_interrupts = rs_disable_tx_interrupts, - .enable_tx_interrupts = rs_enable_tx_interrupts, - .disable_rx_interrupts = rs_disable_rx_interrupts, - .enable_rx_interrupts = rs_enable_rx_interrupts, - .get_CD = rs_get_CD, - .shutdown_port = rs_shutdown_port, - .set_real_termios = rs_set_real_termios, - .chars_in_buffer = rs_chars_in_buffer, -}; - -/* - * Structures and such for TTY sessions and usage counts - */ -static struct tty_driver *rs_driver; -struct rs_port *rs_ports; -int rs_initialized = 0; - -/* - * ---------------------------------------------------------------------- - * - * Here starts the interrupt handling routines. All of the following - * subroutines are declared as inline and are folded into - * rs_interrupt(). They were separated out for readability's sake. - * - * Note: rs_interrupt() is a "fast" interrupt, which means that it - * runs with interrupts turned off. People who may want to modify - * rs_interrupt() should try to keep the interrupt handler as fast as - * possible. After you are done making modifications, it is not a bad - * idea to do: - * - * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c - * - * and look at the resulting assemble code in serial.s. - * - * - Ted Ts'o (tytso@mit.edu), 7-Mar-93 - * ----------------------------------------------------------------------- - */ -static inline void receive_char_pio(struct rs_port *port) -{ - struct tty_struct *tty = port->gs.tty; - unsigned char ch; - int counter = 2048; - - /* While there are characters, get them ... */ - while (counter>0) { - if (!(inl(port->base + TX3912_UART_CTRL1) & UART_RX_HOLD_FULL)) - break; - ch = inb(port->base + TX3912_UART_DATA); - if (tty->flip.count < TTY_FLIPBUF_SIZE) { - *tty->flip.char_buf_ptr++ = ch; - *tty->flip.flag_buf_ptr++ = 0; - tty->flip.count++; - } - udelay(1); /* Allow things to happen - it take a while */ - counter--; - } - if (!counter) - printk( "Ugh, looped in receive_char_pio!\n" ); - - tty_flip_buffer_push(tty); - -#if 0 - /* Now handle error conditions */ - if (*status & (INTTYPE(UART_RXOVERRUN_INT) | - INTTYPE(UART_FRAMEERR_INT) | - INTTYPE(UART_PARITYERR_INT) | - INTTYPE(UART_BREAK_INT))) { - - /* - * Now check to see if character should be - * ignored, and mask off conditions which - * should be ignored. - */ - if (*status & port->ignore_status_mask) { - goto ignore_char; - } - *status &= port->read_status_mask; - - if (*status & INTTYPE(UART_BREAK_INT)) { - rs_dprintk(TX3912_UART_DEBUG_INTERRUPTS, "handling break...."); - *tty->flip.flag_buf_ptr = TTY_BREAK; - } - else if (*status & INTTYPE(UART_PARITYERR_INT)) { - *tty->flip.flag_buf_ptr = TTY_PARITY; - } - else if (*status & INTTYPE(UART_FRAMEERR_INT)) { - *tty->flip.flag_buf_ptr = TTY_FRAME; - } - if (*status & INTTYPE(UART_RXOVERRUN_INT)) { - /* - * Overrun is special, since it's - * reported immediately, and doesn't - * affect the current character - */ - if (tty->flip.count < TTY_FLIPBUF_SIZE) { - tty->flip.count++; - tty->flip.flag_buf_ptr++; - tty->flip.char_buf_ptr++; - *tty->flip.flag_buf_ptr = TTY_OVERRUN; - } - } - } - - tty->flip.flag_buf_ptr++; - tty->flip.char_buf_ptr++; - tty->flip.count++; - -ignore_char: - tty_flip_buffer_push(tty); -#endif -} - -static inline void transmit_char_pio(struct rs_port *port) -{ - /* While I'm able to transmit ... */ - for (;;) { - if (!(inl(port->base + TX3912_UART_CTRL1) & UART_TX_EMPTY)) - break; - else if (port->x_char) { - outb(port->x_char, port->base + TX3912_UART_DATA); - port->icount.tx++; - port->x_char = 0; - } - else if (port->gs.xmit_cnt <= 0 || port->gs.tty->stopped || - port->gs.tty->hw_stopped) { - break; - } - else { - outb(port->gs.xmit_buf[port->gs.xmit_tail++], - port->base + TX3912_UART_DATA); - port->icount.tx++; - port->gs.xmit_tail &= SERIAL_XMIT_SIZE-1; - if (--port->gs.xmit_cnt <= 0) { - break; - } - } - udelay(10); /* Allow things to happen - it take a while */ - } - - if (port->gs.xmit_cnt <= 0 || port->gs.tty->stopped || - port->gs.tty->hw_stopped) { - rs_disable_tx_interrupts(port); - } - - if (port->gs.xmit_cnt <= port->gs.wakeup_chars) { - tty_wakeup(port->gs.tty); - rs_dprintk (TX3912_UART_DEBUG_TRANSMIT, "Waking up.... ldisc (%d)....\n", - port->gs.wakeup_chars); - } -} - - - -static inline void check_modem_status(struct rs_port *port) -{ - /* We don't have a carrier detect line - but just respond - like we had one anyways so that open() becomes unblocked */ - wake_up_interruptible(&port->gs.open_wait); -} - -int count = 0; - -/* - * This is the serial driver's interrupt routine (inlined, because - * there are two different versions of this, one for each serial port, - * differing only by the bits used in interrupt status 2 register) - */ - -static inline void rs_rx_interrupt(int irq, void *dev_id, - struct pt_regs * regs, int intshift) -{ - struct rs_port * port; - unsigned long int2status; - unsigned long flags; - unsigned long ints; - - save_and_cli(flags); - - port = (struct rs_port *)dev_id; - rs_dprintk (TX3912_UART_DEBUG_INTERRUPTS, "rs_interrupt (port %p, shift %d)...", port, intshift); - - /* Get the interrrupts we have enabled */ - int2status = IntStatus2 & IntEnable2; - - /* Get interrupts in easy to use form */ - ints = int2status >> intshift; - - /* Clear any interrupts we might be about to handle */ - IntClear2 = int2status & ( - (INTTYPE(UART_RXOVERRUN_INT) | - INTTYPE(UART_FRAMEERR_INT) | - INTTYPE(UART_BREAK_INT) | - INTTYPE(UART_PARITYERR_INT) | - INTTYPE(UART_RX_INT)) << intshift); - - if (!port || !port->gs.tty) { - restore_flags(flags); - return; - } - - /* RX Receiver Holding Register Overrun */ - if (ints & INTTYPE(UART_RXOVERRUN_INT)) { - rs_dprintk (TX3912_UART_DEBUG_INTERRUPTS, "overrun"); - port->icount.overrun++; - } - - /* RX Frame Error */ - if (ints & INTTYPE(UART_FRAMEERR_INT)) { - rs_dprintk (TX3912_UART_DEBUG_INTERRUPTS, "frame error"); - port->icount.frame++; - } - - /* Break signal received */ - if (ints & INTTYPE(UART_BREAK_INT)) { - rs_dprintk (TX3912_UART_DEBUG_INTERRUPTS, "break"); - port->icount.brk++; - } - - /* RX Parity Error */ - if (ints & INTTYPE(UART_PARITYERR_INT)) { - rs_dprintk (TX3912_UART_DEBUG_INTERRUPTS, "parity error"); - port->icount.parity++; - } - - /* Receive byte (non-DMA) */ - if (ints & INTTYPE(UART_RX_INT)) { - receive_char_pio(port); - } - - restore_flags(flags); - - rs_dprintk (TX3912_UART_DEBUG_INTERRUPTS, "end.\n"); -} - -static inline void rs_tx_interrupt(int irq, void *dev_id, - struct pt_regs * regs, int intshift) -{ - struct rs_port * port; - unsigned long int2status; - unsigned long flags; - unsigned long ints; - - save_and_cli(flags); - - port = (struct rs_port *)dev_id; - rs_dprintk (TX3912_UART_DEBUG_INTERRUPTS, "rs_interrupt (port %p, shift %d)...", port, intshift); - - /* Get the interrrupts we have enabled */ - int2status = IntStatus2 & IntEnable2; - - if (!port || !port->gs.tty) { - restore_flags(flags); - return; - } - - /* Get interrupts in easy to use form */ - ints = int2status >> intshift; - - /* Clear any interrupts we might be about to handle */ - IntClear2 = int2status & ( - (INTTYPE(UART_TX_INT) | - INTTYPE(UART_EMPTY_INT) | - INTTYPE(UART_TXOVERRUN_INT)) << intshift); - - /* TX holding register empty, so transmit byte (non-DMA) */ - if (ints & (INTTYPE(UART_TX_INT) | INTTYPE(UART_EMPTY_INT))) { - transmit_char_pio(port); - } - - /* TX Transmit Holding Register Overrun (shouldn't happen) */ - if (ints & INTTYPE(UART_TXOVERRUN_INT)) { - printk ( "rs: TX overrun\n"); - } - - /* - check_modem_status(); - */ - - restore_flags(flags); - - rs_dprintk (TX3912_UART_DEBUG_INTERRUPTS, "end.\n"); -} - -static void rs_rx_interrupt_uarta(int irq, void *dev_id, - struct pt_regs * regs) -{ - rs_rx_interrupt(irq, dev_id, regs, UARTA_SHIFT); -} - -static void rs_tx_interrupt_uarta(int irq, void *dev_id, - struct pt_regs * regs) -{ - rs_tx_interrupt(irq, dev_id, regs, UARTA_SHIFT); -} - -/* - *********************************************************************** - * Here are the routines that actually * - * interface with the generic_serial driver * - *********************************************************************** - */ -static void rs_disable_tx_interrupts (void * ptr) -{ - struct rs_port *port = ptr; - unsigned long flags; - - save_and_cli(flags); - port->gs.flags &= ~GS_TX_INTEN; - - IntEnable2 &= ~((INTTYPE(UART_TX_INT) | - INTTYPE(UART_EMPTY_INT) | - INTTYPE(UART_TXOVERRUN_INT)) << port->intshift); - - IntClear2 = (INTTYPE(UART_TX_INT) | - INTTYPE(UART_EMPTY_INT) | - INTTYPE(UART_TXOVERRUN_INT)) << port->intshift; - - restore_flags(flags); -} - -static void rs_enable_tx_interrupts (void * ptr) -{ - struct rs_port *port = ptr; - unsigned long flags; - - save_and_cli(flags); - - IntClear2 = (INTTYPE(UART_TX_INT) | - INTTYPE(UART_EMPTY_INT) | - INTTYPE(UART_TXOVERRUN_INT)) << port->intshift; - - IntEnable2 |= (INTTYPE(UART_TX_INT) | - INTTYPE(UART_EMPTY_INT) | - INTTYPE(UART_TXOVERRUN_INT)) << port->intshift; - - /* Send a char to start TX interrupts happening */ - transmit_char_pio(port); - - restore_flags(flags); -} - -static void rs_disable_rx_interrupts (void * ptr) -{ - struct rs_port *port = ptr; - unsigned long flags; - - save_and_cli(flags); - - IntEnable2 &= ~((INTTYPE(UART_RX_INT) | - INTTYPE(UART_RXOVERRUN_INT) | - INTTYPE(UART_FRAMEERR_INT) | - INTTYPE(UART_BREAK_INT) | - INTTYPE(UART_PARITYERR_INT)) << port->intshift); - - IntClear2 = (INTTYPE(UART_RX_INT) | - INTTYPE(UART_RXOVERRUN_INT) | - INTTYPE(UART_FRAMEERR_INT) | - INTTYPE(UART_BREAK_INT) | - INTTYPE(UART_PARITYERR_INT)) << port->intshift; - - restore_flags(flags); -} - -static void rs_enable_rx_interrupts (void * ptr) -{ - struct rs_port *port = ptr; - unsigned long flags; - - save_and_cli(flags); - - IntEnable2 |= (INTTYPE(UART_RX_INT) | - INTTYPE(UART_RXOVERRUN_INT) | - INTTYPE(UART_FRAMEERR_INT) | - INTTYPE(UART_BREAK_INT) | - INTTYPE(UART_PARITYERR_INT)) << port->intshift; - - /* Empty the input buffer - apparently this is *vital* */ - while (inl(port->base + TX3912_UART_CTRL1) & UART_RX_HOLD_FULL) { - inb(port->base + TX3912_UART_DATA); - } - - IntClear2 = (INTTYPE(UART_RX_INT) | - INTTYPE(UART_RXOVERRUN_INT) | - INTTYPE(UART_FRAMEERR_INT) | - INTTYPE(UART_BREAK_INT) | - INTTYPE(UART_PARITYERR_INT)) << port->intshift; - - restore_flags(flags); -} - - -static int rs_get_CD (void * ptr) -{ - /* No Carried Detect in Hardware - just return true */ - func_exit(); - return (1); -} - -static void rs_shutdown_port (void * ptr) -{ - struct rs_port *port = ptr; - - func_enter(); - - port->gs.flags &= ~GS_ACTIVE; - - func_exit(); -} - -static int rs_set_real_termios (void *ptr) -{ - struct rs_port *port = ptr; - int t; - - switch (port->gs.baud) { - /* Save some typing work... */ -#define e(x) case x:t= TX3912_UART_CTRL2_B ## x ; break - e(300);e(600);e(1200);e(2400);e(4800);e(9600); - e(19200);e(38400);e(57600);e(76800);e(115200);e(230400); - case 0 :t = -1; - break; - default: - /* Can I return "invalid"? */ - t = TX3912_UART_CTRL2_B9600; - printk (KERN_INFO "rs: unsupported baud rate: %d.\n", port->gs.baud); - break; - } -#undef e - if (t >= 0) { - /* Jim: Set Hardware Baud rate - there is some good - code in drivers/char/serial.c */ - - /* Program hardware for parity, data bits, stop bits (note: these are hardcoded to 8N1 */ - UartA_Ctrl1 &= 0xf000000f; - UartA_Ctrl1 &= ~(UART_DIS_TXD | SER_SEVEN_BIT | SER_EVEN_PARITY | SER_TWO_STOP); - -#define CFLAG port->gs.tty->termios->c_cflag - if (C_PARENB(port->gs.tty)) { - if (!C_PARODD(port->gs.tty)) - UartA_Ctrl1 |= SER_EVEN_PARITY; - else - UartA_Ctrl1 |= SER_ODD_PARITY; - } - if ((CFLAG & CSIZE)==CS6) - printk(KERN_ERR "6 bits not supported\n"); - if ((CFLAG & CSIZE)==CS5) - printk(KERN_ERR "5 bits not supported\n"); - if ((CFLAG & CSIZE)==CS7) - UartA_Ctrl1 |= SER_SEVEN_BIT; - if (C_CSTOPB(port->gs.tty)) - UartA_Ctrl1 |= SER_TWO_STOP; - - outl(t, port->base + TX3912_UART_CTRL2); - outl(0, port->base + TX3912_UART_DMA_CTRL1); - outl(0, port->base + TX3912_UART_DMA_CTRL2); - UartA_Ctrl1 |= TX3912_UART_CTRL1_UARTON; - - /* wait until UARTA is stable */ - while (~UartA_Ctrl1 & TX3912_UART_CTRL1_UARTON); - } - - func_exit (); - return 0; -} - -static int rs_chars_in_buffer (void * ptr) -{ - struct rs_port *port = ptr; - int scratch; - - scratch = inl(port->base + TX3912_UART_CTRL1); - - return ((scratch & UART_TX_EMPTY) ? 0 : 1); -} - -/* ********************************************************************** * - * Here are the routines that actually * - * interface with the rest of the system * - * ********************************************************************** */ -static int rs_open (struct tty_struct * tty, struct file * filp) -{ - struct rs_port *port; - int retval, line; - - func_enter(); - - if (!rs_initialized) { - return -EIO; - } - - line = tty->index; - rs_dprintk (TX3912_UART_DEBUG_OPEN, "%d: opening line %d. tty=%p ctty=%p)\n", - (int) current->pid, line, tty, current->tty); - - if ((line < 0) || (line >= TX3912_UART_NPORTS)) - return -ENODEV; - - /* Pre-initialized already */ - port = & rs_ports[line]; - - rs_dprintk (TX3912_UART_DEBUG_OPEN, "port = %p\n", port); - - tty->driver_data = port; - port->gs.tty = tty; - port->gs.count++; - - rs_dprintk (TX3912_UART_DEBUG_OPEN, "starting port\n"); - - /* - * Start up serial port - */ - retval = gs_init_port(&port->gs); - rs_dprintk (TX3912_UART_DEBUG_OPEN, "done gs_init\n"); - if (retval) { - port->gs.count--; - return retval; - } - - port->gs.flags |= GS_ACTIVE; - - rs_dprintk (TX3912_UART_DEBUG_OPEN, "before inc_use_count (count=%d.\n", - port->gs.count); - rs_dprintk (TX3912_UART_DEBUG_OPEN, "after inc_use_count\n"); - - /* Jim: Initialize port hardware here */ - - /* Enable high-priority interrupts for UARTA */ - IntEnable6 |= INT6_UARTARXINT; - rs_enable_rx_interrupts(&rs_ports[0]); - - retval = gs_block_til_ready(&port->gs, filp); - rs_dprintk (TX3912_UART_DEBUG_OPEN, "Block til ready returned %d. Count=%d\n", - retval, port->gs.count); - - if (retval) { - port->gs.count--; - return retval; - } - /* tty->low_latency = 1; */ - - func_exit(); - - /* Jim */ -/* cli(); */ - - return 0; - -} - - -static int rs_ioctl (struct tty_struct * tty, struct file * filp, - unsigned int cmd, unsigned long arg) -{ - int rc; - struct rs_port *port = tty->driver_data; - int ival; - - rc = 0; - switch (cmd) { - case TIOCGSOFTCAR: - rc = put_user(((tty->termios->c_cflag & CLOCAL) ? 1 : 0), - (unsigned int *) arg); - break; - case TIOCSSOFTCAR: - if ((rc = get_user(ival, (unsigned int *) arg)) == 0) { - tty->termios->c_cflag = - (tty->termios->c_cflag & ~CLOCAL) | - (ival ? CLOCAL : 0); - } - break; - case TIOCGSERIAL: - if ((rc = verify_area(VERIFY_WRITE, (void *) arg, - sizeof(struct serial_struct))) == 0) - rc = gs_getserial(&port->gs, (struct serial_struct *) arg); - break; - case TIOCSSERIAL: - if ((rc = verify_area(VERIFY_READ, (void *) arg, - sizeof(struct serial_struct))) == 0) - rc = gs_setserial(&port->gs, (struct serial_struct *) arg); - break; - default: - rc = -ENOIOCTLCMD; - break; - } - - /* func_exit(); */ - return rc; -} - - -/* - * This function is used to send a high-priority XON/XOFF character to - * the device - */ -static void rs_send_xchar(struct tty_struct * tty, char ch) -{ - struct rs_port *port = (struct rs_port *)tty->driver_data; - func_enter (); - - port->x_char = ch; - if (ch) { - /* Make sure transmit interrupts are on */ - rs_enable_tx_interrupts(tty); - } - - func_exit(); -} - - -/* - * ------------------------------------------------------------ - * rs_throttle() - * - * This routine is called by the upper-layer tty layer to signal that - * incoming characters should be throttled. - * ------------------------------------------------------------ - */ -static void rs_throttle(struct tty_struct * tty) -{ -#ifdef TX3912_UART_DEBUG_THROTTLE - char buf[64]; - - printk("throttle %s: %d....\n", tty_name(tty, buf), - tty->ldisc.chars_in_buffer(tty)); -#endif - - func_enter (); - - if (I_IXOFF(tty)) - rs_send_xchar(tty, STOP_CHAR(tty)); - - func_exit (); -} - -static void rs_unthrottle(struct tty_struct * tty) -{ - struct rs_port *port = (struct rs_port *)tty->driver_data; -#ifdef TX3912_UART_DEBUG_THROTTLE - char buf[64]; - - printk("unthrottle %s: %d....\n", tty_name(tty, buf), - tty->ldisc.chars_in_buffer(tty)); -#endif - - func_enter(); - - if (I_IXOFF(tty)) { - if (port->x_char) - port->x_char = 0; - else - rs_send_xchar(tty, START_CHAR(tty)); - } - - func_exit(); -} - - - - - -/* ********************************************************************** * - * Here are the initialization routines. * - * ********************************************************************** */ - -void * ckmalloc (int size) -{ - void *p; - - p = kmalloc(size, GFP_KERNEL); - if (p) - memset(p, 0, size); - return p; -} - - - -static int rs_init_portstructs(void) -{ - struct rs_port *port; - int i; - - /* Debugging */ - func_enter(); - - rs_ports = ckmalloc(TX3912_UART_NPORTS * sizeof (struct rs_port)); - if (!rs_ports) - return -ENOMEM; - - port = rs_ports; - for (i=0; i < TX3912_UART_NPORTS;i++) { - rs_dprintk (TX3912_UART_DEBUG_INIT, "initing port %d\n", i); - port->gs.magic = SERIAL_MAGIC; - port->gs.close_delay = HZ/2; - port->gs.closing_wait = 30 * HZ; - port->gs.rd = &rs_real_driver; -#ifdef NEW_WRITE_LOCKING - port->gs.port_write_sem = MUTEX; -#endif -#ifdef DECLARE_WAITQUEUE - init_waitqueue_head(&port->gs.open_wait); - init_waitqueue_head(&port->gs.close_wait); -#endif - port->base = (i == 0) ? TX3912_UARTA_BASE : TX3912_UARTB_BASE; - port->intshift = (i == 0) ? UARTA_SHIFT : UARTB_SHIFT; - rs_dprintk (TX3912_UART_DEBUG_INIT, "base 0x%08lx intshift %d\n", - port->base, port->intshift); - port++; - } - - func_exit(); - return 0; -} - -static struct tty_operations rs_ops = { - .open = rs_open, - .close = gs_close, - .write = gs_write, - .put_char = gs_put_char, - .flush_chars = gs_flush_chars, - .write_room = gs_write_room, - .chars_in_buffer = gs_chars_in_buffer, - .flush_buffer = gs_flush_buffer, - .ioctl = rs_ioctl, - .throttle = rs_throttle, - .unthrottle = rs_unthrottle, - .set_termios = gs_set_termios, - .stop = gs_stop, - .start = gs_start, - .hangup = gs_hangup, -}; - -static int rs_init_drivers(void) -{ - int error; - - func_enter(); - - rs_driver = alloc_tty_driver(TX3912_UART_NPORTS); - if (!rs_driver) - return -ENOMEM; - rs_driver->owner = THIS_MODULE; - rs_driver->driver_name = "serial"; - rs_driver->name = "ttyS"; - rs_driver->major = TTY_MAJOR; - rs_driver->minor_start = 64; - rs_driver->type = TTY_DRIVER_TYPE_SERIAL; - rs_driver->subtype = SERIAL_TYPE_NORMAL; - rs_driver->init_termios = tty_std_termios; - rs_driver->init_termios.c_cflag = - B115200 | CS8 | CREAD | HUPCL | CLOCAL; - tty_set_operations(rs_driver, &rs_ops); - if ((error = tty_register_driver(rs_driver))) { - printk(KERN_ERR "Couldn't register serial driver, error = %d\n", - error); - put_tty_driver(rs_driver); - return 1; - } - return 0; -} - - -static void __init tx3912_rs_init(void) -{ - int rc; - - - func_enter(); - rs_dprintk (TX3912_UART_DEBUG_INIT, "Initing serial module... (rs_debug=%d)\n", rs_debug); - - rc = rs_init_portstructs (); - rs_init_drivers (); - if (request_irq(2, rs_tx_interrupt_uarta, SA_SHIRQ | SA_INTERRUPT, - "serial", &rs_ports[0])) { - printk(KERN_ERR "rs: Cannot allocate irq for UARTA.\n"); - rc = 0; - } - if (request_irq(3, rs_rx_interrupt_uarta, SA_SHIRQ | SA_INTERRUPT, - "serial", &rs_ports[0])) { - printk(KERN_ERR "rs: Cannot allocate irq for UARTA.\n"); - rc = 0; - } - - IntEnable6 |= INT6_UARTARXINT; - rs_enable_rx_interrupts(&rs_ports[0]); - -#ifndef CONFIG_SERIAL_TX3912_CONSOLE -{ - unsigned int scratch = 0; - - /* Setup master clock for UART */ - scratch = inl(TX3912_CLK_CTRL_BASE); - scratch &= ~TX3912_CLK_CTRL_SIBMCLKDIV_MASK; - scratch |= ((0x2 << TX3912_CLK_CTRL_SIBMCLKDIV_SHIFT) & - TX3912_CLK_CTRL_SIBMCLKDIV_MASK) - | TX3912_CLK_CTRL_SIBMCLKDIR - | TX3912_CLK_CTRL_ENSIBMCLK - | TX3912_CLK_CTRL_CSERSEL; - outl(scratch, TX3912_CLK_CTRL_BASE); - - /* Configure UARTA clock */ - scratch = inl(TX3912_CLK_CTRL_BASE); - scratch |= ((0x3 << TX3912_CLK_CTRL_CSERDIV_SHIFT) & - TX3912_CLK_CTRL_CSERDIV_MASK) - | TX3912_CLK_CTRL_ENCSERCLK - | TX3912_CLK_CTRL_ENUARTACLK; - outl(scratch, TX3912_CLK_CTRL_BASE); - - /* Setup UARTA for 115200,8N1 */ - outl(0, TX3912_UARTA_BASE + TX3912_UART_CTRL1); - outl(TX3912_UART_CTRL2_B115200, TX3912_UARTA_BASE + TX3912_UART_CTRL2); - outl(0, TX3912_UARTA_BASE + TX3912_UART_DMA_CTRL1); - outl(0, TX3912_UARTA_BASE + TX3912_UART_DMA_CTRL2); - - /* Enable UARTA */ - outl(TX3912_UART_CTRL1_ENUART, TX3912_UARTA_BASE + TX3912_UART_CTRL1); - while (~inl(TX3912_UARTA_BASE + TX3912_UART_CTRL1) & - TX3912_UART_CTRL1_UARTON); -} -#endif - - /* Note: I didn't do anything to enable the second UART */ - if (rc >= 0) - rs_initialized++; - - func_exit(); -} -module_init(tx3912_rs_init); - -/* - * Begin serial console routines - */ -#ifdef CONFIG_SERIAL_TX3912_CONSOLE - -void serial_outc(unsigned char c) -{ - int i; - unsigned long int2; - #define BUSY_WAIT 10000 - - /* - * Turn UARTA interrupts off - */ - int2 = IntEnable2; - IntEnable2 &= - ~(INT2_UARTATXINT | INT2_UARTATXOVERRUN | INT2_UARTAEMPTY); - - /* - * The UART_TX_EMPTY bit in UartA_Ctrl1 seems - * not to be very reliable :-( - * - * Wait for the Tx register to become empty - */ - for (i = 0; !(IntStatus2 & INT2_UARTATXINT) && (i < BUSY_WAIT); i++); - - IntClear2 = INT2_UARTATXINT | INT2_UARTATXOVERRUN | INT2_UARTAEMPTY; - UartA_Data = c; - for (i = 0; !(IntStatus2 & INT2_UARTATXINT) && (i < BUSY_WAIT); i++); - IntClear2 = INT2_UARTATXINT | INT2_UARTATXOVERRUN | INT2_UARTAEMPTY; - - IntEnable2 = int2; -} - -static void serial_console_write(struct console *co, const char *s, - unsigned count) -{ - unsigned int i; - - for (i = 0; i < count; i++) { - if (*s == '\n') - serial_outc('\r'); - serial_outc(*s++); - } -} - -static struct tty_driver *serial_console_device(struct console *c, int *index) -{ - *index = c->index; - return rs_driver; -} - -static __init int serial_console_setup(struct console *co, char *options) -{ - unsigned int scratch = 0; - - /* Setup master clock for UART */ - scratch = inl(TX3912_CLK_CTRL_BASE); - scratch &= ~TX3912_CLK_CTRL_SIBMCLKDIV_MASK; - scratch |= ((0x2 << TX3912_CLK_CTRL_SIBMCLKDIV_SHIFT) & - TX3912_CLK_CTRL_SIBMCLKDIV_MASK) - | TX3912_CLK_CTRL_SIBMCLKDIR - | TX3912_CLK_CTRL_ENSIBMCLK - | TX3912_CLK_CTRL_CSERSEL; - outl(scratch, TX3912_CLK_CTRL_BASE); - - /* Configure UARTA clock */ - scratch = inl(TX3912_CLK_CTRL_BASE); - scratch |= ((0x3 << TX3912_CLK_CTRL_CSERDIV_SHIFT) & - TX3912_CLK_CTRL_CSERDIV_MASK) - | TX3912_CLK_CTRL_ENCSERCLK - | TX3912_CLK_CTRL_ENUARTACLK; - outl(scratch, TX3912_CLK_CTRL_BASE); - - /* Setup UARTA for 115200,8N1 */ - outl(0, TX3912_UARTA_BASE + TX3912_UART_CTRL1); - outl(TX3912_UART_CTRL2_B115200, TX3912_UARTA_BASE + TX3912_UART_CTRL2); - outl(0, TX3912_UARTA_BASE + TX3912_UART_DMA_CTRL1); - outl(0, TX3912_UARTA_BASE + TX3912_UART_DMA_CTRL2); - - /* Enable UARTA */ - outl(TX3912_UART_CTRL1_ENUART, TX3912_UARTA_BASE + TX3912_UART_CTRL1); - while (~inl(TX3912_UARTA_BASE + TX3912_UART_CTRL1) & - TX3912_UART_CTRL1_UARTON); - - return 0; -} - -static struct console sercons = { - .name = "ttyS", - .write = serial_console_write, - .device = serial_console_device, - .setup = serial_console_setup, - .flags = CON_PRINTBUFFER, - .index = -1 -}; - -static int __init tx3912_console_init(void) -{ - register_console(&sercons); - return 0; -} -console_initcall(tx3912_console_init); - -#endif diff -L drivers/char/serial_tx3912.h -puN drivers/char/serial_tx3912.h~mips-txx9-serieal-driver-rewrite /dev/null --- 25/drivers/char/serial_tx3912.h +++ /dev/null 2003-09-15 06:40:47.000000000 -0700 @@ -1,92 +0,0 @@ -/* - * drivers/char/serial_tx3912.h - * - * Copyright (C) 1999 Harald Koerfgen - * Copyright (C) 2000 Jim Pick - * Copyright (C) 2001 Steven J. Hill (sjhill@realitydiluted.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Serial driver for TMPR3912/05 and PR31700 processors - */ -#include -#include - -/* UART Interrupt (Interrupt 2) bits (UARTA,UARTB) */ -#define UART_RX_INT 9 /* receiver holding register full (31, 21) */ -#define UART_RXOVERRUN_INT 8 /* receiver overrun error (30, 20) */ -#define UART_FRAMEERR_INT 7 /* receiver frame error (29, 19) */ -#define UART_BREAK_INT 6 /* received break signal (28, 18) */ -#define UART_PARITYERR_INT 5 /* receiver parity error (27, 17) */ -#define UART_TX_INT 4 /* transmit holding register empty (26, 16) */ -#define UART_TXOVERRUN_INT 3 /* transmit overrun error (25, 15) */ -#define UART_EMPTY_INT 2 /* both trans/recv regs empty (24, 14) */ -#define UART_DMAFULL_INT 1 /* DMA at end of buffer (23, 13) */ -#define UART_DMAHALF_INT 0 /* DMA halfway through buffer (22, 12) */ - -#define UARTA_SHIFT 22 -#define UARTB_SHIFT 12 - -#define INTTYPE(interrupttype) (1 << interrupttype) - -/* - * This driver can spew a whole lot of debugging output at you. If you - * need maximum performance, you should disable the DEBUG define. - */ -#undef TX3912_UART_DEBUG - -#ifdef TX3912_UART_DEBUG -#define TX3912_UART_DEBUG_OPEN 0x00000001 -#define TX3912_UART_DEBUG_SETTING 0x00000002 -#define TX3912_UART_DEBUG_FLOW 0x00000004 -#define TX3912_UART_DEBUG_MODEMSIGNALS 0x00000008 -#define TX3912_UART_DEBUG_TERMIOS 0x00000010 -#define TX3912_UART_DEBUG_TRANSMIT 0x00000020 -#define TX3912_UART_DEBUG_RECEIVE 0x00000040 -#define TX3912_UART_DEBUG_INTERRUPTS 0x00000080 -#define TX3912_UART_DEBUG_PROBE 0x00000100 -#define TX3912_UART_DEBUG_INIT 0x00000200 -#define TX3912_UART_DEBUG_CLEANUP 0x00000400 -#define TX3912_UART_DEBUG_CLOSE 0x00000800 -#define TX3912_UART_DEBUG_FIRMWARE 0x00001000 -#define TX3912_UART_DEBUG_MEMTEST 0x00002000 -#define TX3912_UART_DEBUG_THROTTLE 0x00004000 -#define TX3912_UART_DEBUG_ALL 0xffffffff - -int rs_debug = TX3912_UART_DEBUG_ALL & ~TX3912_UART_DEBUG_TRANSMIT; - -#define rs_dprintk(f, str...) if (rs_debug & f) printk (str) -#define func_enter() rs_dprintk (TX3912_UART_DEBUG_FLOW, \ - "rs: enter %s\n", __FUNCTION__) -#define func_exit() rs_dprintk (TX3912_UART_DEBUG_FLOW, \ - "rs: exit %s\n", __FUNCTION__) - -#else -#define rs_dprintk(f, str...) -#define func_enter() -#define func_exit() - -#endif /* TX3912_UART_DEBUG */ - -/* - * Number of serial ports - */ -#define TX3912_UART_NPORTS 2 - -/* - * Hardware specific serial port structure - */ -struct rs_port { - struct gs_port gs; /* Must be first field! */ - - unsigned long base; - int intshift; /* Register shift */ - struct wait_queue *shutdown_wait; - int stat_flags; - struct async_icount icount; /* Counters for 4 input IRQs */ - int read_status_mask; - int ignore_status_mask; - int x_char; /* XON/XOFF character */ -}; diff -puN drivers/serial/Kconfig~mips-txx9-serieal-driver-rewrite drivers/serial/Kconfig --- 25/drivers/serial/Kconfig~mips-txx9-serieal-driver-rewrite 2005-01-29 11:25:59.133879592 -0800 +++ 25-akpm/drivers/serial/Kconfig 2005-01-29 11:25:59.141878376 -0800 @@ -792,4 +792,18 @@ config SERIAL_M32R_PLDSIO If you use an M3T-M32700UT or an OPSPUT platform, please say Y. +config SERIAL_TXX9 + bool "TMPTX39XX/49XX SIO support" + depends on MIPS || PCI + select SERIAL_CORE + +config SERIAL_TXX9_CONSOLE + bool "TMPTX39XX/49XX SIO Console support" + depends on SERIAL_TXX9=y + select SERIAL_CORE_CONSOLE + +config SERIAL_TXX9_STDSERIAL + bool "TX39XX/49XX SIO act as standard serial" + depends on !SERIAL_8250 && SERIAL_TXX9 + endmenu diff -puN /dev/null drivers/serial/serial_txx9.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/drivers/serial/serial_txx9.c 2005-01-29 11:25:59.147877464 -0800 @@ -0,0 +1,1171 @@ +/* + * drivers/serial/serial_txx9.c + * + * Derived from many drivers using generic_serial interface, + * especially serial_tx3912.c by Steven J. Hill and r39xx_serial.c + * (was in Linux/VR tree) by Jim Pick. + * + * Copyright (C) 1999 Harald Koerfgen + * Copyright (C) 2000 Jim Pick + * Copyright (C) 2001 Steven J. Hill (sjhill@realitydiluted.com) + * Copyright (C) 2000-2002 Toshiba Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Serial driver for TX3927/TX4927/TX4925/TX4938 internal SIO controller + * + * Revision History: + * 0.30 Initial revision. (Renamed from serial_txx927.c) + * 0.31 Use save_flags instead of local_irq_save. + * 0.32 Support SCLK. + * 0.33 Switch TXX9_TTY_NAME by CONFIG_SERIAL_TXX9_STDSERIAL. + * Support TIOCSERGETLSR. + * 0.34 Support slow baudrate. + * 0.40 Merge codes from mainstream kernel (2.4.22). + * 0.41 Fix console checking in rs_shutdown_port(). + * Disable flow-control in serial_console_write(). + * 0.42 Fix minor compiler warning. + * 1.00 Kernel 2.6. Converted to new serial core (based on 8250.c). + * 1.01 Set fifosize to make tx_empry called properly. + * Use standard uart_get_divisor. + * 1.02 Cleanup. (import 8250.c changes) + */ +#include + +#if defined(CONFIG_SERIAL_TXX9_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static char *serial_version = "1.02"; +static char *serial_name = "TX39/49 Serial driver"; + +#define PASS_LIMIT 256 + +#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 + +/* flag aliases */ +#define UPF_TXX9_HAVE_CTS_LINE UPF_BUGGY_UART +#define UPF_TXX9_USE_SCLK UPF_MAGIC_MULTIPLIER + +#ifdef CONFIG_PCI +/* support for Toshiba TC86C001 SIO */ +#define ENABLE_SERIAL_TXX9_PCI +#endif + +/* + * Number of serial ports + */ +#ifdef ENABLE_SERIAL_TXX9_PCI +#define NR_PCI_BOARDS 4 +#define UART_NR (2 + NR_PCI_BOARDS) +#else +#define UART_NR 2 +#endif + +struct uart_txx9_port { + struct uart_port port; + + /* + * We provide a per-port pm hook. + */ + void (*pm)(struct uart_port *port, + unsigned int state, unsigned int old); +}; + +#define TXX9_REGION_SIZE 0x24 + +/* TXX9 Serial Registers */ +#define TXX9_SILCR 0x00 +#define TXX9_SIDICR 0x04 +#define TXX9_SIDISR 0x08 +#define TXX9_SICISR 0x0c +#define TXX9_SIFCR 0x10 +#define TXX9_SIFLCR 0x14 +#define TXX9_SIBGR 0x18 +#define TXX9_SITFIFO 0x1c +#define TXX9_SIRFIFO 0x20 + +/* SILCR : Line Control */ +#define TXX9_SILCR_SCS_MASK 0x00000060 +#define TXX9_SILCR_SCS_IMCLK 0x00000000 +#define TXX9_SILCR_SCS_IMCLK_BG 0x00000020 +#define TXX9_SILCR_SCS_SCLK 0x00000040 +#define TXX9_SILCR_SCS_SCLK_BG 0x00000060 +#define TXX9_SILCR_UEPS 0x00000010 +#define TXX9_SILCR_UPEN 0x00000008 +#define TXX9_SILCR_USBL_MASK 0x00000004 +#define TXX9_SILCR_USBL_1BIT 0x00000000 +#define TXX9_SILCR_USBL_2BIT 0x00000004 +#define TXX9_SILCR_UMODE_MASK 0x00000003 +#define TXX9_SILCR_UMODE_8BIT 0x00000000 +#define TXX9_SILCR_UMODE_7BIT 0x00000001 + +/* SIDICR : DMA/Int. Control */ +#define TXX9_SIDICR_TDE 0x00008000 +#define TXX9_SIDICR_RDE 0x00004000 +#define TXX9_SIDICR_TIE 0x00002000 +#define TXX9_SIDICR_RIE 0x00001000 +#define TXX9_SIDICR_SPIE 0x00000800 +#define TXX9_SIDICR_CTSAC 0x00000600 +#define TXX9_SIDICR_STIE_MASK 0x0000003f +#define TXX9_SIDICR_STIE_OERS 0x00000020 +#define TXX9_SIDICR_STIE_CTSS 0x00000010 +#define TXX9_SIDICR_STIE_RBRKD 0x00000008 +#define TXX9_SIDICR_STIE_TRDY 0x00000004 +#define TXX9_SIDICR_STIE_TXALS 0x00000002 +#define TXX9_SIDICR_STIE_UBRKD 0x00000001 + +/* SIDISR : DMA/Int. Status */ +#define TXX9_SIDISR_UBRK 0x00008000 +#define TXX9_SIDISR_UVALID 0x00004000 +#define TXX9_SIDISR_UFER 0x00002000 +#define TXX9_SIDISR_UPER 0x00001000 +#define TXX9_SIDISR_UOER 0x00000800 +#define TXX9_SIDISR_ERI 0x00000400 +#define TXX9_SIDISR_TOUT 0x00000200 +#define TXX9_SIDISR_TDIS 0x00000100 +#define TXX9_SIDISR_RDIS 0x00000080 +#define TXX9_SIDISR_STIS 0x00000040 +#define TXX9_SIDISR_RFDN_MASK 0x0000001f + +/* SICISR : Change Int. Status */ +#define TXX9_SICISR_OERS 0x00000020 +#define TXX9_SICISR_CTSS 0x00000010 +#define TXX9_SICISR_RBRKD 0x00000008 +#define TXX9_SICISR_TRDY 0x00000004 +#define TXX9_SICISR_TXALS 0x00000002 +#define TXX9_SICISR_UBRKD 0x00000001 + +/* SIFCR : FIFO Control */ +#define TXX9_SIFCR_SWRST 0x00008000 +#define TXX9_SIFCR_RDIL_MASK 0x00000180 +#define TXX9_SIFCR_RDIL_1 0x00000000 +#define TXX9_SIFCR_RDIL_4 0x00000080 +#define TXX9_SIFCR_RDIL_8 0x00000100 +#define TXX9_SIFCR_RDIL_12 0x00000180 +#define TXX9_SIFCR_RDIL_MAX 0x00000180 +#define TXX9_SIFCR_TDIL_MASK 0x00000018 +#define TXX9_SIFCR_TDIL_MASK 0x00000018 +#define TXX9_SIFCR_TDIL_1 0x00000000 +#define TXX9_SIFCR_TDIL_4 0x00000001 +#define TXX9_SIFCR_TDIL_8 0x00000010 +#define TXX9_SIFCR_TDIL_MAX 0x00000010 +#define TXX9_SIFCR_TFRST 0x00000004 +#define TXX9_SIFCR_RFRST 0x00000002 +#define TXX9_SIFCR_FRSTE 0x00000001 +#define TXX9_SIO_TX_FIFO 8 +#define TXX9_SIO_RX_FIFO 16 + +/* SIFLCR : Flow Control */ +#define TXX9_SIFLCR_RCS 0x00001000 +#define TXX9_SIFLCR_TES 0x00000800 +#define TXX9_SIFLCR_RTSSC 0x00000200 +#define TXX9_SIFLCR_RSDE 0x00000100 +#define TXX9_SIFLCR_TSDE 0x00000080 +#define TXX9_SIFLCR_RTSTL_MASK 0x0000001e +#define TXX9_SIFLCR_RTSTL_MAX 0x0000001e +#define TXX9_SIFLCR_TBRK 0x00000001 + +/* SIBGR : Baudrate Control */ +#define TXX9_SIBGR_BCLK_MASK 0x00000300 +#define TXX9_SIBGR_BCLK_T0 0x00000000 +#define TXX9_SIBGR_BCLK_T2 0x00000100 +#define TXX9_SIBGR_BCLK_T4 0x00000200 +#define TXX9_SIBGR_BCLK_T6 0x00000300 +#define TXX9_SIBGR_BRD_MASK 0x000000ff + +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); + case UPIO_PORT: + return inl(up->port.iobase + offset); + } +} + +static inline void +sio_out(struct uart_txx9_port *up, int offset, int value) +{ + switch (up->port.iotype) { + default: + *(volatile u32 *)(up->port.membase + offset) = value; + break; + case UPIO_PORT: + outl(value, up->port.iobase + offset); + break; + } +} + +static inline void +sio_mask(struct uart_txx9_port *up, int offset, unsigned int value) +{ + sio_out(up, offset, sio_in(up, offset) & ~value); +} +static inline void +sio_set(struct uart_txx9_port *up, int offset, unsigned int value) +{ + sio_out(up, offset, sio_in(up, offset) | value); +} + +static inline void +sio_quot_set(struct uart_txx9_port *up, int quot) +{ + quot >>= 1; + if (quot < 256) + sio_out(up, TXX9_SIBGR, quot | TXX9_SIBGR_BCLK_T0); + else if (quot < (256 << 2)) + sio_out(up, TXX9_SIBGR, (quot >> 2) | TXX9_SIBGR_BCLK_T2); + else if (quot < (256 << 4)) + sio_out(up, TXX9_SIBGR, (quot >> 4) | TXX9_SIBGR_BCLK_T4); + else if (quot < (256 << 6)) + sio_out(up, TXX9_SIBGR, (quot >> 6) | TXX9_SIBGR_BCLK_T6); + else + sio_out(up, TXX9_SIBGR, 0xff | TXX9_SIBGR_BCLK_T6); +} + +static void serial_txx9_stop_tx(struct uart_port *port, unsigned int tty_stop) +{ + 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, unsigned int tty_start) +{ + 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) +{ + /* TXX9-SIO can not control DTR... */ +} + +static inline void +receive_chars(struct uart_txx9_port *up, unsigned int *status, struct pt_regs *regs) +{ + struct tty_struct *tty = up->port.info->tty; + unsigned char ch; + unsigned int disr = *status; + int max_count = 256; + char flag; + + do { + /* The following is not allowed by the tty layer and + unsafe. It should be fixed ASAP */ + if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) { + if(tty->low_latency) + tty_flip_buffer_push(tty); + /* If this failed then we will throw away the + bytes but must do so to clear interrupts */ + } + ch = sio_in(up, TXX9_SIRFIFO); + flag = TTY_NORMAL; + up->port.icount.rx++; + + if (unlikely(disr & (TXX9_SIDISR_UBRK | TXX9_SIDISR_UPER | + TXX9_SIDISR_UFER | TXX9_SIDISR_UOER))) { + /* + * For statistics only + */ + if (disr & TXX9_SIDISR_UBRK) { + disr &= ~(TXX9_SIDISR_UFER | TXX9_SIDISR_UPER); + up->port.icount.brk++; + /* + * We do the SysRQ and SAK checking + * here because otherwise the break + * may get masked by ignore_status_mask + * or read_status_mask. + */ + if (uart_handle_break(&up->port)) + goto ignore_char; + } else if (disr & TXX9_SIDISR_UPER) + up->port.icount.parity++; + else if (disr & TXX9_SIDISR_UFER) + up->port.icount.frame++; + if (disr & TXX9_SIDISR_UOER) + up->port.icount.overrun++; + + /* + * Mask off conditions which should be ingored. + */ + disr &= up->port.read_status_mask; + + if (disr & TXX9_SIDISR_UBRK) { + flag = TTY_BREAK; + } else if (disr & TXX9_SIDISR_UPER) + flag = TTY_PARITY; + else if (disr & TXX9_SIDISR_UFER) + flag = TTY_FRAME; + } + if (uart_handle_sysrq_char(&up->port, ch, regs)) + goto ignore_char; + if ((disr & up->port.ignore_status_mask) == 0) { + tty_insert_flip_char(tty, ch, flag); + } + if ((disr & TXX9_SIDISR_UOER) && + tty->flip.count < TTY_FLIPBUF_SIZE) { + /* + * Overrun is special, since it's reported + * immediately, and doesn't affect the current + * character. + */ + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + } + ignore_char: + disr = sio_in(up, TXX9_SIDISR); + } while (!(disr & TXX9_SIDISR_UVALID) && (max_count-- > 0)); + tty_flip_buffer_push(tty); + *status = disr; +} + +static inline void transmit_chars(struct uart_txx9_port *up) +{ + struct circ_buf *xmit = &up->port.info->xmit; + int count; + + if (up->port.x_char) { + sio_out(up, TXX9_SITFIFO, up->port.x_char); + up->port.icount.tx++; + up->port.x_char = 0; + return; + } + if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) { + serial_txx9_stop_tx(&up->port, 0); + return; + } + + count = TXX9_SIO_TX_FIFO; + do { + sio_out(up, TXX9_SITFIFO, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + up->port.icount.tx++; + if (uart_circ_empty(xmit)) + break; + } while (--count > 0); + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&up->port); + + if (uart_circ_empty(xmit)) + serial_txx9_stop_tx(&up->port, 0); +} + +static irqreturn_t serial_txx9_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + int pass_counter = 0; + struct uart_txx9_port *up = dev_id; + unsigned int status; + + while (1) { + spin_lock(&up->port.lock); + status = sio_in(up, TXX9_SIDISR); + if (!(sio_in(up, TXX9_SIDICR) & TXX9_SIDICR_TIE)) + status &= ~TXX9_SIDISR_TDIS; + if (!(status & (TXX9_SIDISR_TDIS | TXX9_SIDISR_RDIS | + TXX9_SIDISR_TOUT))) { + spin_unlock(&up->port.lock); + break; + } + + if (status & TXX9_SIDISR_RDIS) + receive_chars(up, &status, regs); + if (status & TXX9_SIDISR_TDIS) + transmit_chars(up); + /* Clear TX/RX Int. Status */ + sio_mask(up, TXX9_SIDISR, + TXX9_SIDISR_TDIS | TXX9_SIDISR_RDIS | + TXX9_SIDISR_TOUT); + spin_unlock(&up->port.lock); + + if (pass_counter++ > PASS_LIMIT) + break; + } + + return pass_counter ? IRQ_HANDLED : IRQ_NONE; +} + +static unsigned int serial_txx9_tx_empty(struct uart_port *port) +{ + struct uart_txx9_port *up = (struct uart_txx9_port *)port; + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(&up->port.lock, flags); + ret = (sio_in(up, TXX9_SICISR) & TXX9_SICISR_TXALS) ? TIOCSER_TEMT : 0; + spin_unlock_irqrestore(&up->port.lock, flags); + + return ret; +} + +static unsigned int serial_txx9_get_mctrl(struct uart_port *port) +{ + struct uart_txx9_port *up = (struct uart_txx9_port *)port; + unsigned long flags; + unsigned int ret; + + spin_lock_irqsave(&up->port.lock, flags); + ret = ((sio_in(up, TXX9_SIFLCR) & TXX9_SIFLCR_RTSSC) ? 0 : TIOCM_RTS) + | ((sio_in(up, TXX9_SICISR) & TXX9_SICISR_CTSS) ? 0 : TIOCM_CTS); + spin_unlock_irqrestore(&up->port.lock, flags); + + return ret; +} + +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) +{ + struct uart_txx9_port *up = (struct uart_txx9_port *)port; + unsigned long flags; + + spin_lock_irqsave(&up->port.lock, flags); + if (break_state == -1) + sio_set(up, TXX9_SIFLCR, TXX9_SIFLCR_TBRK); + else + sio_mask(up, TXX9_SIFLCR, TXX9_SIFLCR_TBRK); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static int serial_txx9_startup(struct uart_port *port) +{ + struct uart_txx9_port *up = (struct uart_txx9_port *)port; + unsigned long flags; + int retval; + + /* + * Clear the FIFO buffers and disable them. + * (they will be reeanbled in set_termios()) + */ + sio_set(up, TXX9_SIFCR, + TXX9_SIFCR_TFRST | TXX9_SIFCR_RFRST | TXX9_SIFCR_FRSTE); + /* clear reset */ + sio_mask(up, TXX9_SIFCR, + TXX9_SIFCR_TFRST | TXX9_SIFCR_RFRST | TXX9_SIFCR_FRSTE); + sio_out(up, TXX9_SIDICR, 0); + + /* + * Clear the interrupt registers. + */ + sio_out(up, TXX9_SIDISR, 0); + + retval = request_irq(up->port.irq, serial_txx9_interrupt, + SA_SHIRQ, "serial_txx9", up); + if (retval) + return retval; + + /* + * Now, initialize the UART + */ + spin_lock_irqsave(&up->port.lock, flags); + serial_txx9_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); + + /* Enable RX/TX */ + sio_mask(up, TXX9_SIFLCR, TXX9_SIFLCR_RSDE | TXX9_SIFLCR_TSDE); + + /* + * Finally, enable interrupts. + */ + sio_set(up, TXX9_SIDICR, TXX9_SIDICR_RIE); + + return 0; +} + +static void serial_txx9_shutdown(struct uart_port *port) +{ + struct uart_txx9_port *up = (struct uart_txx9_port *)port; + unsigned long flags; + + /* + * Disable interrupts from this port + */ + sio_out(up, TXX9_SIDICR, 0); /* disable all intrs */ + + spin_lock_irqsave(&up->port.lock, flags); + serial_txx9_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); + + /* + * Disable break condition + */ + sio_mask(up, TXX9_SIFLCR, TXX9_SIFLCR_TBRK); + +#ifdef CONFIG_SERIAL_TXX9_CONSOLE + if (up->port.cons && up->port.line == up->port.cons->index) { + free_irq(up->port.irq, up); + return; + } +#endif + /* reset FIFOs */ + sio_set(up, TXX9_SIFCR, + TXX9_SIFCR_TFRST | TXX9_SIFCR_RFRST | TXX9_SIFCR_FRSTE); + /* clear reset */ + sio_mask(up, TXX9_SIFCR, + TXX9_SIFCR_TFRST | TXX9_SIFCR_RFRST | TXX9_SIFCR_FRSTE); + + /* Disable RX/TX */ + sio_set(up, TXX9_SIFLCR, TXX9_SIFLCR_RSDE | TXX9_SIFLCR_TSDE); + + free_irq(up->port.irq, up); +} + +static void +serial_txx9_set_termios(struct uart_port *port, struct termios *termios, + struct termios *old) +{ + struct uart_txx9_port *up = (struct uart_txx9_port *)port; + unsigned int cval, fcr = 0; + unsigned long flags; + unsigned int baud, quot; + + cval = sio_in(up, TXX9_SILCR); + /* byte size and parity */ + cval &= ~TXX9_SILCR_UMODE_MASK; + switch (termios->c_cflag & CSIZE) { + case CS7: + cval |= TXX9_SILCR_UMODE_7BIT; + break; + default: + case CS5: /* not supported */ + case CS6: /* not supported */ + case CS8: + cval |= TXX9_SILCR_UMODE_8BIT; + break; + } + + cval &= ~TXX9_SILCR_USBL_MASK; + if (termios->c_cflag & CSTOPB) + cval |= TXX9_SILCR_USBL_2BIT; + else + cval |= TXX9_SILCR_USBL_1BIT; + cval &= ~(TXX9_SILCR_UPEN | TXX9_SILCR_UEPS); + if (termios->c_cflag & PARENB) + cval |= TXX9_SILCR_UPEN; + if (!(termios->c_cflag & PARODD)) + cval |= TXX9_SILCR_UEPS; + + /* + * Ask the core to calculate the divisor for us. + */ + baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16/2); + quot = uart_get_divisor(port, baud); + + /* Set up FIFOs */ + /* TX Int by FIFO Empty, RX Int by Receiving 1 char. */ + fcr = TXX9_SIFCR_TDIL_MAX | TXX9_SIFCR_RDIL_1; + + /* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + spin_lock_irqsave(&up->port.lock, flags); + + /* + * Update the per-port timeout. + */ + uart_update_timeout(port, termios->c_cflag, baud); + + up->port.read_status_mask = TXX9_SIDISR_UOER | + TXX9_SIDISR_TDIS | TXX9_SIDISR_RDIS; + if (termios->c_iflag & INPCK) + up->port.read_status_mask |= TXX9_SIDISR_UFER | TXX9_SIDISR_UPER; + if (termios->c_iflag & (BRKINT | PARMRK)) + up->port.read_status_mask |= TXX9_SIDISR_UBRK; + + /* + * Characteres to ignore + */ + up->port.ignore_status_mask = 0; + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= TXX9_SIDISR_UPER | TXX9_SIDISR_UFER; + if (termios->c_iflag & IGNBRK) { + up->port.ignore_status_mask |= TXX9_SIDISR_UBRK; + /* + * If we're ignoring parity and break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) + up->port.ignore_status_mask |= TXX9_SIDISR_UOER; + } + + /* + * ignore all characters if CREAD is not set + */ + if ((termios->c_cflag & CREAD) == 0) + up->port.ignore_status_mask |= TXX9_SIDISR_RDIS; + + /* CTS flow control flag */ + if ((termios->c_cflag & CRTSCTS) && + (up->port.flags & UPF_TXX9_HAVE_CTS_LINE)) { + sio_set(up, TXX9_SIFLCR, + TXX9_SIFLCR_RCS | TXX9_SIFLCR_TES); + } else { + sio_mask(up, TXX9_SIFLCR, + TXX9_SIFLCR_RCS | TXX9_SIFLCR_TES); + } + + sio_out(up, TXX9_SILCR, cval); + sio_quot_set(up, quot); + sio_out(up, TXX9_SIFCR, fcr); + + serial_txx9_set_mctrl(&up->port, up->port.mctrl); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static void +serial_txx9_pm(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct uart_txx9_port *up = (struct uart_txx9_port *)port; + if (state) { + /* sleep */ + + if (up->pm) + up->pm(port, state, oldstate); + } else { + /* wake */ + + if (up->pm) + up->pm(port, state, oldstate); + } +} + +static int serial_txx9_request_resource(struct uart_txx9_port *up) +{ + unsigned int size = TXX9_REGION_SIZE; + int ret = 0; + + switch (up->port.iotype) { + default: + if (!up->port.mapbase) + break; + + if (!request_mem_region(up->port.mapbase, size, "serial_txx9")) { + ret = -EBUSY; + break; + } + + if (up->port.flags & UPF_IOREMAP) { + up->port.membase = ioremap(up->port.mapbase, size); + if (!up->port.membase) { + release_mem_region(up->port.mapbase, size); + ret = -ENOMEM; + } + } + break; + + case UPIO_PORT: + if (!request_region(up->port.iobase, size, "serial_txx9")) + ret = -EBUSY; + break; + } + return ret; +} + +static void serial_txx9_release_resource(struct uart_txx9_port *up) +{ + unsigned int size = TXX9_REGION_SIZE; + + switch (up->port.iotype) { + default: + if (!up->port.mapbase) + break; + + if (up->port.flags & UPF_IOREMAP) { + iounmap(up->port.membase); + up->port.membase = NULL; + } + + release_mem_region(up->port.mapbase, size); + break; + + case UPIO_PORT: + release_region(up->port.iobase, size); + break; + } +} + +static void serial_txx9_release_port(struct uart_port *port) +{ + struct uart_txx9_port *up = (struct uart_txx9_port *)port; + serial_txx9_release_resource(up); +} + +static int serial_txx9_request_port(struct uart_port *port) +{ + struct uart_txx9_port *up = (struct uart_txx9_port *)port; + return serial_txx9_request_resource(up); +} + +static void serial_txx9_config_port(struct uart_port *port, int uflags) +{ + struct uart_txx9_port *up = (struct uart_txx9_port *)port; + unsigned long flags; + int ret; + + /* + * Find the region that we can probe for. This in turn + * tells us whether we can probe for the type of port. + */ + ret = serial_txx9_request_resource(up); + if (ret < 0) + return; + port->type = PORT_TXX9; + up->port.fifosize = TXX9_SIO_TX_FIFO; + +#ifdef CONFIG_SERIAL_TXX9_CONSOLE + if (up->port.line == up->port.cons->index) + return; +#endif + spin_lock_irqsave(&up->port.lock, flags); + /* + * Reset the UART. + */ + sio_out(up, TXX9_SIFCR, TXX9_SIFCR_SWRST); +#ifdef CONFIG_CPU_TX49XX + /* TX4925 BUG WORKAROUND. Accessing SIOC register + * immediately after soft reset causes bus error. */ + iob(); + udelay(1); +#endif + while (sio_in(up, TXX9_SIFCR) & TXX9_SIFCR_SWRST) + ; + /* TX Int by FIFO Empty, RX Int by Receiving 1 char. */ + sio_set(up, TXX9_SIFCR, + TXX9_SIFCR_TDIL_MAX | TXX9_SIFCR_RDIL_1); + /* initial settings */ + sio_out(up, TXX9_SILCR, + TXX9_SILCR_UMODE_8BIT | TXX9_SILCR_USBL_1BIT | + ((up->port.flags & UPF_TXX9_USE_SCLK) ? + TXX9_SILCR_SCS_SCLK_BG : TXX9_SILCR_SCS_IMCLK_BG)); + sio_quot_set(up, uart_get_divisor(port, 9600)); + sio_out(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSTL_MAX /* 15 */); + spin_unlock_irqrestore(&up->port.lock, flags); +} + +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) + return -EINVAL; + return 0; +} + +static const char * +serial_txx9_type(struct uart_port *port) +{ + return "txx9"; +} + +static struct uart_ops serial_txx9_pops = { + .tx_empty = serial_txx9_tx_empty, + .set_mctrl = serial_txx9_set_mctrl, + .get_mctrl = serial_txx9_get_mctrl, + .stop_tx = serial_txx9_stop_tx, + .start_tx = serial_txx9_start_tx, + .stop_rx = serial_txx9_stop_rx, + .enable_ms = serial_txx9_enable_ms, + .break_ctl = serial_txx9_break_ctl, + .startup = serial_txx9_startup, + .shutdown = serial_txx9_shutdown, + .set_termios = serial_txx9_set_termios, + .pm = serial_txx9_pm, + .type = serial_txx9_type, + .release_port = serial_txx9_release_port, + .request_port = serial_txx9_request_port, + .config_port = serial_txx9_config_port, + .verify_port = serial_txx9_verify_port, +}; + +static struct uart_txx9_port serial_txx9_ports[UART_NR]; + +static void __init serial_txx9_register_ports(struct uart_driver *drv) +{ + int i; + + for (i = 0; i < UART_NR; i++) { + struct uart_txx9_port *up = &serial_txx9_ports[i]; + + up->port.line = i; + up->port.ops = &serial_txx9_pops; + uart_add_one_port(drv, &up->port); + } +} + +#ifdef CONFIG_SERIAL_TXX9_CONSOLE + +/* + * Wait for transmitter & holding register to empty + */ +static inline void wait_for_xmitr(struct uart_txx9_port *up) +{ + unsigned int tmout = 10000; + + /* Wait up to 10ms for the character(s) to be sent. */ + while (--tmout && + !(sio_in(up, TXX9_SICISR) & TXX9_SICISR_TXALS)) + udelay(1); + + /* Wait up to 1s for flow control if necessary */ + if (up->port.flags & UPF_CONS_FLOW) { + tmout = 1000000; + while (--tmout && + (sio_in(up, TXX9_SICISR) & TXX9_SICISR_CTSS)) + udelay(1); + } +} + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + * + * The console_lock must be held when we get here. + */ +static void +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 + */ + ier = sio_in(up, TXX9_SIDICR); + sio_out(up, TXX9_SIDICR, 0); + /* + * Disable flow-control if enabled (and unnecessary) + */ + flcr = sio_in(up, TXX9_SIFLCR); + 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); + } + } + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(up); + sio_out(up, TXX9_SIFLCR, flcr); + sio_out(up, TXX9_SIDICR, ier); +} + +static int serial_txx9_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + struct uart_txx9_port *up; + int baud = 9600; + int bits = 8; + int parity = 'n'; + int flow = 'n'; + + /* + * Check whether an invalid uart number has been specified, and + * if so, search for the first available port that does have + * console support. + */ + if (co->index >= UART_NR) + co->index = 0; + up = &serial_txx9_ports[co->index]; + port = &up->port; + if (!port->ops) + return -ENODEV; + + /* + * Temporary fix. + */ + spin_lock_init(&port->lock); + + /* + * Disable UART interrupts, set DTR and RTS high + * and set speed. + */ + sio_out(up, TXX9_SIDICR, 0); + /* initial settings */ + sio_out(up, TXX9_SILCR, + TXX9_SILCR_UMODE_8BIT | TXX9_SILCR_USBL_1BIT | + ((port->flags & UPF_TXX9_USE_SCLK) ? + TXX9_SILCR_SCS_SCLK_BG : TXX9_SILCR_SCS_IMCLK_BG)); + sio_out(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSTL_MAX /* 15 */); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver serial_txx9_reg; +static struct console serial_txx9_console = { + .name = TXX9_TTY_NAME, + .write = serial_txx9_console_write, + .device = uart_console_device, + .setup = serial_txx9_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &serial_txx9_reg, +}; + +static int __init serial_txx9_console_init(void) +{ + register_console(&serial_txx9_console); + return 0; +} +console_initcall(serial_txx9_console_init); + +static int __init serial_txx9_late_console_init(void) +{ + if (!(serial_txx9_console.flags & CON_ENABLED)) + register_console(&serial_txx9_console); + return 0; +} +late_initcall(serial_txx9_late_console_init); + +#define SERIAL_TXX9_CONSOLE &serial_txx9_console +#else +#define SERIAL_TXX9_CONSOLE NULL +#endif + +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, + .nr = UART_NR, + .cons = SERIAL_TXX9_CONSOLE, +}; + +int __init early_serial_txx9_setup(struct uart_port *port) +{ + if (port->line >= ARRAY_SIZE(serial_txx9_ports)) + return -ENODEV; + + serial_txx9_ports[port->line].port = *port; + serial_txx9_ports[port->line].port.ops = &serial_txx9_pops; + serial_txx9_ports[port->line].port.flags |= UPF_BOOT_AUTOCONF; + return 0; +} + +#ifdef ENABLE_SERIAL_TXX9_PCI +/** + * 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. + */ +static void serial_txx9_suspend_port(int line) +{ + uart_suspend_port(&serial_txx9_reg, &serial_txx9_ports[line].port); +} + +/** + * 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. + */ +static void serial_txx9_resume_port(int line) +{ + uart_resume_port(&serial_txx9_reg, &serial_txx9_ports[line].port); +} + +/* + * Probe one serial board. Unfortunately, there is no rhyme nor reason + * to the arrangement of serial ports on a PCI card. + */ +static int __devinit +pciserial_txx9_init_one(struct pci_dev *dev, const struct pci_device_id *ent) +{ + struct uart_port port; + int line; + int rc; + + rc = pci_enable_device(dev); + if (rc) + return rc; + + memset(&port, 0, sizeof(port)); + port.ops = &serial_txx9_pops; + port.flags |= UPF_BOOT_AUTOCONF; /* uart_ops.config_port will be called */ + port.flags |= UPF_TXX9_HAVE_CTS_LINE; + port.uartclk = 66670000; + port.irq = dev->irq; + port.iotype = UPIO_PORT; + port.iobase = pci_resource_start(dev, 1); + line = uart_register_port(&serial_txx9_reg, &port); + if (line < 0) { + printk(KERN_WARNING "Couldn't register serial port %s: %d\n", pci_name(dev), line); + } + pci_set_drvdata(dev, (void *)(long)line); + + return 0; +} + +static void __devexit pciserial_txx9_remove_one(struct pci_dev *dev) +{ + int line = (int)(long)pci_get_drvdata(dev); + + pci_set_drvdata(dev, NULL); + + if (line) { + uart_unregister_port(&serial_txx9_reg, line); + pci_disable_device(dev); + } +} + +static int pciserial_txx9_suspend_one(struct pci_dev *dev, u32 state) +{ + int line = (int)(long)pci_get_drvdata(dev); + + if (line) + serial_txx9_suspend_port(line); + return 0; +} + +static int pciserial_txx9_resume_one(struct pci_dev *dev) +{ + int line = (int)(long)pci_get_drvdata(dev); + + if (line) + serial_txx9_resume_port(line); + return 0; +} + +static struct pci_device_id serial_txx9_pci_tbl[] = { + { PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC86C001_MISC, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, 0 }, + { 0, } +}; + +static struct pci_driver serial_txx9_pci_driver = { + .name = "serial_txx9", + .probe = pciserial_txx9_init_one, + .remove = __devexit_p(pciserial_txx9_remove_one), + .suspend = pciserial_txx9_suspend_one, + .resume = pciserial_txx9_resume_one, + .id_table = serial_txx9_pci_tbl, +}; + +MODULE_DEVICE_TABLE(pci, serial_txx9_pci_tbl); +#endif /* ENABLE_SERIAL_TXX9_PCI */ + +static int __init serial_txx9_init(void) +{ + int ret; + + printk(KERN_INFO "%s version %s\n", serial_name, serial_version); + + ret = uart_register_driver(&serial_txx9_reg); + if (ret >= 0) { + serial_txx9_register_ports(&serial_txx9_reg); + +#ifdef ENABLE_SERIAL_TXX9_PCI + ret = pci_module_init(&serial_txx9_pci_driver); +#endif + } + return ret; +} + +static void __exit serial_txx9_exit(void) +{ + int i; + +#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); + + uart_unregister_driver(&serial_txx9_reg); +} + +module_init(serial_txx9_init); +module_exit(serial_txx9_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("TX39/49 serial driver"); + +MODULE_ALIAS_CHARDEV_MAJOR(TXX9_TTY_MAJOR); diff -puN include/linux/pci_ids.h~mips-txx9-serieal-driver-rewrite include/linux/pci_ids.h --- 25/include/linux/pci_ids.h~mips-txx9-serieal-driver-rewrite 2005-01-29 11:25:59.135879288 -0800 +++ 25-akpm/include/linux/pci_ids.h 2005-01-29 11:25:59.150877008 -0800 @@ -1473,6 +1473,7 @@ #define PCI_DEVICE_ID_TOSHIBA_TX3927 0x000a #define PCI_DEVICE_ID_TOSHIBA_TC35815CF 0x0030 #define PCI_DEVICE_ID_TOSHIBA_TX4927 0x0180 +#define PCI_DEVICE_ID_TOSHIBA_TC86C001_MISC 0x0108 #define PCI_VENDOR_ID_RICOH 0x1180 #define PCI_DEVICE_ID_RICOH_RL5C465 0x0465 diff -puN include/linux/serial_core.h~mips-txx9-serieal-driver-rewrite include/linux/serial_core.h --- 25/include/linux/serial_core.h~mips-txx9-serieal-driver-rewrite 2005-01-29 11:25:59.137878984 -0800 +++ 25-akpm/include/linux/serial_core.h 2005-01-29 11:25:59.147877464 -0800 @@ -103,6 +103,9 @@ /* Marvell MPSC */ #define PORT_MPSC 63 +/* TXX9 type number */ +#define PORT_TXX9 64 + #ifdef __KERNEL__ #include _