summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRalf Bächle <ralf@linux-mips.org>2005-02-01 16:43:17 -0800
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-02-01 16:43:17 -0800
commitae6e171969d62f809a64203374db3bfcfaa7c71f (patch)
tree976ff4de3d8805f4a50ef1afcb6706dfaa2b2b84
parentdb3a8f4674d7fad32157353e59c300c30b4c3ef5 (diff)
[PATCH] mips: TXX9 serieal driver rewrite
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 <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--drivers/char/Kconfig16
-rw-r--r--drivers/char/Makefile1
-rw-r--r--drivers/char/serial_tx3912.c981
-rw-r--r--drivers/char/serial_tx3912.h92
-rw-r--r--drivers/serial/Kconfig14
-rw-r--r--drivers/serial/serial_txx9.c1171
-rw-r--r--include/linux/pci_ids.h1
-rw-r--r--include/linux/serial_core.h3
8 files changed, 1189 insertions, 1090 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 5cc7863a681a..e2579ef90462 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -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 <http://www.toshiba.com/taec/components/Generic/risc/tx3912.htm>.
- 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 <http://www.toshiba.com/taec/components/Generic/risc/tx3912.htm>.
- 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 --git a/drivers/char/Makefile b/drivers/char/Makefile
index 1cbbaabda434..a68bd73dbd89 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -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 --git a/drivers/char/serial_tx3912.c b/drivers/char/serial_tx3912.c
deleted file mode 100644
index 50ed6dfec03d..000000000000
--- a/drivers/char/serial_tx3912.c
+++ /dev/null
@@ -1,981 +0,0 @@
-/*
- * drivers/char/serial_tx3912.c
- *
- * Copyright (C) 1999 Harald Koerfgen
- * Copyright (C) 2000 Jim Pick <jim@jimpick.com>
- * 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 <linux/init.h>
-#include <linux/config.h>
-#include <linux/tty.h>
-#include <linux/major.h>
-#include <linux/ptrace.h>
-#include <linux/console.h>
-#include <linux/fs.h>
-#include <linux/mm.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/pm.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <asm/delay.h>
-#include <asm/wbflush.h>
-#include <asm/tx3912.h>
-#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 --git a/drivers/char/serial_tx3912.h b/drivers/char/serial_tx3912.h
deleted file mode 100644
index 73e0b4f23c80..000000000000
--- a/drivers/char/serial_tx3912.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * drivers/char/serial_tx3912.h
- *
- * Copyright (C) 1999 Harald Koerfgen
- * Copyright (C) 2000 Jim Pick <jim@jimpick.com>
- * 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 <linux/serialP.h>
-#include <linux/generic_serial.h>
-
-/* 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 --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 4326ec67c194..51c51205538c 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -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 --git a/drivers/serial/serial_txx9.c b/drivers/serial/serial_txx9.c
new file mode 100644
index 000000000000..dfc987301f17
--- /dev/null
+++ b/drivers/serial/serial_txx9.c
@@ -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 <jim@jimpick.com>
+ * 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 <linux/config.h>
+
+#if defined(CONFIG_SERIAL_TXX9_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/pci.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+
+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 --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index b434d33f6144..65fe9d45f891 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -1469,6 +1469,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 --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 84d80cfe7b41..c45598d0a846 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -103,6 +103,9 @@
/* Marvell MPSC */
#define PORT_MPSC 63
+/* TXX9 type number */
+#define PORT_TXX9 64
+
#ifdef __KERNEL__
#include <linux/config.h>