staging: Add SystemBase Multi-2/PCI driver
authorSteven Rostedt <srostedt@redhat.com>
Fri, 16 Nov 2012 14:28:49 +0000 (09:28 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 16 Nov 2012 14:58:09 +0000 (06:58 -0800)
I ported the driver supplied by SystemBase to mainline.

As the driver had MODULE_LICENSE("GPL") it is declared as a GPL module
and thus I have the right to distribute it upstream. Note, I did the
bare minimum to get it working. It still needs a lot of loving.

Cc: hjchoi <hjchoi@sysbas.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/Kconfig
drivers/staging/Makefile
drivers/staging/sb105x/Kconfig [new file with mode: 0644]
drivers/staging/sb105x/Makefile [new file with mode: 0644]
drivers/staging/sb105x/sb_mp_register.h [new file with mode: 0644]
drivers/staging/sb105x/sb_pci_mp.c [new file with mode: 0644]
drivers/staging/sb105x/sb_pci_mp.h [new file with mode: 0644]
drivers/staging/sb105x/sb_ser_core.h [new file with mode: 0644]

index d805eef..f245fd3 100644 (file)
@@ -144,4 +144,6 @@ source "drivers/staging/imx-drm/Kconfig"
 
 source "drivers/staging/dgrp/Kconfig"
 
+source "drivers/staging/sb105x/Kconfig"
+
 endif # STAGING
index 76e2ebd..94cc3fa 100644 (file)
@@ -64,3 +64,4 @@ obj-$(CONFIG_NET_VENDOR_SILICOM)      += silicom/
 obj-$(CONFIG_CED1401)          += ced1401/
 obj-$(CONFIG_DRM_IMX)          += imx-drm/
 obj-$(CONFIG_DGRP)             += dgrp/
+obj-$(CONFIG_SB105X)           += sb105x/
diff --git a/drivers/staging/sb105x/Kconfig b/drivers/staging/sb105x/Kconfig
new file mode 100644 (file)
index 0000000..ac87c5e
--- /dev/null
@@ -0,0 +1,9 @@
+config SB105X
+       tristate "SystemBase PCI Multiport UART"
+       select SERIAL_CORE
+       depends on PCI
+       help
+         A driver for the SystemBase Multi-2/PCI serial card
+
+         To compile this driver a module, choose M here: the module
+         will be called "sb105x".
diff --git a/drivers/staging/sb105x/Makefile b/drivers/staging/sb105x/Makefile
new file mode 100644 (file)
index 0000000..b1bf377
--- /dev/null
@@ -0,0 +1,3 @@
+obj-$(CONFIG_SB105X) +=        sb105x.o
+
+sb105x-y :=  sb_pci_mp.o
diff --git a/drivers/staging/sb105x/sb_mp_register.h b/drivers/staging/sb105x/sb_mp_register.h
new file mode 100644 (file)
index 0000000..5480ae1
--- /dev/null
@@ -0,0 +1,295 @@
+
+/*
+ * SB105X_UART.h
+ *
+ * Copyright (C) 2008 systembase
+ *
+ * UART registers.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef UART_SB105X_H
+#define UART_SB105X_H
+
+/* 
+ * option register 
+ */
+
+/* Device Infomation Register */
+#define MP_OPTR_DIR0           0x04    /* port0 ~ port8 */
+#define MP_OPTR_DIR1           0x05    /* port8 ~ port15 */
+#define MP_OPTR_DIR2           0x06    /* port16 ~ port23 */
+#define MP_OPTR_DIR3           0x07    /* port24 ~ port31 */
+
+#define DIR_UART_16C550        0
+#define DIR_UART_16C1050       1
+#define DIR_UART_16C1050A      2
+
+#define        DIR_CLK_1843200         0x0             /* input clock 1843200 Hz */
+#define        DIR_CLK_3686400         0x1             /* input clock 3686400 Hz */
+#define        DIR_CLK_7372800         0x2             /* input clock 7372800 Hz */
+#define        DIR_CLK_14745600        0x3             /* input clock 14745600 Hz */
+#define        DIR_CLK_29491200        0x4             /* input clock 29491200 Hz */
+#define        DIR_CLK_58985400        0x5             /* input clock 58985400 Hz */
+
+/* Interface Information Register */
+#define MP_OPTR_IIR0           0x08    /* port0 ~ port8 */
+#define MP_OPTR_IIR1           0x09    /* port8 ~ port15 */
+#define MP_OPTR_IIR2           0x0A    /* port16 ~ port23 */
+#define MP_OPTR_IIR3           0x0B    /* port24 ~ port31 */
+
+#define IIR_RS232              0x00            /* RS232 type */
+#define IIR_RS422              0x10            /* RS422 type */
+#define IIR_RS485              0x20            /* RS485 type */
+#define IIR_UNKNOWN            0x30            /* unknown type */
+
+/* Interrrupt Mask Register */
+#define MP_OPTR_IMR0           0x0C    /* port0 ~ port8 */
+#define MP_OPTR_IMR1           0x0D    /* port8 ~ port15 */
+#define MP_OPTR_IMR2           0x0E    /* port16 ~ port23 */
+#define MP_OPTR_IMR3           0x0F    /* port24 ~ port31 */
+
+/* Interrupt Poll Register */
+#define MP_OPTR_IPR0           0x10    /* port0 ~ port8 */
+#define MP_OPTR_IPR1           0x11    /* port8 ~ port15 */
+#define MP_OPTR_IPR2           0x12    /* port16 ~ port23 */
+#define MP_OPTR_IPR3           0x13    /* port24 ~ port31 */
+
+/* General Purpose Output Control Register */
+#define MP_OPTR_GPOCR          0x20
+
+/* General Purpose Output Data Register */
+#define MP_OPTR_GPODR          0x21
+
+/* Parallel Additional Function Register */
+#define MP_OPTR_PAFR           0x23
+
+/*
+ * systembase 16c105x UART register
+ */
+
+#define PAGE_0 0
+#define PAGE_1 1
+#define PAGE_2 2
+#define PAGE_3 3
+#define PAGE_4 4
+
+/*
+ *  ******************************************************************
+ *  * DLAB=0                  ===============       Page 0 Registers *
+ *  ******************************************************************
+ */
+
+#define SB105X_RX              0       /* In:  Receive buffer */
+#define SB105X_TX              0       /* Out: Transmit buffer */
+
+#define SB105X_IER             1       /* Out: Interrupt Enable Register */
+
+#define SB105X_IER_CTSI                0x80    /* CTS# Interrupt Enable (Requires EFR[4] = 1) */
+#define SB105X_IER_RTSI                0x40    /* RTS# Interrupt Enable (Requires EFR[4] = 1) */
+#define SB105X_IER_XOI         0x20    /* Xoff Interrupt Enable (Requires EFR[4] = 1) */
+#define SB105X_IER_SME         0x10    /* Sleep Mode Enable (Requires EFR[4] = 1) */
+#define SB105X_IER_MSI         0x08    /* Enable Modem status interrupt */
+#define SB105X_IER_RLSI                0x04    /* Enable receiver line status interrupt */
+#define SB105X_IER_THRI                0x02    /* Enable Transmitter holding register int. */
+#define SB105X_IER_RDI         0x01    /* Enable receiver data interrupt */
+
+#define SB105X_ISR             2       /* In:  Interrupt ID Register */
+
+#define SB105X_ISR_NOINT       0x01    /* No interrupts pending */
+#define SB105X_ISR_RLSI                0x06    /* Receiver line status interrupt (Priority = 1)*/
+#define SB105X_ISR_RDAI                0x0c    /* Receive Data Available interrupt */
+#define SB105X_ISR_CTII                0x04    /* Character Timeout Indication interrupt */
+#define SB105X_ISR_THRI                0x02    /* Transmitter holding register empty */
+#define SB105X_ISR_MSI         0x00    /* Modem status interrupt */
+#define SB105X_ISR_RXCI                0x10    /* Receive Xoff or Special Character interrupt */
+#define SB105X_ISR_RCSI                0x20    /* RTS#, CTS# status interrupt during Auto RTS/CTS flow control */
+
+#define SB105X_FCR             2       /* Out: FIFO Control Register */
+
+#define SB105X_FCR_FEN         0x01    /* FIFO Enable */
+#define SB105X_FCR_RXFR                0x02    /* RX FIFO Reset */
+#define SB105X_FCR_TXFR                0x04    /* TX FIFO Reset */
+#define SB105X_FCR_DMS         0x08    /* DMA Mode Select */
+
+#define SB105X_FCR_RTR08       0x00    /* Receice Trigger Level set at 8 */
+#define SB105X_FCR_RTR16       0x40  /* Receice Trigger Level set at 16 */
+#define SB105X_FCR_RTR56       0x80  /* Receice Trigger Level set at 56 */
+#define SB105X_FCR_RTR60       0xc0  /* Receice Trigger Level set at 60 */
+#define SB105X_FCR_TTR08       0x00  /* Transmit Trigger Level set at 8 */
+#define SB105X_FCR_TTR16       0x10  /* Transmit Trigger Level set at 16 */
+#define SB105X_FCR_TTR32       0x20  /* Transmit Trigger Level set at 32 */
+#define SB105X_FCR_TTR56       0x30  /* Transmit Trigger Level set at 56 */
+
+#define SB105X_LCR             3       /* Out: Line Control Register */
+/*
+ *  * Note: if the word length is 5 bits (SB105X_LCR_WLEN5), then setting 
+ *  * SB105X_LCR_STOP will select 1.5 stop bits, not 2 stop bits.
+ */
+#define SB105X_LCR_DLAB        0x80  /* Divisor Latch Enable */
+#define SB105X_LCR_SBC         0x40  /* Break Enable*/
+#define SB105X_LCR_SPAR        0x20  /* Set Stick parity */
+#define SB105X_LCR_EPAR        0x10  /* Even parity select */
+#define SB105X_LCR_PAREN       0x08  /* Parity Enable */
+#define SB105X_LCR_STOP        0x04  /* Stop bits: 0->1 bit, 1->2 bits, 1 and SB105X_LCR_WLEN5 -> 1.5 bit */
+#define SB105X_LCR_WLEN5       0x00  /* Wordlength: 5 bits */
+#define SB105X_LCR_WLEN6       0x01  /* Wordlength: 6 bits */
+#define SB105X_LCR_WLEN7       0x02  /* Wordlength: 7 bits */
+#define SB105X_LCR_WLEN8       0x03  /* Wordlength: 8 bits */
+
+#define SB105X_LCR_BF          0xBF
+
+#define SB105X_MCR             4       /* Out: Modem Control Register */
+#define SB105X_MCR_CPS         0x80  /* Clock Prescaler Select */
+#define SB105X_MCR_P2S         0x40  /* Page 2 Select /Xoff Re-Transmit Access Enable */
+#define SB105X_MCR_XOA         0x20  /* Xon Any Enable */
+#define SB105X_MCR_ILB         0x10  /* Internal Loopback Enable */
+#define SB105X_MCR_OUT2                0x08  /* Out2/Interrupt Output Enable*/
+#define SB105X_MCR_OUT1                0x04  /* Out1/Interrupt Output Enable */
+#define SB105X_MCR_RTS         0x02  /* RTS# Output */
+#define SB105X_MCR_DTR         0x01  /* DTR# Output */
+
+#define SB105X_LSR             5       /* In:  Line Status Register */
+#define SB105X_LSR_RFEI        0x80  /* Receive FIFO data error Indicator */
+#define SB105X_LSR_TEMI        0x40  /* THR and TSR Empty Indicator */
+#define SB105X_LSR_THRE                0x20  /* THR Empty Indicator */
+#define SB105X_LSR_BII         0x10  /* Break interrupt indicator */
+#define SB105X_LSR_FEI         0x08  /* Frame error indicator */
+#define SB105X_LSR_PEI         0x04  /* Parity error indicator */
+#define SB105X_LSR_OEI         0x02  /* Overrun error indicator */
+#define SB105X_LSR_RDRI                0x01  /* Receive data ready Indicator*/
+
+#define SB105X_MSR             6       /* In:  Modem Status Register */
+#define SB105X_MSR_DCD         0x80  /* Data Carrier Detect */
+#define SB105X_MSR_RI          0x40  /* Ring Indicator */
+#define SB105X_MSR_DSR         0x20  /* Data Set Ready */
+#define SB105X_MSR_CTS         0x10  /* Clear to Send */
+#define SB105X_MSR_DDCD                0x08  /* Delta DCD */
+#define SB105X_MSR_DRI         0x04  /* Delta ring indicator */
+#define SB105X_MSR_DDSR                0x02  /* Delta DSR */
+#define SB105X_MSR_DCTS                0x01  /* Delta CTS */
+
+#define SB105XA_MDR            6       /* Out: Multi Drop mode Register */
+#define SB105XA_MDR_NPS                0x08  /* 9th Bit Polarity Select */
+#define SB105XA_MDR_AME                0x02  /* Auto Multi-drop Enable */
+#define SB105XA_MDR_MDE                0x01  /* Multi Drop Enable */
+
+#define SB105X_SPR             7       /* I/O: Scratch Register */
+
+/*
+ * DLAB=1
+ */
+#define SB105X_DLL             0       /* Out: Divisor Latch Low */
+#define SB105X_DLM             1       /* Out: Divisor Latch High */
+
+/*
+ *  ******************************************************************
+ *  * DLAB(LCR[7]) = 0 , MCR[6] = 1  =============  Page 2 Registers *
+ *  ******************************************************************
+ */
+#define SB105X_GICR            1       /* Global Interrupt Control Register */
+#define SB105X_GICR_GIM        0x01  /* Global Interrupt Mask */
+
+#define SB105X_GISR            2       /* Global Interrupt Status Register */
+#define SB105X_GISR_MGICR0     0x80  /* Mirror the content of GICR[0] */
+#define SB105X_GISR_CS3IS      0x08  /* SB105X of CS3# Interrupt Status */
+#define SB105X_GISR_CS2IS      0x04  /* SB105X of CS2# Interrupt Status */
+#define SB105X_GISR_CS1IS      0x02  /* SB105X of CS1# Interrupt Status */
+#define SB105X_GISR_CS0IS      0x01  /* SB105X of CS0# Interrupt Status */
+
+#define SB105X_TFCR            5       /* Transmit FIFO Count Register */
+
+#define SB105X_RFCR            6       /* Receive FIFO Count Register */
+
+#define        SB105X_FSR              7       /* Flow Control Status Register */
+#define SB105X_FSR_THFS        0x20  /* Transmit Hardware Flow Control Status */
+#define SB105X_FSR_TSFS        0x10  /* Transmit Software Flow Control Status */
+#define SB105X_FSR_RHFS        0x02  /* Receive Hardware Flow Control Status */
+#define SB105X_FSR_RSFS        0x01  /* Receive Software Flow Control Status */
+
+/*
+ *  ******************************************************************
+ *  * LCR = 0xBF, PSR[0] = 0       =============    Page 3 Registers *
+ *  ******************************************************************
+ */
+
+#define SB105X_PSR             0       /* Page Select Register */
+#define SB105X_PSR_P3KEY       0xA4 /* Page 3 Select Key */
+#define SB105X_PSR_P4KEY       0xA5 /* Page 5 Select Key */
+
+#define SB105X_ATR             1       /* Auto Toggle Control Register */
+#define SB105X_ATR_RPS         0x80  /* RXEN Polarity Select */
+#define SB105X_ATR_RCMS        0x40  /* RXEN Control Mode Select */
+#define SB105X_ATR_TPS         0x20  /* TXEN Polarity Select */
+#define SB105X_ATR_TCMS        0x10  /* TXEN Control Mode Select */
+#define SB105X_ATR_ATDIS       0x00  /* Auto Toggle is disabled */
+#define SB105X_ATR_ART         0x01  /* RTS#/TXEN pin operates as TXEN */
+#define SB105X_ATR_ADT         0x02  /* DTR#/TXEN pin operates as TXEN */
+#define SB105X_ATR_A80         0x03  /* only in 80 pin use */
+
+#define SB105X_EFR             2       /* (Auto) Enhanced Feature Register */
+#define SB105X_EFR_ACTS        0x80  /* Auto-CTS Flow Control Enable */
+#define SB105X_EFR_ARTS        0x40  /* Auto-RTS Flow Control Enable */
+#define SB105X_EFR_SCD         0x20  /* Special Character Detect */
+#define SB105X_EFR_EFBEN       0x10  /* Enhanced Function Bits Enable */
+
+#define SB105X_XON1            4       /* Xon1 Character Register */
+#define SB105X_XON2            5       /* Xon2 Character Register */
+#define SB105X_XOFF1           6       /* Xoff1 Character Register */
+#define SB105X_XOFF2           7       /* Xoff2 Character Register */
+
+/*
+ *  ******************************************************************
+ *  * LCR = 0xBF, PSR[0] = 1       ============     Page 4 Registers *
+ *  ******************************************************************
+ */
+
+#define SB105X_AFR             1       /* Additional Feature Register */
+#define SB105X_AFR_GIPS        0x20  /* Global Interrupt Polarity Select */
+#define SB105X_AFR_GIEN        0x10  /* Global Interrupt Enable */
+#define SB105X_AFR_AFEN        0x01  /* 256-byte FIFO Enable */
+
+#define SB105X_XRCR            2       /* Xoff Re-transmit Count Register */
+#define SB105X_XRCR_NRC1       0x00  /* Transmits Xoff Character whenever the number of received data is 1 during XOFF status */
+#define SB105X_XRCR_NRC4       0x01  /* Transmits Xoff Character whenever the number of received data is 4 during XOFF status */
+#define SB105X_XRCR_NRC8       0x02  /* Transmits Xoff Character whenever the number of received data is 8 during XOFF status */
+#define SB105X_XRCR_NRC16      0x03  /* Transmits Xoff Character whenever the number of received data is 16 during XOFF status */
+
+#define SB105X_TTR             4       /* Transmit FIFO Trigger Level Register */
+#define SB105X_RTR             5       /* Receive FIFO Trigger Level Register */
+#define SB105X_FUR             6       /* Flow Control Upper Threshold Register */
+#define SB105X_FLR             7       /* Flow Control Lower Threshold Register */
+
+
+/* page 0 */
+
+#define SB105X_GET_CHAR(port)  inb((port)->iobase + SB105X_RX)
+#define SB105X_GET_IER(port)   inb((port)->iobase + SB105X_IER)
+#define SB105X_GET_ISR(port)   inb((port)->iobase + SB105X_ISR)
+#define SB105X_GET_LCR(port)   inb((port)->iobase + SB105X_LCR)
+#define SB105X_GET_MCR(port)   inb((port)->iobase + SB105X_MCR)
+#define SB105X_GET_LSR(port)   inb((port)->iobase + SB105X_LSR)
+#define SB105X_GET_MSR(port)   inb((port)->iobase + SB105X_MSR)
+#define SB105X_GET_SPR(port)   inb((port)->iobase + SB105X_SPR)
+
+#define SB105X_PUT_CHAR(port,v)        outb((v),(port)->iobase + SB105X_TX )
+#define SB105X_PUT_IER(port,v) outb((v),(port)->iobase + SB105X_IER )
+#define SB105X_PUT_FCR(port,v) outb((v),(port)->iobase + SB105X_FCR )
+#define SB105X_PUT_LCR(port,v) outb((v),(port)->iobase + SB105X_LCR )
+#define SB105X_PUT_MCR(port,v) outb((v),(port)->iobase + SB105X_MCR )
+#define SB105X_PUT_SPR(port,v) outb((v),(port)->iobase + SB105X_SPR )
+
+
+/* page 1 */
+#define SB105X_GET_REG(port,reg)       inb((port)->iobase + (reg))
+#define SB105X_PUT_REG(port,reg,v)     outb((v),(port)->iobase + (reg))
+
+/* page 2 */
+
+#define SB105X_PUT_PSR(port,v) outb((v),(port)->iobase + SB105X_PSR )
+
+#endif 
diff --git a/drivers/staging/sb105x/sb_pci_mp.c b/drivers/staging/sb105x/sb_pci_mp.c
new file mode 100644 (file)
index 0000000..fbebf88
--- /dev/null
@@ -0,0 +1,3195 @@
+#include "sb_pci_mp.h"
+#include <linux/module.h>
+#include <linux/parport.h>
+
+extern struct parport *parport_pc_probe_port(unsigned long base_lo,
+               unsigned long base_hi,
+               int irq, int dma,
+               struct device *dev,
+               int irqflags);
+
+static struct mp_device_t mp_devs[MAX_MP_DEV];
+static int mp_nrpcibrds = sizeof(mp_pciboards)/sizeof(mppcibrd_t);
+static int NR_BOARD=0;
+static int NR_PORTS=0;
+static struct mp_port multi_ports[MAX_MP_PORT];
+static struct irq_info irq_lists[NR_IRQS];
+
+static _INLINE_ unsigned int serial_in(struct mp_port *mtpt, int offset);
+static _INLINE_ void serial_out(struct mp_port *mtpt, int offset, int value);
+static _INLINE_ unsigned int read_option_register(struct mp_port *mtpt, int offset);
+static int sb1054_get_register(struct sb_uart_port * port, int page, int reg);
+static int sb1054_set_register(struct sb_uart_port * port, int page, int reg, int value);
+static void SendATCommand(struct mp_port * mtpt);
+static int set_deep_fifo(struct sb_uart_port * port, int status);
+static int get_deep_fifo(struct sb_uart_port * port);
+static int get_device_type(int arg);
+static int set_auto_rts(struct sb_uart_port *port, int status);
+static void mp_stop(struct tty_struct *tty);
+static void __mp_start(struct tty_struct *tty);
+static void mp_start(struct tty_struct *tty);
+static void mp_tasklet_action(unsigned long data);
+static inline void mp_update_mctrl(struct sb_uart_port *port, unsigned int set, unsigned int clear);
+static int mp_startup(struct sb_uart_state *state, int init_hw);
+static void mp_shutdown(struct sb_uart_state *state);
+static void mp_change_speed(struct sb_uart_state *state, struct MP_TERMIOS *old_termios);
+
+static inline int __mp_put_char(struct sb_uart_port *port, struct circ_buf *circ, unsigned char c);
+static int mp_put_char(struct tty_struct *tty, unsigned char ch);
+
+static void mp_put_chars(struct tty_struct *tty);
+static int mp_write(struct tty_struct *tty, const unsigned char * buf, int count);
+static int mp_write_room(struct tty_struct *tty);
+static int mp_chars_in_buffer(struct tty_struct *tty);
+static void mp_flush_buffer(struct tty_struct *tty);
+static void mp_send_xchar(struct tty_struct *tty, char ch);
+static void mp_throttle(struct tty_struct *tty);
+static void mp_unthrottle(struct tty_struct *tty);
+static int mp_get_info(struct sb_uart_state *state, struct serial_struct *retinfo);
+static int mp_set_info(struct sb_uart_state *state, struct serial_struct *newinfo);
+static int mp_get_lsr_info(struct sb_uart_state *state, unsigned int *value);
+
+static int mp_tiocmget(struct tty_struct *tty);
+static int mp_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear);
+static int mp_break_ctl(struct tty_struct *tty, int break_state);
+static int mp_do_autoconfig(struct sb_uart_state *state);
+static int mp_wait_modem_status(struct sb_uart_state *state, unsigned long arg);
+static int mp_get_count(struct sb_uart_state *state, struct serial_icounter_struct *icnt);
+static int mp_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg);
+static void mp_set_termios(struct tty_struct *tty, struct MP_TERMIOS *old_termios);
+static void mp_close(struct tty_struct *tty, struct file *filp);
+static void mp_wait_until_sent(struct tty_struct *tty, int timeout);
+static void mp_hangup(struct tty_struct *tty);
+static void mp_update_termios(struct sb_uart_state *state);
+static int mp_block_til_ready(struct file *filp, struct sb_uart_state *state);
+static struct sb_uart_state *uart_get(struct uart_driver *drv, int line);
+static int mp_open(struct tty_struct *tty, struct file *filp);
+static const char *mp_type(struct sb_uart_port *port);
+static void mp_change_pm(struct sb_uart_state *state, int pm_state);
+static inline void mp_report_port(struct uart_driver *drv, struct sb_uart_port *port);
+static void mp_configure_port(struct uart_driver *drv, struct sb_uart_state *state, struct sb_uart_port *port);
+static void mp_unconfigure_port(struct uart_driver *drv, struct sb_uart_state *state);
+static int mp_register_driver(struct uart_driver *drv);
+static void mp_unregister_driver(struct uart_driver *drv);
+static int mp_add_one_port(struct uart_driver *drv, struct sb_uart_port *port);
+static int mp_remove_one_port(struct uart_driver *drv, struct sb_uart_port *port);
+static void autoconfig(struct mp_port *mtpt, unsigned int probeflags);
+static void autoconfig_irq(struct mp_port *mtpt);
+static void multi_stop_tx(struct sb_uart_port *port);
+static void multi_start_tx(struct sb_uart_port *port);
+static void multi_stop_rx(struct sb_uart_port *port);
+static void multi_enable_ms(struct sb_uart_port *port);
+static _INLINE_ void receive_chars(struct mp_port *mtpt, int *status );
+static _INLINE_ void transmit_chars(struct mp_port *mtpt);
+static _INLINE_ void check_modem_status(struct mp_port *mtpt);
+static inline void multi_handle_port(struct mp_port *mtpt);
+static irqreturn_t multi_interrupt(int irq, void *dev_id);
+static void serial_do_unlink(struct irq_info *i, struct mp_port *mtpt);
+static int serial_link_irq_chain(struct mp_port *mtpt);
+static void serial_unlink_irq_chain(struct mp_port *mtpt);
+static void multi_timeout(unsigned long data);
+static unsigned int multi_tx_empty(struct sb_uart_port *port);
+static unsigned int multi_get_mctrl(struct sb_uart_port *port);
+static void multi_set_mctrl(struct sb_uart_port *port, unsigned int mctrl);
+static void multi_break_ctl(struct sb_uart_port *port, int break_state);
+static int multi_startup(struct sb_uart_port *port);
+static void multi_shutdown(struct sb_uart_port *port);
+static unsigned int multi_get_divisor(struct sb_uart_port *port, unsigned int baud);
+static void multi_set_termios(struct sb_uart_port *port, struct MP_TERMIOS *termios, struct MP_TERMIOS *old);
+static void multi_pm(struct sb_uart_port *port, unsigned int state, unsigned int oldstate);
+static void multi_release_std_resource(struct mp_port *mtpt);
+static void multi_release_port(struct sb_uart_port *port);
+static int multi_request_port(struct sb_uart_port *port);
+static void multi_config_port(struct sb_uart_port *port, int flags);
+static int multi_verify_port(struct sb_uart_port *port, struct serial_struct *ser);
+static const char * multi_type(struct sb_uart_port *port);
+static void __init multi_init_ports(void);
+static void __init multi_register_ports(struct uart_driver *drv);
+static int init_mp_dev(struct pci_dev *pcidev, mppcibrd_t brd);
+
+static int deep[256];
+static int deep_count;
+static int fcr_arr[256];
+static int fcr_count;
+static int ttr[256];
+static int ttr_count;
+static int rtr[256];
+static int rtr_count;
+
+module_param_array(deep,int,&deep_count,0);
+module_param_array(fcr_arr,int,&fcr_count,0);
+module_param_array(ttr,int,&ttr_count,0);
+module_param_array(rtr,int,&rtr_count,0);
+
+static _INLINE_ unsigned int serial_in(struct mp_port *mtpt, int offset)
+{
+       return inb(mtpt->port.iobase + offset);
+}
+
+static _INLINE_ void serial_out(struct mp_port *mtpt, int offset, int value)
+{
+       outb(value, mtpt->port.iobase + offset);
+}
+
+static _INLINE_ unsigned int read_option_register(struct mp_port *mtpt, int offset)
+{
+       return inb(mtpt->option_base_addr + offset);
+}
+
+static int sb1053a_get_interface(struct mp_port *mtpt, int port_num)
+{
+       unsigned long option_base_addr = mtpt->option_base_addr;
+       unsigned int  interface = 0;
+
+       switch (port_num)
+       {
+               case 0:
+               case 1:
+                       /* set GPO[1:0] = 00 */
+                       outb(0x00, option_base_addr + MP_OPTR_GPODR);
+                       break;
+               case 2:
+               case 3:
+                       /* set GPO[1:0] = 01 */
+                       outb(0x01, option_base_addr + MP_OPTR_GPODR);
+                       break;
+               case 4:
+               case 5:
+                       /* set GPO[1:0] = 10 */
+                       outb(0x02, option_base_addr + MP_OPTR_GPODR);
+                       break;
+               default:
+                       break;
+       }
+
+       port_num &= 0x1;
+
+       /* get interface */
+       interface = inb(option_base_addr + MP_OPTR_IIR0 + port_num);
+
+       /* set GPO[1:0] = 11 */
+       outb(0x03, option_base_addr + MP_OPTR_GPODR);
+
+       return (interface);
+}
+               
+static int sb1054_get_register(struct sb_uart_port * port, int page, int reg)
+{
+       int ret = 0;
+       unsigned int lcr = 0;
+       unsigned int mcr = 0;
+       unsigned int tmp = 0;
+
+       if( page <= 0)
+       {
+               printk(" page 0 can not use this fuction\n");
+               return -1;
+       }
+
+       switch(page)
+       {
+               case 1:
+                       lcr = SB105X_GET_LCR(port);
+                       tmp = lcr | SB105X_LCR_DLAB;
+                       SB105X_PUT_LCR(port, tmp);
+
+                       tmp = SB105X_GET_LCR(port);
+
+                       ret = SB105X_GET_REG(port,reg);
+                       SB105X_PUT_LCR(port,lcr);
+                       break;
+               case 2:
+                       mcr = SB105X_GET_MCR(port);
+                       tmp = mcr | SB105X_MCR_P2S;
+                       SB105X_PUT_MCR(port,tmp);
+
+                       ret = SB105X_GET_REG(port,reg);
+
+                       SB105X_PUT_MCR(port,mcr);
+                       break;
+               case 3:
+                       lcr = SB105X_GET_LCR(port);
+                       tmp = lcr | SB105X_LCR_BF;
+                       SB105X_PUT_LCR(port,tmp);
+                       SB105X_PUT_REG(port,SB105X_PSR,SB105X_PSR_P3KEY);
+
+                       ret = SB105X_GET_REG(port,reg);
+
+                       SB105X_PUT_LCR(port,lcr);
+                       break;
+               case 4:
+                       lcr = SB105X_GET_LCR(port);
+                       tmp = lcr | SB105X_LCR_BF;
+                       SB105X_PUT_LCR(port,tmp);
+                       SB105X_PUT_REG(port,SB105X_PSR,SB105X_PSR_P4KEY);
+
+                       ret = SB105X_GET_REG(port,reg);
+
+                       SB105X_PUT_LCR(port,lcr);
+                       break;
+               default:
+                       printk(" error invalid page number \n");
+                       return -1;
+       }
+
+       return ret;
+}
+
+static int sb1054_set_register(struct sb_uart_port * port, int page, int reg, int value)
+{  
+       int lcr = 0;
+       int mcr = 0;
+       int ret = 0;
+
+       if( page <= 0)
+       {
+               printk(" page 0 can not use this fuction\n");
+               return -1;
+       }
+       switch(page)
+       {
+               case 1:
+                       lcr = SB105X_GET_LCR(port);
+                       SB105X_PUT_LCR(port, lcr | SB105X_LCR_DLAB);
+
+                       SB105X_PUT_REG(port,reg,value);
+
+                       SB105X_PUT_LCR(port, lcr);
+                       ret = 1;
+                       break;
+               case 2:
+                       mcr = SB105X_GET_MCR(port);
+                       SB105X_PUT_MCR(port, mcr | SB105X_MCR_P2S);
+
+                       SB105X_PUT_REG(port,reg,value);
+
+                       SB105X_PUT_MCR(port, mcr);
+                       ret = 1;
+                       break;
+               case 3:
+                       lcr = SB105X_GET_LCR(port);
+                       SB105X_PUT_LCR(port, lcr | SB105X_LCR_BF);
+                       SB105X_PUT_PSR(port, SB105X_PSR_P3KEY);
+
+                       SB105X_PUT_REG(port,reg,value);
+
+                       SB105X_PUT_LCR(port, lcr);
+                       ret = 1;
+                       break;
+               case 4:
+                       lcr = SB105X_GET_LCR(port);
+                       SB105X_PUT_LCR(port, lcr | SB105X_LCR_BF);
+                       SB105X_PUT_PSR(port, SB105X_PSR_P4KEY);
+
+                       SB105X_PUT_REG(port,reg,value);
+
+                       SB105X_PUT_LCR(port, lcr);
+                       ret = 1;
+                       break;
+               default:
+                       printk(" error invalid page number \n");
+                       return -1;
+       }
+
+       return ret;
+}
+
+static int set_multidrop_mode(struct sb_uart_port *port, unsigned int mode)
+{
+       int mdr = SB105XA_MDR_NPS;
+
+       if (mode & MDMODE_ENABLE)
+       {
+               mdr |= SB105XA_MDR_MDE;
+       }
+
+       if (1) //(mode & MDMODE_AUTO)
+       {
+               int efr = 0;
+               mdr |= SB105XA_MDR_AME;
+               efr = sb1054_get_register(port, PAGE_3, SB105X_EFR);
+               efr |= SB105X_EFR_SCD;
+               sb1054_set_register(port, PAGE_3, SB105X_EFR, efr);
+       }
+
+       sb1054_set_register(port, PAGE_1, SB105XA_MDR, mdr);
+       port->mdmode &= ~0x6;
+       port->mdmode |= mode;
+       printk("[%d] multidrop init: %x\n", port->line, port->mdmode);
+
+       return 0;
+}
+
+static int get_multidrop_addr(struct sb_uart_port *port)
+{
+       return sb1054_get_register(port, PAGE_3, SB105X_XOFF2);
+}
+
+static int set_multidrop_addr(struct sb_uart_port *port, unsigned int addr)
+{
+       sb1054_set_register(port, PAGE_3, SB105X_XOFF2, addr);
+
+       return 0;
+}
+
+static void SendATCommand(struct mp_port * mtpt)
+{
+       //                    a    t    cr   lf
+       unsigned char ch[] = {0x61,0x74,0x0d,0x0a,0x0};
+       unsigned char lineControl;
+       unsigned char i=0;
+       unsigned char Divisor = 0xc;
+
+       lineControl = serial_inp(mtpt,UART_LCR);
+       serial_outp(mtpt,UART_LCR,(lineControl | UART_LCR_DLAB));
+       serial_outp(mtpt,UART_DLL,(Divisor & 0xff));
+       serial_outp(mtpt,UART_DLM,(Divisor & 0xff00)>>8); //baudrate is 4800
+
+
+       serial_outp(mtpt,UART_LCR,lineControl); 
+       serial_outp(mtpt,UART_LCR,0x03); // N-8-1
+       serial_outp(mtpt,UART_FCR,7); 
+       serial_outp(mtpt,UART_MCR,0x3);
+       while(ch[i]){
+               while((serial_inp(mtpt,UART_LSR) & 0x60) !=0x60){
+                       ;
+               }
+               serial_outp(mtpt,0,ch[i++]);
+       }
+
+
+}// end of SendATCommand()
+
+static int set_deep_fifo(struct sb_uart_port * port, int status)
+{
+       int afr_status = 0;
+       afr_status = sb1054_get_register(port, PAGE_4, SB105X_AFR);
+
+       if(status == ENABLE)
+       {
+               afr_status |= SB105X_AFR_AFEN;
+       }
+       else
+       {
+               afr_status &= ~SB105X_AFR_AFEN;
+       }
+               
+       sb1054_set_register(port,PAGE_4,SB105X_AFR,afr_status);
+       sb1054_set_register(port,PAGE_4,SB105X_TTR,ttr[port->line]); 
+       sb1054_set_register(port,PAGE_4,SB105X_RTR,rtr[port->line]); 
+       afr_status = sb1054_get_register(port, PAGE_4, SB105X_AFR);
+               
+       return afr_status;
+}
+
+static int get_device_type(int arg)
+{
+       int ret;
+        ret = inb(mp_devs[arg].option_reg_addr+MP_OPTR_DIR0);
+        ret = (ret & 0xf0) >> 4;
+        switch (ret)
+        {
+               case DIR_UART_16C550:
+                    return PORT_16C55X;
+               case DIR_UART_16C1050:
+                    return PORT_16C105X;
+               case DIR_UART_16C1050A:
+               /*
+               if (mtpt->port.line < 2)
+               {
+                    return PORT_16C105XA;
+               }
+               else
+               {
+                   if (mtpt->device->device_id & 0x50)
+                   {
+                       return PORT_16C55X;
+                   }
+                   else
+                   {
+                       return PORT_16C105X;
+                   }
+               }*/
+               return PORT_16C105XA;
+               default:
+                    return PORT_UNKNOWN;
+        }
+
+}
+static int get_deep_fifo(struct sb_uart_port * port)
+{
+       int afr_status = 0;
+       afr_status = sb1054_get_register(port, PAGE_4, SB105X_AFR);
+       return afr_status;
+}
+
+static int set_auto_rts(struct sb_uart_port *port, int status)
+{
+       int atr_status = 0;
+
+#if 0
+       int efr_status = 0;
+
+       efr_status = sb1054_get_register(port, PAGE_3, SB105X_EFR);
+       if(status == ENABLE)
+               efr_status |= SB105X_EFR_ARTS;
+       else
+               efr_status &= ~SB105X_EFR_ARTS;
+       sb1054_set_register(port,PAGE_3,SB105X_EFR,efr_status);
+       efr_status = sb1054_get_register(port, PAGE_3, SB105X_EFR);
+#endif
+               
+//ATR
+       atr_status = sb1054_get_register(port, PAGE_3, SB105X_ATR);
+       switch(status)
+       {
+               case RS422PTP:
+                       atr_status = (SB105X_ATR_TPS) | (SB105X_ATR_A80);
+                       break;
+               case RS422MD:
+                       atr_status = (SB105X_ATR_TPS) | (SB105X_ATR_TCMS) | (SB105X_ATR_A80);
+                       break;
+               case RS485NE:
+                       atr_status = (SB105X_ATR_RCMS) | (SB105X_ATR_TPS) | (SB105X_ATR_TCMS) | (SB105X_ATR_A80);
+                       break;
+               case RS485ECHO:
+                       atr_status = (SB105X_ATR_TPS) | (SB105X_ATR_TCMS) | (SB105X_ATR_A80);
+                       break;
+       }
+
+       sb1054_set_register(port,PAGE_3,SB105X_ATR,atr_status);
+       atr_status = sb1054_get_register(port, PAGE_3, SB105X_ATR);
+
+       return atr_status;
+}
+
+static void mp_stop(struct tty_struct *tty)
+{
+       struct sb_uart_state *state = tty->driver_data;
+       struct sb_uart_port *port = state->port;
+       unsigned long flags;
+
+       spin_lock_irqsave(&port->lock, flags);
+       port->ops->stop_tx(port);
+       spin_unlock_irqrestore(&port->lock, flags);
+}
+
+static void __mp_start(struct tty_struct *tty)
+{
+       struct sb_uart_state *state = tty->driver_data;
+       struct sb_uart_port *port = state->port;
+
+       if (!uart_circ_empty(&state->info->xmit) && state->info->xmit.buf &&
+                       !tty->stopped && !tty->hw_stopped)
+               port->ops->start_tx(port);
+}
+
+static void mp_start(struct tty_struct *tty)
+{
+       __mp_start(tty);
+}
+
+static void mp_tasklet_action(unsigned long data)
+{
+       struct sb_uart_state *state = (struct sb_uart_state *)data;
+       struct tty_struct *tty;
+
+       printk("tasklet is called!\n");
+       tty = state->info->tty;
+       tty_wakeup(tty);
+}
+
+static inline void mp_update_mctrl(struct sb_uart_port *port, unsigned int set, unsigned int clear)
+{
+       unsigned int old;
+
+       old = port->mctrl;
+       port->mctrl = (old & ~clear) | set;
+       if (old != port->mctrl)
+               port->ops->set_mctrl(port, port->mctrl);
+}
+
+#define uart_set_mctrl(port,set)       mp_update_mctrl(port,set,0)
+#define uart_clear_mctrl(port,clear)   mp_update_mctrl(port,0,clear)
+
+static int mp_startup(struct sb_uart_state *state, int init_hw)
+{
+       struct sb_uart_info *info = state->info;
+       struct sb_uart_port *port = state->port;
+       unsigned long page;
+       int retval = 0;
+
+       if (info->flags & UIF_INITIALIZED)
+               return 0;
+
+       if (info->tty)
+               set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+       if (port->type == PORT_UNKNOWN)
+               return 0;
+
+       if (!info->xmit.buf) {
+               page = get_zeroed_page(GFP_KERNEL);
+               if (!page)
+                       return -ENOMEM;
+
+               info->xmit.buf = (unsigned char *) page;
+                       
+               uart_circ_clear(&info->xmit);
+       }
+
+       retval = port->ops->startup(port);
+       if (retval == 0) {
+               if (init_hw) {
+                       mp_change_speed(state, NULL);
+
+                       if (info->tty->termios.c_cflag & CBAUD)
+                               uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR);
+               }
+
+               info->flags |= UIF_INITIALIZED;
+
+
+               clear_bit(TTY_IO_ERROR, &info->tty->flags);
+       }
+
+       if (retval && capable(CAP_SYS_ADMIN))
+               retval = 0;
+
+       return retval;
+}
+
+static void mp_shutdown(struct sb_uart_state *state)
+{
+       struct sb_uart_info *info = state->info;
+       struct sb_uart_port *port = state->port;
+
+       if (info->tty)
+               set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+       if (info->flags & UIF_INITIALIZED) {
+               info->flags &= ~UIF_INITIALIZED;
+
+               if (!info->tty || (info->tty->termios.c_cflag & HUPCL))
+                       uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS);
+
+               wake_up_interruptible(&info->delta_msr_wait);
+
+               port->ops->shutdown(port);
+
+               synchronize_irq(port->irq);
+       }
+       tasklet_kill(&info->tlet);
+
+       if (info->xmit.buf) {
+               free_page((unsigned long)info->xmit.buf);
+               info->xmit.buf = NULL;
+       }
+}
+
+static void mp_change_speed(struct sb_uart_state *state, struct MP_TERMIOS *old_termios)
+{
+       struct tty_struct *tty = state->info->tty;
+       struct sb_uart_port *port = state->port;
+
+       if (!tty || port->type == PORT_UNKNOWN)
+               return;
+
+       if (tty->termios.c_cflag & CRTSCTS)
+               state->info->flags |= UIF_CTS_FLOW;
+       else
+               state->info->flags &= ~UIF_CTS_FLOW;
+
+       if (tty->termios.c_cflag & CLOCAL)
+               state->info->flags &= ~UIF_CHECK_CD;
+       else
+               state->info->flags |= UIF_CHECK_CD;
+
+       port->ops->set_termios(port, &tty->termios, old_termios);
+}
+
+static inline int __mp_put_char(struct sb_uart_port *port, struct circ_buf *circ, unsigned char c)
+{
+       unsigned long flags;
+       int ret = 0;
+
+       if (!circ->buf)
+               return 0;
+
+       spin_lock_irqsave(&port->lock, flags);
+       if (uart_circ_chars_free(circ) != 0) {
+               circ->buf[circ->head] = c;
+               circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1);
+               ret = 1;
+       }
+       spin_unlock_irqrestore(&port->lock, flags);
+       return ret;
+}
+
+static int mp_put_char(struct tty_struct *tty, unsigned char ch)
+{
+       struct sb_uart_state *state = tty->driver_data;
+
+       return __mp_put_char(state->port, &state->info->xmit, ch);
+}
+
+static void mp_put_chars(struct tty_struct *tty)
+{
+       mp_start(tty);
+}
+
+static int mp_write(struct tty_struct *tty, const unsigned char * buf, int count)
+{
+       struct sb_uart_state *state = tty->driver_data;
+       struct sb_uart_port *port;
+       struct circ_buf *circ;
+       int c, ret = 0;
+
+       if (!state || !state->info) {
+               return -EL3HLT;
+       }
+
+       port = state->port;
+       circ = &state->info->xmit;
+
+       if (!circ->buf)
+               return 0;
+               
+       while (1) {
+               c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
+               if (count < c)
+                       c = count;
+               if (c <= 0)
+                       break;
+       memcpy(circ->buf + circ->head, buf, c);
+
+               circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
+               buf += c;
+               count -= c;
+               ret += c;
+       }
+       mp_start(tty);
+       return ret;
+}
+
+static int mp_write_room(struct tty_struct *tty)
+{
+       struct sb_uart_state *state = tty->driver_data;
+
+       return uart_circ_chars_free(&state->info->xmit);
+}
+
+static int mp_chars_in_buffer(struct tty_struct *tty)
+{
+       struct sb_uart_state *state = tty->driver_data;
+
+       return uart_circ_chars_pending(&state->info->xmit);
+}
+
+static void mp_flush_buffer(struct tty_struct *tty)
+{
+       struct sb_uart_state *state = tty->driver_data;
+       struct sb_uart_port *port = state->port;
+       unsigned long flags;
+
+       if (!state || !state->info) {
+               return;
+       }
+
+       spin_lock_irqsave(&port->lock, flags);
+       uart_circ_clear(&state->info->xmit);
+       spin_unlock_irqrestore(&port->lock, flags);
+       wake_up_interruptible(&tty->write_wait);
+       tty_wakeup(tty);
+}
+
+static void mp_send_xchar(struct tty_struct *tty, char ch)
+{
+       struct sb_uart_state *state = tty->driver_data;
+       struct sb_uart_port *port = state->port;
+       unsigned long flags;
+
+       if (port->ops->send_xchar)
+               port->ops->send_xchar(port, ch);
+       else {
+               port->x_char = ch;
+               if (ch) {
+                       spin_lock_irqsave(&port->lock, flags);
+                       port->ops->start_tx(port);
+                       spin_unlock_irqrestore(&port->lock, flags);
+               }
+       }
+}
+
+static void mp_throttle(struct tty_struct *tty)
+{
+       struct sb_uart_state *state = tty->driver_data;
+
+       if (I_IXOFF(tty))
+               mp_send_xchar(tty, STOP_CHAR(tty));
+
+       if (tty->termios.c_cflag & CRTSCTS)
+               uart_clear_mctrl(state->port, TIOCM_RTS);
+}
+
+static void mp_unthrottle(struct tty_struct *tty)
+{
+       struct sb_uart_state *state = tty->driver_data;
+       struct sb_uart_port *port = state->port;
+
+       if (I_IXOFF(tty)) {
+               if (port->x_char)
+                       port->x_char = 0;
+               else
+                       mp_send_xchar(tty, START_CHAR(tty));
+       }
+
+       if (tty->termios.c_cflag & CRTSCTS)
+               uart_set_mctrl(port, TIOCM_RTS);
+}
+
+static int mp_get_info(struct sb_uart_state *state, struct serial_struct *retinfo)
+{
+       struct sb_uart_port *port = state->port;
+       struct serial_struct tmp;
+
+       memset(&tmp, 0, sizeof(tmp));
+       tmp.type            = port->type;
+       tmp.line            = port->line;
+       tmp.port            = port->iobase;
+       if (HIGH_BITS_OFFSET)
+               tmp.port_high = (long) port->iobase >> HIGH_BITS_OFFSET;
+       tmp.irq             = port->irq;
+       tmp.flags           = port->flags;
+       tmp.xmit_fifo_size  = port->fifosize;
+       tmp.baud_base       = port->uartclk / 16;
+       tmp.close_delay     = state->close_delay;
+       tmp.closing_wait    = state->closing_wait == USF_CLOSING_WAIT_NONE ?
+               ASYNC_CLOSING_WAIT_NONE :
+               state->closing_wait;
+       tmp.custom_divisor  = port->custom_divisor;
+       tmp.hub6            = port->hub6;
+       tmp.io_type         = port->iotype;
+       tmp.iomem_reg_shift = port->regshift;
+       tmp.iomem_base      = (void *)port->mapbase;
+
+       if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+               return -EFAULT;
+       return 0;
+}
+
+static int mp_set_info(struct sb_uart_state *state, struct serial_struct *newinfo)
+{
+       struct serial_struct new_serial;
+       struct sb_uart_port *port = state->port;
+       unsigned long new_port;
+       unsigned int change_irq, change_port, closing_wait;
+       unsigned int old_custom_divisor;
+       unsigned int old_flags, new_flags;
+       int retval = 0;
+
+       if (copy_from_user(&new_serial, newinfo, sizeof(new_serial)))
+               return -EFAULT;
+
+       new_port = new_serial.port;
+       if (HIGH_BITS_OFFSET)
+               new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET;
+
+       new_serial.irq = irq_canonicalize(new_serial.irq);
+
+       closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ?
+               USF_CLOSING_WAIT_NONE : new_serial.closing_wait;
+       MP_STATE_LOCK(state);
+
+       change_irq  = new_serial.irq != port->irq;
+       change_port = new_port != port->iobase ||
+               (unsigned long)new_serial.iomem_base != port->mapbase ||
+               new_serial.hub6 != port->hub6 ||
+               new_serial.io_type != port->iotype ||
+               new_serial.iomem_reg_shift != port->regshift ||
+               new_serial.type != port->type;
+       old_flags = port->flags;
+       new_flags = new_serial.flags;
+       old_custom_divisor = port->custom_divisor;
+
+       if (!capable(CAP_SYS_ADMIN)) {
+               retval = -EPERM;
+               if (change_irq || change_port ||
+                               (new_serial.baud_base != port->uartclk / 16) ||
+                               (new_serial.close_delay != state->close_delay) ||
+                               (closing_wait != state->closing_wait) ||
+                               (new_serial.xmit_fifo_size != port->fifosize) ||
+                               (((new_flags ^ old_flags) & ~UPF_USR_MASK) != 0))
+                       goto exit;
+               port->flags = ((port->flags & ~UPF_USR_MASK) |
+                               (new_flags & UPF_USR_MASK));
+               port->custom_divisor = new_serial.custom_divisor;
+               goto check_and_exit;
+       }
+
+       if (port->ops->verify_port)
+               retval = port->ops->verify_port(port, &new_serial);
+
+       if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) ||
+                       (new_serial.baud_base < 9600))
+               retval = -EINVAL;
+
+       if (retval)
+               goto exit;
+
+       if (change_port || change_irq) {
+               retval = -EBUSY;
+
+               if (uart_users(state) > 1)
+                       goto exit;
+
+               mp_shutdown(state);
+       }
+
+       if (change_port) {
+               unsigned long old_iobase, old_mapbase;
+               unsigned int old_type, old_iotype, old_hub6, old_shift;
+
+               old_iobase = port->iobase;
+               old_mapbase = port->mapbase;
+               old_type = port->type;
+               old_hub6 = port->hub6;
+               old_iotype = port->iotype;
+               old_shift = port->regshift;
+
+               if (old_type != PORT_UNKNOWN)
+                       port->ops->release_port(port);
+
+               port->iobase = new_port;
+               port->type = new_serial.type;
+               port->hub6 = new_serial.hub6;
+               port->iotype = new_serial.io_type;
+               port->regshift = new_serial.iomem_reg_shift;
+               port->mapbase = (unsigned long)new_serial.iomem_base;
+
+               if (port->type != PORT_UNKNOWN) {
+                       retval = port->ops->request_port(port);
+               } else {
+                       retval = 0;
+               }
+
+               if (retval && old_type != PORT_UNKNOWN) {
+                       port->iobase = old_iobase;
+                       port->type = old_type;
+                       port->hub6 = old_hub6;
+                       port->iotype = old_iotype;
+                       port->regshift = old_shift;
+                       port->mapbase = old_mapbase;
+                       retval = port->ops->request_port(port);
+                       if (retval)
+                               port->type = PORT_UNKNOWN;
+
+                       retval = -EBUSY;
+               }
+       }
+
+       port->irq              = new_serial.irq;
+       port->uartclk          = new_serial.baud_base * 16;
+       port->flags            = (port->flags & ~UPF_CHANGE_MASK) |
+               (new_flags & UPF_CHANGE_MASK);
+       port->custom_divisor   = new_serial.custom_divisor;
+       state->close_delay     = new_serial.close_delay;
+       state->closing_wait    = closing_wait;
+       port->fifosize         = new_serial.xmit_fifo_size;
+       if (state->info->tty)
+               state->info->tty->low_latency =
+                       (port->flags & UPF_LOW_LATENCY) ? 1 : 0;
+
+check_and_exit:
+       retval = 0;
+       if (port->type == PORT_UNKNOWN)
+               goto exit;
+       if (state->info->flags & UIF_INITIALIZED) {
+               if (((old_flags ^ port->flags) & UPF_SPD_MASK) ||
+                               old_custom_divisor != port->custom_divisor) {
+                       if (port->flags & UPF_SPD_MASK) {
+                               printk(KERN_NOTICE
+                                               "%s sets custom speed on ttyMP%d. This "
+                                               "is deprecated.\n", current->comm,
+                                               port->line);
+                       }
+                       mp_change_speed(state, NULL);
+               }
+       } else
+               retval = mp_startup(state, 1);
+exit:
+       MP_STATE_UNLOCK(state);
+       return retval;
+}
+
+
+static int mp_get_lsr_info(struct sb_uart_state *state, unsigned int *value)
+{
+       struct sb_uart_port *port = state->port;
+       unsigned int result;
+
+       result = port->ops->tx_empty(port);
+
+       if (port->x_char ||
+                       ((uart_circ_chars_pending(&state->info->xmit) > 0) &&
+                               !state->info->tty->stopped && !state->info->tty->hw_stopped))
+               result &= ~TIOCSER_TEMT;
+
+       return put_user(result, value);
+}
+
+static int mp_tiocmget(struct tty_struct *tty)
+{
+       struct sb_uart_state *state = tty->driver_data;
+       struct sb_uart_port *port = state->port;
+       int result = -EIO;
+
+       MP_STATE_LOCK(state);
+       if (!(tty->flags & (1 << TTY_IO_ERROR))) {
+               result = port->mctrl;
+               spin_lock_irq(&port->lock);
+               result |= port->ops->get_mctrl(port);
+               spin_unlock_irq(&port->lock);
+       }
+       MP_STATE_UNLOCK(state);
+       return result;
+}
+
+static int mp_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear)
+{
+       struct sb_uart_state *state = tty->driver_data;
+       struct sb_uart_port *port = state->port;
+       int ret = -EIO;
+
+
+       MP_STATE_LOCK(state);
+       if (!(tty->flags & (1 << TTY_IO_ERROR))) {
+               mp_update_mctrl(port, set, clear);
+               ret = 0;
+       }
+       MP_STATE_UNLOCK(state);
+
+       return ret;
+}
+
+static int mp_break_ctl(struct tty_struct *tty, int break_state)
+{
+       struct sb_uart_state *state = tty->driver_data;
+       struct sb_uart_port *port = state->port;
+
+       MP_STATE_LOCK(state);
+
+       if (port->type != PORT_UNKNOWN)
+               port->ops->break_ctl(port, break_state);
+
+       MP_STATE_UNLOCK(state);
+       return 0;
+}
+
+static int mp_do_autoconfig(struct sb_uart_state *state)
+{
+       struct sb_uart_port *port = state->port;
+       int flags, ret;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       if (mutex_lock_interruptible(&state->mutex))
+               return -ERESTARTSYS;
+       ret = -EBUSY;
+       if (uart_users(state) == 1) {
+               mp_shutdown(state);
+
+               if (port->type != PORT_UNKNOWN)
+                       port->ops->release_port(port);
+
+               flags = UART_CONFIG_TYPE;
+               if (port->flags & UPF_AUTO_IRQ)
+                       flags |= UART_CONFIG_IRQ;
+
+               port->ops->config_port(port, flags);
+
+               ret = mp_startup(state, 1);
+       }
+       MP_STATE_UNLOCK(state);
+       return ret;
+}
+
+static int mp_wait_modem_status(struct sb_uart_state *state, unsigned long arg)
+{
+       struct sb_uart_port *port = state->port;
+       DECLARE_WAITQUEUE(wait, current);
+       struct sb_uart_icount cprev, cnow;
+       int ret;
+
+       spin_lock_irq(&port->lock);
+       memcpy(&cprev, &port->icount, sizeof(struct sb_uart_icount));
+
+       port->ops->enable_ms(port);
+       spin_unlock_irq(&port->lock);
+
+       add_wait_queue(&state->info->delta_msr_wait, &wait);
+       for (;;) {
+               spin_lock_irq(&port->lock);
+               memcpy(&cnow, &port->icount, sizeof(struct sb_uart_icount));
+               spin_unlock_irq(&port->lock);
+
+               set_current_state(TASK_INTERRUPTIBLE);
+
+               if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+                               ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+                               ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) ||
+                               ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) {
+                       ret = 0;
+                       break;
+               }
+
+               schedule();
+
+               if (signal_pending(current)) {
+                       ret = -ERESTARTSYS;
+                       break;
+               }
+
+               cprev = cnow;
+       }
+
+       current->state = TASK_RUNNING;
+       remove_wait_queue(&state->info->delta_msr_wait, &wait);
+
+       return ret;
+}
+
+static int mp_get_count(struct sb_uart_state *state, struct serial_icounter_struct *icnt)
+{
+       struct serial_icounter_struct icount;
+       struct sb_uart_icount cnow;
+       struct sb_uart_port *port = state->port;
+
+       spin_lock_irq(&port->lock);
+       memcpy(&cnow, &port->icount, sizeof(struct sb_uart_icount));
+       spin_unlock_irq(&port->lock);
+
+       icount.cts         = cnow.cts;
+       icount.dsr         = cnow.dsr;
+       icount.rng         = cnow.rng;
+       icount.dcd         = cnow.dcd;
+       icount.rx          = cnow.rx;
+       icount.tx          = cnow.tx;
+       icount.frame       = cnow.frame;
+       icount.overrun     = cnow.overrun;
+       icount.parity      = cnow.parity;
+       icount.brk         = cnow.brk;
+       icount.buf_overrun = cnow.buf_overrun;
+
+       return copy_to_user(icnt, &icount, sizeof(icount)) ? -EFAULT : 0;
+}
+
+static int mp_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
+{
+       struct sb_uart_state *state = tty->driver_data;
+       struct mp_port *info = (struct mp_port *)state->port;
+       int ret = -ENOIOCTLCMD;
+
+
+       switch (cmd) {
+               case TIOCSMULTIDROP:
+                       /* set multi-drop mode enable or disable, and default operation mode is H/W mode */
+                       if (info->port.type == PORT_16C105XA)
+                       {
+                               //arg &= ~0x6;
+                               //state->port->mdmode = 0;
+                               return set_multidrop_mode((struct sb_uart_port *)info, (unsigned int)arg);
+                       }
+                       ret = -ENOTSUPP;
+                       break;
+               case GETDEEPFIFO:
+                       ret = get_deep_fifo(state->port);
+                       return ret;
+               case SETDEEPFIFO:
+                       ret = set_deep_fifo(state->port,arg);
+                       deep[state->port->line] = arg;
+                       return ret;
+               case SETTTR:
+                       if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){
+                               ret = sb1054_set_register(state->port,PAGE_4,SB105X_TTR,arg);
+                               ttr[state->port->line] = arg;
+                       }
+                       return ret;
+               case SETRTR:
+                       if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){
+                               ret = sb1054_set_register(state->port,PAGE_4,SB105X_RTR,arg);
+                               rtr[state->port->line] = arg;
+                       }
+                       return ret;
+               case GETTTR:
+                       if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){
+                               ret = sb1054_get_register(state->port,PAGE_4,SB105X_TTR);
+                       }
+                       return ret;
+               case GETRTR:
+                       if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){
+                               ret = sb1054_get_register(state->port,PAGE_4,SB105X_RTR);
+                       }
+                       return ret;
+
+               case SETFCR:
+                       if (info->port.type == PORT_16C105X || info->port.type == PORT_16C105XA){
+                               ret = sb1054_set_register(state->port,PAGE_1,SB105X_FCR,arg);
+                       }
+                       else{
+                               serial_out(info,2,arg);
+                       }
+
+                       return ret;
+               case TIOCSMDADDR:
+                       /* set multi-drop address */
+                       if (info->port.type == PORT_16C105XA)
+                       {
+                               state->port->mdmode |= MDMODE_ADDR;
+                               return set_multidrop_addr((struct sb_uart_port *)info, (unsigned int)arg);
+                       }
+                       ret = -ENOTSUPP;
+                       break;
+
+               case TIOCGMDADDR:
+                       /* set multi-drop address */
+                       if ((info->port.type == PORT_16C105XA) && (state->port->mdmode & MDMODE_ADDR))
+                       {
+                               return get_multidrop_addr((struct sb_uart_port *)info);
+                       }
+                       ret = -ENOTSUPP;
+                       break;
+
+               case TIOCSENDADDR:
+                       /* send address in multi-drop mode */
+                       if ((info->port.type == PORT_16C105XA) 
+                                       && (state->port->mdmode & (MDMODE_ENABLE)))
+                       {
+                               if (mp_chars_in_buffer(tty) > 0)
+                               {
+                                       tty_wait_until_sent(tty, 0);
+                               }
+                               //while ((serial_in(info, UART_LSR) & 0x60) != 0x60);
+                               //while (sb1054_get_register(state->port, PAGE_2, SB105X_TFCR) != 0);
+                               while ((serial_in(info, UART_LSR) & 0x60) != 0x60);
+                               serial_out(info, UART_SCR, (int)arg);
+                       }
+                       break;
+
+               case TIOCGSERIAL:
+                       ret = mp_get_info(state, (struct serial_struct *)arg);
+                       break;
+
+               case TIOCSSERIAL:
+                       ret = mp_set_info(state, (struct serial_struct *)arg);
+                       break;
+
+               case TIOCSERCONFIG:
+                       ret = mp_do_autoconfig(state);
+                       break;
+
+               case TIOCSERGWILD: /* obsolete */
+               case TIOCSERSWILD: /* obsolete */
+                       ret = 0;
+                       break;
+                       /* for Multiport */
+               case TIOCGNUMOFPORT: /* Get number of ports */
+                       return NR_PORTS;
+               case TIOCGGETDEVID:
+                       return mp_devs[arg].device_id;
+               case TIOCGGETREV:
+                       return mp_devs[arg].revision;
+               case TIOCGGETNRPORTS:
+                       return mp_devs[arg].nr_ports;
+               case TIOCGGETBDNO:
+                       return NR_BOARD;
+               case TIOCGGETINTERFACE:
+                       if (mp_devs[arg].revision == 0xc0)
+                       {
+                               /* for SB16C1053APCI */
+                               return (sb1053a_get_interface(info, info->port.line));
+                       }
+                       else
+                       {
+                               return (inb(mp_devs[arg].option_reg_addr+MP_OPTR_IIR0+(state->port->line/8)));
+                       }
+               case TIOCGGETPORTTYPE:
+                       ret = get_device_type(arg);;
+                       return ret;
+               case TIOCSMULTIECHO: /* set to multi-drop mode(RS422) or echo mode(RS485)*/
+                       outb( ( inb(info->interface_config_addr) & ~0x03 ) | 0x01 ,  
+                                       info->interface_config_addr);
+                       return 0;
+               case TIOCSPTPNOECHO: /* set to multi-drop mode(RS422) or echo mode(RS485) */
+                       outb( ( inb(info->interface_config_addr) & ~0x03 )  ,             
+                                       info->interface_config_addr);
+                       return 0;
+       }
+
+       if (ret != -ENOIOCTLCMD)
+               goto out;
+
+       if (tty->flags & (1 << TTY_IO_ERROR)) {
+               ret = -EIO;
+               goto out;
+       }
+
+       switch (cmd) {
+               case TIOCMIWAIT:
+                       ret = mp_wait_modem_status(state, arg);
+                       break;
+
+               case TIOCGICOUNT:
+                       ret = mp_get_count(state, (struct serial_icounter_struct *)arg);
+                       break;
+       }
+
+       if (ret != -ENOIOCTLCMD)
+               goto out;
+
+       MP_STATE_LOCK(state);
+       switch (cmd) {
+               case TIOCSERGETLSR: /* Get line status register */
+                       ret = mp_get_lsr_info(state, (unsigned int *)arg);
+                       break;
+
+               default: {
+                                       struct sb_uart_port *port = state->port;
+                                       if (port->ops->ioctl)
+                                               ret = port->ops->ioctl(port, cmd, arg);
+                                       break;
+                               }
+       }
+
+       MP_STATE_UNLOCK(state);
+out:
+       return ret;
+}
+
+static void mp_set_termios(struct tty_struct *tty, struct MP_TERMIOS *old_termios)
+{
+       struct sb_uart_state *state = tty->driver_data;
+       unsigned long flags;
+       unsigned int cflag = tty->termios.c_cflag;
+
+#define RELEVANT_IFLAG(iflag)  ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+       if ((cflag ^ old_termios->c_cflag) == 0 &&
+                       RELEVANT_IFLAG(tty->termios.c_iflag ^ old_termios->c_iflag) == 0)
+               return;
+
+       mp_change_speed(state, old_termios);
+
+       if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD))
+               uart_clear_mctrl(state->port, TIOCM_RTS | TIOCM_DTR);
+
+       if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) {
+               unsigned int mask = TIOCM_DTR;
+               if (!(cflag & CRTSCTS) ||
+                               !test_bit(TTY_THROTTLED, &tty->flags))
+                       mask |= TIOCM_RTS;
+               uart_set_mctrl(state->port, mask);
+       }
+
+       if ((old_termios->c_cflag & CRTSCTS) && !(cflag & CRTSCTS)) {
+               spin_lock_irqsave(&state->port->lock, flags);
+               tty->hw_stopped = 0;
+               __mp_start(tty);
+               spin_unlock_irqrestore(&state->port->lock, flags);
+       }
+
+       if (!(old_termios->c_cflag & CRTSCTS) && (cflag & CRTSCTS)) {
+               spin_lock_irqsave(&state->port->lock, flags);
+               if (!(state->port->ops->get_mctrl(state->port) & TIOCM_CTS)) {
+                       tty->hw_stopped = 1;
+                       state->port->ops->stop_tx(state->port);
+               }
+               spin_unlock_irqrestore(&state->port->lock, flags);
+       }
+}
+
+static void mp_close(struct tty_struct *tty, struct file *filp)
+{
+       struct sb_uart_state *state = tty->driver_data;
+       struct sb_uart_port *port;
+
+       printk("mp_close!\n");
+       if (!state || !state->port)
+               return;
+
+       port = state->port;
+
+       printk("close1 %d\n", __LINE__);
+       MP_STATE_LOCK(state);
+
+       printk("close2 %d\n", __LINE__);
+       if (tty_hung_up_p(filp))
+               goto done;
+
+       printk("close3 %d\n", __LINE__);
+       if ((tty->count == 1) && (state->count != 1)) {
+               printk("mp_close: bad serial port count; tty->count is 1, "
+                               "state->count is %d\n", state->count);
+               state->count = 1;
+       }
+       printk("close4 %d\n", __LINE__);
+       if (--state->count < 0) {
+               printk("rs_close: bad serial port count for ttyMP%d: %d\n",
+                               port->line, state->count);
+               state->count = 0;
+       }
+       if (state->count)
+               goto done;
+
+       tty->closing = 1;
+
+       printk("close5 %d\n", __LINE__);
+       if (state->closing_wait != USF_CLOSING_WAIT_NONE)
+               tty_wait_until_sent(tty, state->closing_wait);
+
+       printk("close6 %d\n", __LINE__);
+       if (state->info->flags & UIF_INITIALIZED) {
+               unsigned long flags;
+               spin_lock_irqsave(&port->lock, flags);
+               port->ops->stop_rx(port);
+               spin_unlock_irqrestore(&port->lock, flags);
+               mp_wait_until_sent(tty, port->timeout);
+       }
+       printk("close7 %d\n", __LINE__);
+
+       mp_shutdown(state);
+       printk("close8 %d\n", __LINE__);
+       mp_flush_buffer(tty);
+       tty_ldisc_flush(tty);
+       tty->closing = 0;
+       state->info->tty = NULL;
+       if (state->info->blocked_open) 
+       {
+               if (state->close_delay)
+               {
+                       set_current_state(TASK_INTERRUPTIBLE);
+                       schedule_timeout(state->close_delay);
+               }
+       }
+       else
+       {
+               mp_change_pm(state, 3);
+       }
+       printk("close8 %d\n", __LINE__);
+
+       state->info->flags &= ~UIF_NORMAL_ACTIVE;
+       wake_up_interruptible(&state->info->open_wait);
+
+done:
+       printk("close done\n");
+       MP_STATE_UNLOCK(state);
+       module_put(THIS_MODULE);
+}
+
+static void mp_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+       struct sb_uart_state *state = tty->driver_data;
+       struct sb_uart_port *port = state->port;
+       unsigned long char_time, expire;
+
+       if (port->type == PORT_UNKNOWN || port->fifosize == 0)
+               return;
+
+       char_time = (port->timeout - HZ/50) / port->fifosize;
+       char_time = char_time / 5;
+       if (char_time == 0)
+               char_time = 1;
+       if (timeout && timeout < char_time)
+               char_time = timeout;
+
+       if (timeout == 0 || timeout > 2 * port->timeout)
+               timeout = 2 * port->timeout;
+
+       expire = jiffies + timeout;
+
+       while (!port->ops->tx_empty(port)) {
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(char_time);
+               if (signal_pending(current))
+                       break;
+               if (time_after(jiffies, expire))
+                       break;
+       }
+       set_current_state(TASK_RUNNING); /* might not be needed */
+}
+
+static void mp_hangup(struct tty_struct *tty)
+{
+       struct sb_uart_state *state = tty->driver_data;
+
+       MP_STATE_LOCK(state);
+       if (state->info && state->info->flags & UIF_NORMAL_ACTIVE) {
+               mp_flush_buffer(tty);
+               mp_shutdown(state);
+               state->count = 0;
+               state->info->flags &= ~UIF_NORMAL_ACTIVE;
+               state->info->tty = NULL;
+               wake_up_interruptible(&state->info->open_wait);
+               wake_up_interruptible(&state->info->delta_msr_wait);
+       }
+       MP_STATE_UNLOCK(state);
+}
+
+static void mp_update_termios(struct sb_uart_state *state)
+{
+       struct tty_struct *tty = state->info->tty;
+       struct sb_uart_port *port = state->port;
+
+       if (!(tty->flags & (1 << TTY_IO_ERROR))) {
+               mp_change_speed(state, NULL);
+
+               if (tty->termios.c_cflag & CBAUD)
+                       uart_set_mctrl(port, TIOCM_DTR | TIOCM_RTS);
+       }
+}
+
+static int mp_block_til_ready(struct file *filp, struct sb_uart_state *state)
+{
+       DECLARE_WAITQUEUE(wait, current);
+       struct sb_uart_info *info = state->info;
+       struct sb_uart_port *port = state->port;
+       unsigned int mctrl;
+
+       info->blocked_open++;
+       state->count--;
+
+       add_wait_queue(&info->open_wait, &wait);
+       while (1) {
+               set_current_state(TASK_INTERRUPTIBLE);
+
+               if (tty_hung_up_p(filp) || info->tty == NULL)
+                       break;
+
+               if (!(info->flags & UIF_INITIALIZED))
+                       break;
+
+               if ((filp->f_flags & O_NONBLOCK) ||
+                               (info->tty->termios.c_cflag & CLOCAL) ||
+                               (info->tty->flags & (1 << TTY_IO_ERROR))) {
+                       break;
+               }
+
+               if (info->tty->termios.c_cflag & CBAUD)
+                       uart_set_mctrl(port, TIOCM_DTR);
+
+               spin_lock_irq(&port->lock);
+               port->ops->enable_ms(port);
+               mctrl = port->ops->get_mctrl(port);
+               spin_unlock_irq(&port->lock);
+               if (mctrl & TIOCM_CAR)
+                       break;
+
+               MP_STATE_UNLOCK(state);
+               schedule();
+               MP_STATE_LOCK(state);
+
+               if (signal_pending(current))
+                       break;
+       }
+       set_current_state(TASK_RUNNING);
+       remove_wait_queue(&info->open_wait, &wait);
+
+       state->count++;
+       info->blocked_open--;
+
+       if (signal_pending(current))
+               return -ERESTARTSYS;
+
+       if (!info->tty || tty_hung_up_p(filp))
+               return -EAGAIN;
+
+       return 0;
+}
+
+static struct sb_uart_state *uart_get(struct uart_driver *drv, int line)
+{
+       struct sb_uart_state *state;
+
+       MP_MUTEX_LOCK(mp_mutex);
+       state = drv->state + line;
+       if (mutex_lock_interruptible(&state->mutex)) {
+               state = ERR_PTR(-ERESTARTSYS);
+               goto out;
+       }
+       state->count++;
+       if (!state->port) {
+               state->count--;
+               MP_STATE_UNLOCK(state);
+               state = ERR_PTR(-ENXIO);
+               goto out;
+       }
+
+       if (!state->info) {
+               state->info = kmalloc(sizeof(struct sb_uart_info), GFP_KERNEL);
+               if (state->info) {
+                       memset(state->info, 0, sizeof(struct sb_uart_info));
+                       init_waitqueue_head(&state->info->open_wait);
+                       init_waitqueue_head(&state->info->delta_msr_wait);
+
+                       state->port->info = state->info;
+
+                       tasklet_init(&state->info->tlet, mp_tasklet_action,
+                                       (unsigned long)state);
+               } else {
+                       state->count--;
+                       MP_STATE_UNLOCK(state);
+                       state = ERR_PTR(-ENOMEM);
+               }
+       }
+
+out:
+       MP_MUTEX_UNLOCK(mp_mutex);
+       return state;
+}
+
+static int mp_open(struct tty_struct *tty, struct file *filp)
+{
+       struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state;
+       struct sb_uart_state *state;
+       int retval;
+       int  line = tty->index;
+       struct mp_port *mtpt;
+
+       retval = -ENODEV;
+       if (line >= tty->driver->num)
+               goto fail;
+
+       state = uart_get(drv, line);
+
+       mtpt  = (struct mp_port *)state->port;
+
+       if (IS_ERR(state)) {
+               retval = PTR_ERR(state);
+               goto fail;
+       }
+
+       tty->driver_data = state;
+       tty->low_latency = (state->port->flags & UPF_LOW_LATENCY) ? 1 : 0;
+       tty->alt_speed = 0;
+       state->info->tty = tty;
+
+       if (tty_hung_up_p(filp)) {
+               retval = -EAGAIN;
+               state->count--;
+               MP_STATE_UNLOCK(state);
+               goto fail;
+       }
+
+       if (state->count == 1)
+               mp_change_pm(state, 0);
+
+       retval = mp_startup(state, 0);
+
+       if (retval == 0)
+               retval = mp_block_til_ready(filp, state);
+       MP_STATE_UNLOCK(state);
+
+       if (retval == 0 && !(state->info->flags & UIF_NORMAL_ACTIVE)) {
+               state->info->flags |= UIF_NORMAL_ACTIVE;
+
+               mp_update_termios(state);
+       }
+
+       uart_clear_mctrl(state->port, TIOCM_RTS);
+       try_module_get(THIS_MODULE);
+fail:
+       return retval;
+}
+
+
+static const char *mp_type(struct sb_uart_port *port)
+{
+       const char *str = NULL;
+
+       if (port->ops->type)
+               str = port->ops->type(port);
+
+       if (!str)
+               str = "unknown";
+
+       return str;
+}
+
+static void mp_change_pm(struct sb_uart_state *state, int pm_state)
+{
+       struct sb_uart_port *port = state->port;
+       if (port->ops->pm)
+               port->ops->pm(port, pm_state, state->pm_state);
+       state->pm_state = pm_state;
+}
+
+static inline void mp_report_port(struct uart_driver *drv, struct sb_uart_port *port)
+{
+       char address[64];
+
+       switch (port->iotype) {
+               case UPIO_PORT:
+                       snprintf(address, sizeof(address),"I/O 0x%x", port->iobase);
+                       break;
+               case UPIO_HUB6:
+                       snprintf(address, sizeof(address),"I/O 0x%x offset 0x%x", port->iobase, port->hub6);
+                       break;
+               case UPIO_MEM:
+                       snprintf(address, sizeof(address),"MMIO 0x%lx", port->mapbase);
+                       break;
+               default:
+                       snprintf(address, sizeof(address),"*unknown*" );
+                       strlcpy(address, "*unknown*", sizeof(address));
+                       break;
+       }
+
+       printk( "%s%d at %s (irq = %d) is a %s\n",
+                       drv->dev_name, port->line, address, port->irq, mp_type(port));
+
+}
+
+static void mp_configure_port(struct uart_driver *drv, struct sb_uart_state *state, struct sb_uart_port *port)
+{
+       unsigned int flags;
+
+
+       if (!port->iobase && !port->mapbase && !port->membase)
+       {
+               DPRINTK("%s error \n",__FUNCTION__);
+               return;
+       }
+       flags = UART_CONFIG_TYPE;
+       if (port->flags & UPF_AUTO_IRQ)
+               flags |= UART_CONFIG_IRQ;
+       if (port->flags & UPF_BOOT_AUTOCONF) {
+               port->type = PORT_UNKNOWN;
+               port->ops->config_port(port, flags);
+       }
+
+       if (port->type != PORT_UNKNOWN) {
+               unsigned long flags;
+
+               mp_report_port(drv, port);
+
+               spin_lock_irqsave(&port->lock, flags);
+               port->ops->set_mctrl(port, 0);
+               spin_unlock_irqrestore(&port->lock, flags);
+
+               mp_change_pm(state, 3);
+       }
+}
+
+static void mp_unconfigure_port(struct uart_driver *drv, struct sb_uart_state *state)
+{
+       struct sb_uart_port *port = state->port;
+       struct sb_uart_info *info = state->info;
+
+       if (info && info->tty)
+               tty_hangup(info->tty);
+
+       MP_STATE_LOCK(state);
+
+       state->info = NULL;
+
+       if (port->type != PORT_UNKNOWN)
+               port->ops->release_port(port);
+
+       port->type = PORT_UNKNOWN;
+
+       if (info) {
+               tasklet_kill(&info->tlet);
+               kfree(info);
+       }
+
+       MP_STATE_UNLOCK(state);
+}
+static struct tty_operations mp_ops = {
+       .open           = mp_open,
+       .close          = mp_close,
+       .write          = mp_write,
+       .put_char       = mp_put_char,
+       .flush_chars    = mp_put_chars,
+       .write_room     = mp_write_room,
+       .chars_in_buffer= mp_chars_in_buffer,
+       .flush_buffer   = mp_flush_buffer,
+       .ioctl          = mp_ioctl,
+       .throttle       = mp_throttle,
+       .unthrottle     = mp_unthrottle,
+       .send_xchar     = mp_send_xchar,
+       .set_termios    = mp_set_termios,
+       .stop           = mp_stop,
+       .start          = mp_start,
+       .hangup         = mp_hangup,
+       .break_ctl      = mp_break_ctl,
+       .wait_until_sent= mp_wait_until_sent,
+#ifdef CONFIG_PROC_FS
+       .proc_fops      = NULL,
+#endif
+       .tiocmget       = mp_tiocmget,
+       .tiocmset       = mp_tiocmset,
+};
+
+static int mp_register_driver(struct uart_driver *drv)
+{
+       struct tty_driver *normal = NULL;
+       int i, retval;
+
+       drv->state = kmalloc(sizeof(struct sb_uart_state) * drv->nr, GFP_KERNEL);
+       retval = -ENOMEM;
+       if (!drv->state)
+       {
+               printk("SB PCI Error: Kernel memory allocation error!\n");
+               goto out;
+       }
+       memset(drv->state, 0, sizeof(struct sb_uart_state) * drv->nr);
+
+       normal = alloc_tty_driver(drv->nr);
+       if (!normal)
+       {
+               printk("SB PCI Error: tty allocation error!\n");
+               goto out;
+       }
+
+       drv->tty_driver = normal;
+
+       normal->owner           = drv->owner;
+       normal->magic           = TTY_DRIVER_MAGIC;
+       normal->driver_name     = drv->driver_name;
+       normal->name            = drv->dev_name;
+       normal->major           = drv->major;
+       normal->minor_start     = drv->minor;
+
+       normal->num             = MAX_MP_PORT ; 
+
+       normal->type            = TTY_DRIVER_TYPE_SERIAL;
+       normal->subtype         = SERIAL_TYPE_NORMAL;
+       normal->init_termios    = tty_std_termios;
+       normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+       normal->flags           = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+       normal->driver_state    = drv;
+
+       tty_set_operations(normal, &mp_ops);
+
+for (i = 0; i < drv->nr; i++) {
+       struct sb_uart_state *state = drv->state + i;
+
+       state->close_delay     = 500;   
+       state->closing_wait    = 30000; 
+
+       mutex_init(&state->mutex);
+       }
+
+       retval = tty_register_driver(normal);
+out:
+       if (retval < 0) {
+               printk("Register tty driver Fail!\n");
+               put_tty_driver(normal);
+               kfree(drv->state);
+       }
+
+       return retval;
+}
+
+void mp_unregister_driver(struct uart_driver *drv)
+{
+    struct tty_driver *normal = NULL;
+
+    normal = drv->tty_driver;
+
+    if (!normal)
+    {
+        return;
+    }
+
+    tty_unregister_driver(normal);
+    put_tty_driver(normal);
+    drv->tty_driver = NULL;
+
+
+    if (drv->state)
+    {
+        kfree(drv->state);
+    }
+
+}
+
+static int mp_add_one_port(struct uart_driver *drv, struct sb_uart_port *port)
+{
+       struct sb_uart_state *state;
+       int ret = 0;
+
+
+       if (port->line >= drv->nr)
+               return -EINVAL;
+
+       state = drv->state + port->line;
+
+       MP_MUTEX_LOCK(mp_mutex);
+       if (state->port) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       state->port = port;
+
+       spin_lock_init(&port->lock);
+       port->cons = drv->cons;
+       port->info = state->info;
+
+       mp_configure_port(drv, state, port);
+
+       tty_register_device(drv->tty_driver, port->line, port->dev);
+
+out:
+       MP_MUTEX_UNLOCK(mp_mutex);
+
+
+       return ret;
+}
+
+static int mp_remove_one_port(struct uart_driver *drv, struct sb_uart_port *port)
+{
+       struct sb_uart_state *state = drv->state + port->line;
+
+       if (state->port != port)
+               printk(KERN_ALERT "Removing wrong port: %p != %p\n",
+                               state->port, port);
+
+       MP_MUTEX_LOCK(mp_mutex);
+
+       tty_unregister_device(drv->tty_driver, port->line);
+
+       mp_unconfigure_port(drv, state);
+       state->port = NULL;
+       MP_MUTEX_UNLOCK(mp_mutex);
+
+       return 0;
+}
+
+static void autoconfig(struct mp_port *mtpt, unsigned int probeflags)
+{
+       unsigned char status1, scratch, scratch2, scratch3;
+       unsigned char save_lcr, save_mcr;
+       unsigned long flags;
+
+       unsigned char u_type;
+       unsigned char b_ret = 0;
+
+       if (!mtpt->port.iobase && !mtpt->port.mapbase && !mtpt->port.membase)
+               return;
+
+       DEBUG_AUTOCONF("ttyMP%d: autoconf (0x%04x, 0x%p): ",
+                       mtpt->port.line, mtpt->port.iobase, mtpt->port.membase);
+
+       spin_lock_irqsave(&mtpt->port.lock, flags);
+
+       if (!(mtpt->port.flags & UPF_BUGGY_UART)) {
+               scratch = serial_inp(mtpt, UART_IER);
+               serial_outp(mtpt, UART_IER, 0);
+#ifdef __i386__
+               outb(0xff, 0x080);
+#endif
+               scratch2 = serial_inp(mtpt, UART_IER) & 0x0f;
+               serial_outp(mtpt, UART_IER, 0x0F);
+#ifdef __i386__
+               outb(0, 0x080);
+#endif
+               scratch3 = serial_inp(mtpt, UART_IER) & 0x0F;
+               serial_outp(mtpt, UART_IER, scratch);
+               if (scratch2 != 0 || scratch3 != 0x0F) {
+                       DEBUG_AUTOCONF("IER test failed (%02x, %02x) ",
+                                       scratch2, scratch3);
+                       goto out;
+               }
+       }
+
+       save_mcr = serial_in(mtpt, UART_MCR);
+       save_lcr = serial_in(mtpt, UART_LCR);
+
+       if (!(mtpt->port.flags & UPF_SKIP_TEST)) {
+               serial_outp(mtpt, UART_MCR, UART_MCR_LOOP | 0x0A);
+               status1 = serial_inp(mtpt, UART_MSR) & 0xF0;
+               serial_outp(mtpt, UART_MCR, save_mcr);
+               if (status1 != 0x90) {
+                       DEBUG_AUTOCONF("LOOP test failed (%02x) ",
+                                       status1);
+                       goto out;
+               }
+       }
+
+       serial_outp(mtpt, UART_LCR, 0xBF);
+       serial_outp(mtpt, UART_EFR, 0);
+       serial_outp(mtpt, UART_LCR, 0);
+
+       serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO);
+       scratch = serial_in(mtpt, UART_IIR) >> 6;
+
+       DEBUG_AUTOCONF("iir=%d ", scratch);
+       if(mtpt->device->nr_ports >= 8)
+               b_ret = read_option_register(mtpt,(MP_OPTR_DIR0 + ((mtpt->port.line)/8)));
+       else    
+               b_ret = read_option_register(mtpt,MP_OPTR_DIR0);
+       u_type = (b_ret & 0xf0) >> 4;
+       if(mtpt->port.type == PORT_UNKNOWN )
+       {
+               switch (u_type)
+               {
+                       case DIR_UART_16C550:
+                               mtpt->port.type = PORT_16C55X;
+                               break;
+                       case DIR_UART_16C1050:
+                               mtpt->port.type = PORT_16C105X;
+                               break;
+                       case DIR_UART_16C1050A:
+                               if (mtpt->port.line < 2)
+                               {
+                                       mtpt->port.type = PORT_16C105XA;
+                               }
+                               else
+                               {
+                                       if (mtpt->device->device_id & 0x50)
+                                       {
+                                               mtpt->port.type = PORT_16C55X;
+                                       }
+                                       else
+                                       {
+                                               mtpt->port.type = PORT_16C105X;
+                                       }
+                               }
+                               break;
+                       default:        
+                               mtpt->port.type = PORT_UNKNOWN;
+                               break;
+               }
+       }
+
+       if(mtpt->port.type == PORT_UNKNOWN )
+       {
+printk("unknow2\n");
+               switch (scratch) {
+                       case 0:
+                       case 1:
+                               mtpt->port.type = PORT_UNKNOWN;
+                               break;
+                       case 2:
+                       case 3:
+                               mtpt->port.type = PORT_16C55X;
+                               break;
+               }
+       }
+
+       serial_outp(mtpt, UART_LCR, save_lcr);
+
+       mtpt->port.fifosize = uart_config[mtpt->port.type].dfl_xmit_fifo_size;
+       mtpt->capabilities = uart_config[mtpt->port.type].flags;
+
+       if (mtpt->port.type == PORT_UNKNOWN)
+               goto out;
+       serial_outp(mtpt, UART_MCR, save_mcr);
+       serial_outp(mtpt, UART_FCR, (UART_FCR_ENABLE_FIFO |
+                               UART_FCR_CLEAR_RCVR |
+                               UART_FCR_CLEAR_XMIT));
+       serial_outp(mtpt, UART_FCR, 0);
+       (void)serial_in(mtpt, UART_RX);
+       serial_outp(mtpt, UART_IER, 0);
+
+out:
+       spin_unlock_irqrestore(&mtpt->port.lock, flags);
+       DEBUG_AUTOCONF("type=%s\n", uart_config[mtpt->port.type].name);
+}
+
+static void autoconfig_irq(struct mp_port *mtpt)
+{
+       unsigned char save_mcr, save_ier;
+       unsigned long irqs;
+       int irq;
+
+       /* forget possible initially masked and pending IRQ */
+       probe_irq_off(probe_irq_on());
+       save_mcr = serial_inp(mtpt, UART_MCR);
+       save_ier = serial_inp(mtpt, UART_IER);
+       serial_outp(mtpt, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2);
+
+       irqs = probe_irq_on();
+       serial_outp(mtpt, UART_MCR, 0);
+       serial_outp(mtpt, UART_MCR,
+               UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
+
+       serial_outp(mtpt, UART_IER, 0x0f);    /* enable all intrs */
+       (void)serial_inp(mtpt, UART_LSR);
+       (void)serial_inp(mtpt, UART_RX);
+       (void)serial_inp(mtpt, UART_IIR);
+       (void)serial_inp(mtpt, UART_MSR);
+       serial_outp(mtpt, UART_TX, 0xFF);
+       irq = probe_irq_off(irqs);
+
+       serial_outp(mtpt, UART_MCR, save_mcr);
+       serial_outp(mtpt, UART_IER, save_ier);
+
+       mtpt->port.irq = (irq > 0) ? irq : 0;
+}
+
+static void multi_stop_tx(struct sb_uart_port *port)
+{
+       struct mp_port *mtpt = (struct mp_port *)port;
+
+       if (mtpt->ier & UART_IER_THRI) {
+               mtpt->ier &= ~UART_IER_THRI;
+               serial_out(mtpt, UART_IER, mtpt->ier);
+       }
+
+       tasklet_schedule(&port->info->tlet);
+}
+
+static void multi_start_tx(struct sb_uart_port *port)
+{
+       struct mp_port *mtpt = (struct mp_port *)port;
+
+       if (!(mtpt->ier & UART_IER_THRI)) {
+               mtpt->ier |= UART_IER_THRI;
+               serial_out(mtpt, UART_IER, mtpt->ier);
+       }
+}
+
+static void multi_stop_rx(struct sb_uart_port *port)
+{
+       struct mp_port *mtpt = (struct mp_port *)port;
+
+       mtpt->ier &= ~UART_IER_RLSI;
+       mtpt->port.read_status_mask &= ~UART_LSR_DR;
+       serial_out(mtpt, UART_IER, mtpt->ier);
+}
+
+static void multi_enable_ms(struct sb_uart_port *port)
+{
+       struct mp_port *mtpt = (struct mp_port *)port;
+
+       mtpt->ier |= UART_IER_MSI;
+       serial_out(mtpt, UART_IER, mtpt->ier);
+}
+
+
+static _INLINE_ void receive_chars(struct mp_port *mtpt, int *status )
+{
+       struct tty_struct *tty = mtpt->port.info->tty;
+       unsigned char lsr = *status;
+       int max_count = 256;
+       unsigned char ch;
+       char flag;
+
+       //lsr &= mtpt->port.read_status_mask;
+
+       do {
+               if ((lsr & UART_LSR_PE) && (mtpt->port.mdmode & MDMODE_ENABLE))
+               {
+                       ch = serial_inp(mtpt, UART_RX);
+               }
+               else if (lsr & UART_LSR_SPECIAL) 
+               {
+                       flag = 0;
+                       ch = serial_inp(mtpt, UART_RX);
+
+                       if (lsr & UART_LSR_BI) 
+                       {
+
+                               mtpt->port.icount.brk++;
+                               flag = TTY_BREAK;
+
+                               if (sb_uart_handle_break(&mtpt->port))
+                                       goto ignore_char;
+                       } 
+                       if (lsr & UART_LSR_PE)
+                       {
+                               mtpt->port.icount.parity++;
+                               flag = TTY_PARITY;
+                       }
+                       if (lsr & UART_LSR_FE)
+                       {
+                               mtpt->port.icount.frame++;
+                               flag = TTY_FRAME;
+                       }
+                       if (lsr & UART_LSR_OE)
+                       {
+                               mtpt->port.icount.overrun++;
+                               flag = TTY_OVERRUN;
+                       }
+                       tty_insert_flip_char(tty, ch, flag);
+               }
+               else
+               {
+                       ch = serial_inp(mtpt, UART_RX);
+                       tty_insert_flip_char(tty, ch, 0);
+               }
+ignore_char:
+               lsr = serial_inp(mtpt, UART_LSR);
+       } while ((lsr & UART_LSR_DR) && (max_count-- > 0));
+
+       tty_flip_buffer_push(tty);
+}
+
+
+
+
+static _INLINE_ void transmit_chars(struct mp_port *mtpt)
+{
+       struct circ_buf *xmit = &mtpt->port.info->xmit;
+       int count;
+
+       if (mtpt->port.x_char) {
+               serial_outp(mtpt, UART_TX, mtpt->port.x_char);
+               mtpt->port.icount.tx++;
+               mtpt->port.x_char = 0;
+               return;
+       }
+       if (uart_circ_empty(xmit) || uart_tx_stopped(&mtpt->port)) {
+               multi_stop_tx(&mtpt->port);
+               return;
+       }
+
+       count = uart_circ_chars_pending(xmit);
+
+       if(count > mtpt->port.fifosize)
+       {
+               count = mtpt->port.fifosize;
+       }
+
+       printk("[%d] mdmode: %x\n", mtpt->port.line, mtpt->port.mdmode);
+       do {
+#if 0
+               /* check multi-drop mode */
+               if ((mtpt->port.mdmode & (MDMODE_ENABLE | MDMODE_ADDR)) == (MDMODE_ENABLE | MDMODE_ADDR))
+               {
+                       printk("send address\n");
+                       /* send multi-drop address */
+                       serial_out(mtpt, UART_SCR, xmit->buf[xmit->tail]);
+               }
+               else
+#endif
+               {
+                       serial_out(mtpt, UART_TX, xmit->buf[xmit->tail]);
+               }
+               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+               mtpt->port.icount.tx++;
+       } while (--count > 0);
+}
+
+
+
+static _INLINE_ void check_modem_status(struct mp_port *mtpt)
+{
+       int status;
+
+       status = serial_in(mtpt, UART_MSR);
+
+       if ((status & UART_MSR_ANY_DELTA) == 0)
+               return;
+
+       if (status & UART_MSR_TERI)
+               mtpt->port.icount.rng++;
+       if (status & UART_MSR_DDSR)
+               mtpt->port.icount.dsr++;
+       if (status & UART_MSR_DDCD)
+               sb_uart_handle_dcd_change(&mtpt->port, status & UART_MSR_DCD);
+       if (status & UART_MSR_DCTS)
+               sb_uart_handle_cts_change(&mtpt->port, status & UART_MSR_CTS);
+
+       wake_up_interruptible(&mtpt->port.info->delta_msr_wait);
+}
+
+static inline void multi_handle_port(struct mp_port *mtpt)
+{
+       unsigned int status = serial_inp(mtpt, UART_LSR);
+
+       //printk("lsr: %x\n", status);
+
+       if ((status & UART_LSR_DR) || (status & UART_LSR_SPECIAL))
+               receive_chars(mtpt, &status);
+       check_modem_status(mtpt);
+       if (status & UART_LSR_THRE)
+       {
+               if ((mtpt->port.type == PORT_16C105X)
+                       || (mtpt->port.type == PORT_16C105XA))
+                       transmit_chars(mtpt);
+               else
+               {
+                       if (mtpt->interface >= RS485NE)
+                               uart_set_mctrl(&mtpt->port, TIOCM_RTS);
+                       
+                       transmit_chars(mtpt);
+
+
+                       if (mtpt->interface >= RS485NE)
+                       {
+                               while((status=serial_in(mtpt,UART_LSR) &0x60)!=0x60);
+                               uart_clear_mctrl(&mtpt->port, TIOCM_RTS);
+                       }
+               }
+       }
+}
+
+
+
+static irqreturn_t multi_interrupt(int irq, void *dev_id)
+{
+       struct irq_info *iinfo = dev_id;
+       struct list_head *lhead, *end = NULL;
+       int pass_counter = 0;
+
+
+       spin_lock(&iinfo->lock);
+
+       lhead = iinfo->head;
+       do {
+               struct mp_port *mtpt;
+               unsigned int iir;
+
+               mtpt = list_entry(lhead, struct mp_port, list);
+               
+               iir = serial_in(mtpt, UART_IIR);
+               printk("intrrupt! port %d, iir 0x%x\n", mtpt->port.line, iir); //wlee
+               if (!(iir & UART_IIR_NO_INT)) 
+               {
+                       printk("interrupt handle\n");
+                       spin_lock(&mtpt->port.lock);
+                       multi_handle_port(mtpt);
+                       spin_unlock(&mtpt->port.lock);
+
+                       end = NULL;
+               } else if (end == NULL)
+                       end = lhead;
+
+               lhead = lhead->next;
+               if (lhead == iinfo->head && pass_counter++ > PASS_LIMIT) 
+               {
+                       printk(KERN_ERR "multi: too much work for "
+                                       "irq%d\n", irq);
+                       printk( "multi: too much work for "
+                                       "irq%d\n", irq);
+                       break;
+               }
+       } while (lhead != end);
+
+       spin_unlock(&iinfo->lock);
+
+
+        return IRQ_HANDLED;
+}
+
+static void serial_do_unlink(struct irq_info *i, struct mp_port *mtpt)
+{
+       spin_lock_irq(&i->lock);
+
+       if (!list_empty(i->head)) {
+               if (i->head == &mtpt->list)
+                       i->head = i->head->next;
+               list_del(&mtpt->list);
+       } else {
+               i->head = NULL;
+       }
+
+       spin_unlock_irq(&i->lock);
+}
+
+static int serial_link_irq_chain(struct mp_port *mtpt)
+{
+       struct irq_info *i = irq_lists + mtpt->port.irq;
+       int ret, irq_flags = mtpt->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0;
+       spin_lock_irq(&i->lock);
+
+       if (i->head) {
+               list_add(&mtpt->list, i->head);
+               spin_unlock_irq(&i->lock);
+
+               ret = 0;
+       } else {
+               INIT_LIST_HEAD(&mtpt->list);
+               i->head = &mtpt->list;
+               spin_unlock_irq(&i->lock);
+
+               ret = request_irq(mtpt->port.irq, multi_interrupt,
+                               irq_flags, "serial", i);
+               if (ret < 0)
+                       serial_do_unlink(i, mtpt);
+       }
+
+       return ret;
+}
+
+
+
+
+static void serial_unlink_irq_chain(struct mp_port *mtpt)
+{
+       struct irq_info *i = irq_lists + mtpt->port.irq;
+
+       if (list_empty(i->head))
+       {
+               free_irq(mtpt->port.irq, i);
+       }
+       serial_do_unlink(i, mtpt);
+}
+
+static void multi_timeout(unsigned long data)
+{
+       struct mp_port *mtpt = (struct mp_port *)data;
+
+
+       spin_lock(&mtpt->port.lock);
+       multi_handle_port(mtpt);
+       spin_unlock(&mtpt->port.lock);
+
+       mod_timer(&mtpt->timer, jiffies+1 );
+}
+
+static unsigned int multi_tx_empty(struct sb_uart_port *port)
+{
+       struct mp_port *mtpt = (struct mp_port *)port;
+       unsigned long flags;
+       unsigned int ret;
+
+       spin_lock_irqsave(&mtpt->port.lock, flags);
+       ret = serial_in(mtpt, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
+       spin_unlock_irqrestore(&mtpt->port.lock, flags);
+
+       return ret;
+}
+
+
+static unsigned int multi_get_mctrl(struct sb_uart_port *port)
+{
+       struct mp_port *mtpt = (struct mp_port *)port;
+       unsigned char status;
+       unsigned int ret;
+
+       status = serial_in(mtpt, UART_MSR);
+
+       ret = 0;
+       if (status & UART_MSR_DCD)
+               ret |= TIOCM_CAR;
+       if (status & UART_MSR_RI)
+               ret |= TIOCM_RNG;
+       if (status & UART_MSR_DSR)
+               ret |= TIOCM_DSR;
+       if (status & UART_MSR_CTS)
+               ret |= TIOCM_CTS;
+       return ret;
+}
+
+static void multi_set_mctrl(struct sb_uart_port *port, unsigned int mctrl)
+{
+       struct mp_port *mtpt = (struct mp_port *)port;
+       unsigned char mcr = 0;
+
+       mctrl &= 0xff;
+
+       if (mctrl & TIOCM_RTS)
+               mcr |= UART_MCR_RTS;
+       if (mctrl & TIOCM_DTR)
+               mcr |= UART_MCR_DTR;
+       if (mctrl & TIOCM_OUT1)
+               mcr |= UART_MCR_OUT1;
+       if (mctrl & TIOCM_OUT2)
+               mcr |= UART_MCR_OUT2;
+       if (mctrl & TIOCM_LOOP)
+               mcr |= UART_MCR_LOOP;
+
+
+       serial_out(mtpt, UART_MCR, mcr);
+}
+
+
+static void multi_break_ctl(struct sb_uart_port *port, int break_state)
+{
+       struct mp_port *mtpt = (struct mp_port *)port;
+       unsigned long flags;
+
+       spin_lock_irqsave(&mtpt->port.lock, flags);
+       if (break_state == -1)
+               mtpt->lcr |= UART_LCR_SBC;
+       else
+               mtpt->lcr &= ~UART_LCR_SBC;
+       serial_out(mtpt, UART_LCR, mtpt->lcr);
+       spin_unlock_irqrestore(&mtpt->port.lock, flags);
+}
+
+
+
+static int multi_startup(struct sb_uart_port *port)
+{
+       struct mp_port *mtpt = (struct mp_port *)port;
+       unsigned long flags;
+       int retval;
+
+       mtpt->capabilities = uart_config[mtpt->port.type].flags;
+       mtpt->mcr = 0;
+
+       if (mtpt->capabilities & UART_CLEAR_FIFO) {
+               serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO);
+               serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO |
+                               UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+               serial_outp(mtpt, UART_FCR, 0);
+       }
+
+       (void) serial_inp(mtpt, UART_LSR);
+       (void) serial_inp(mtpt, UART_RX);
+       (void) serial_inp(mtpt, UART_IIR);
+       (void) serial_inp(mtpt, UART_MSR);
+       //test-wlee 9-bit disable
+       serial_outp(mtpt, UART_MSR, 0);
+
+
+       if (!(mtpt->port.flags & UPF_BUGGY_UART) &&
+                       (serial_inp(mtpt, UART_LSR) == 0xff)) {
+               printk("ttyS%d: LSR safety check engaged!\n", mtpt->port.line);
+               //return -ENODEV;
+       }
+
+       if ((!is_real_interrupt(mtpt->port.irq)) || (mtpt->poll_type==TYPE_POLL)) {
+               unsigned int timeout = mtpt->port.timeout;
+
+               timeout = timeout > 6 ? (timeout / 2 - 2) : 1;
+
+               mtpt->timer.data = (unsigned long)mtpt;
+               mod_timer(&mtpt->timer, jiffies + timeout);
+       } 
+       else 
+       {
+               retval = serial_link_irq_chain(mtpt);
+               if (retval)
+                       return retval;
+       }
+
+       serial_outp(mtpt, UART_LCR, UART_LCR_WLEN8);
+
+       spin_lock_irqsave(&mtpt->port.lock, flags);
+       if ((is_real_interrupt(mtpt->port.irq))||(mtpt->poll_type==TYPE_INTERRUPT))
+               mtpt->port.mctrl |= TIOCM_OUT2;
+
+       multi_set_mctrl(&mtpt->port, mtpt->port.mctrl);
+       spin_unlock_irqrestore(&mtpt->port.lock, flags);
+
+       
+       mtpt->ier = UART_IER_RLSI | UART_IER_RDI;
+       serial_outp(mtpt, UART_IER, mtpt->ier);
+
+       (void) serial_inp(mtpt, UART_LSR);
+       (void) serial_inp(mtpt, UART_RX);
+       (void) serial_inp(mtpt, UART_IIR);
+       (void) serial_inp(mtpt, UART_MSR);
+
+       return 0;
+}
+
+
+
+static void multi_shutdown(struct sb_uart_port *port)
+{
+       struct mp_port *mtpt = (struct mp_port *)port;
+       unsigned long flags;
+
+
+       mtpt->ier = 0;
+       serial_outp(mtpt, UART_IER, 0);
+
+       spin_lock_irqsave(&mtpt->port.lock, flags);
+       mtpt->port.mctrl &= ~TIOCM_OUT2;
+
+       multi_set_mctrl(&mtpt->port, mtpt->port.mctrl);
+       spin_unlock_irqrestore(&mtpt->port.lock, flags);
+
+       serial_out(mtpt, UART_LCR, serial_inp(mtpt, UART_LCR) & ~UART_LCR_SBC);
+       serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO |
+                       UART_FCR_CLEAR_RCVR |
+                       UART_FCR_CLEAR_XMIT);
+       serial_outp(mtpt, UART_FCR, 0);
+
+
+       (void) serial_in(mtpt, UART_RX);
+
+       if ((!is_real_interrupt(mtpt->port.irq))||(mtpt->poll_type==TYPE_POLL))
+       {
+               del_timer_sync(&mtpt->timer);
+       }
+       else
+       {
+               serial_unlink_irq_chain(mtpt);
+       }
+}
+
+
+
+static unsigned int multi_get_divisor(struct sb_uart_port *port, unsigned int baud)
+{
+       unsigned int quot;
+
+       if ((port->flags & UPF_MAGIC_MULTIPLIER) &&
+                       baud == (port->uartclk/4))
+               quot = 0x8001;
+       else if ((port->flags & UPF_MAGIC_MULTIPLIER) &&
+                       baud == (port->uartclk/8))
+               quot = 0x8002;
+       else
+               quot = sb_uart_get_divisor(port, baud);
+
+       return quot;
+}
+
+
+
+
+static void multi_set_termios(struct sb_uart_port *port, struct MP_TERMIOS *termios, struct MP_TERMIOS *old)
+{
+       struct mp_port *mtpt = (struct mp_port *)port;
+       unsigned char cval, fcr = 0;
+       unsigned long flags;
+       unsigned int baud, quot;
+
+       switch (termios->c_cflag & CSIZE) {
+               case CS5:
+                       cval = 0x00;
+                       break;
+               case CS6:
+                       cval = 0x01;
+                       break;
+               case CS7:
+                       cval = 0x02;
+                       break;
+               default:
+               case CS8:
+                       cval = 0x03;
+                       break;
+       }
+
+       if (termios->c_cflag & CSTOPB)
+               cval |= 0x04;
+       if (termios->c_cflag & PARENB)
+               cval |= UART_LCR_PARITY;
+       if (!(termios->c_cflag & PARODD))
+               cval |= UART_LCR_EPAR;
+
+#ifdef CMSPAR
+       if (termios->c_cflag & CMSPAR)
+               cval |= UART_LCR_SPAR;
+#endif
+
+       baud = sb_uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
+       quot = multi_get_divisor(port, baud);
+
+       if (mtpt->capabilities & UART_USE_FIFO) {
+               //if (baud < 2400)
+               //      fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
+               //else
+               //      fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
+
+               //      fcr = UART_FCR_ENABLE_FIFO | 0x90;
+                       fcr = fcr_arr[mtpt->port.line];
+       }
+
+       spin_lock_irqsave(&mtpt->port.lock, flags);
+
+       sb_uart_update_timeout(port, termios->c_cflag, baud);
+
+       mtpt->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
+       if (termios->c_iflag & INPCK)
+               mtpt->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+       if (termios->c_iflag & (BRKINT | PARMRK))
+               mtpt->port.read_status_mask |= UART_LSR_BI;
+
+       mtpt->port.ignore_status_mask = 0;
+       if (termios->c_iflag & IGNPAR)
+               mtpt->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+       if (termios->c_iflag & IGNBRK) {
+               mtpt->port.ignore_status_mask |= UART_LSR_BI;
+               if (termios->c_iflag & IGNPAR)
+                       mtpt->port.ignore_status_mask |= UART_LSR_OE;
+       }
+
+       if ((termios->c_cflag & CREAD) == 0)
+               mtpt->port.ignore_status_mask |= UART_LSR_DR;
+
+       mtpt->ier &= ~UART_IER_MSI;
+       if (UART_ENABLE_MS(&mtpt->port, termios->c_cflag))
+               mtpt->ier |= UART_IER_MSI;
+
+       serial_out(mtpt, UART_IER, mtpt->ier);
+
+       if (mtpt->capabilities & UART_STARTECH) {
+               serial_outp(mtpt, UART_LCR, 0xBF);
+               serial_outp(mtpt, UART_EFR,
+                               termios->c_cflag & CRTSCTS ? UART_EFR_CTS :0);
+       }
+
+       serial_outp(mtpt, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */
+
+       serial_outp(mtpt, UART_DLL, quot & 0xff);     /* LS of divisor */
+       serial_outp(mtpt, UART_DLM, quot >> 8);       /* MS of divisor */
+
+       serial_outp(mtpt, UART_LCR, cval);        /* reset DLAB */
+       mtpt->lcr = cval;                 /* Save LCR */
+
+       if (fcr & UART_FCR_ENABLE_FIFO) {
+               /* emulated UARTs (Lucent Venus 167x) need two steps */
+               serial_outp(mtpt, UART_FCR, UART_FCR_ENABLE_FIFO);
+       }
+
+       serial_outp(mtpt, UART_FCR, fcr);     /* set fcr */
+
+
+       if ((mtpt->port.type == PORT_16C105X)
+               || (mtpt->port.type == PORT_16C105XA))
+       {
+               if(deep[mtpt->port.line]!=0)
+                       set_deep_fifo(port, ENABLE);
+
+               if (mtpt->interface != RS232)
+                       set_auto_rts(port,mtpt->interface);
+
+       }
+       else
+       {
+               if (mtpt->interface >= RS485NE)
+               {
+                       uart_clear_mctrl(&mtpt->port, TIOCM_RTS);
+               }
+       }
+
+       if(mtpt->device->device_id == PCI_DEVICE_ID_MP4M)
+       {
+               SendATCommand(mtpt);
+               printk("SendATCommand\n");
+       }       
+       multi_set_mctrl(&mtpt->port, mtpt->port.mctrl);
+       spin_unlock_irqrestore(&mtpt->port.lock, flags);
+}
+
+static void multi_pm(struct sb_uart_port *port, unsigned int state, unsigned int oldstate)
+{
+       struct mp_port *mtpt = (struct mp_port *)port;
+       if (state) {
+               if (mtpt->capabilities & UART_STARTECH) {
+                       serial_outp(mtpt, UART_LCR, 0xBF);
+                       serial_outp(mtpt, UART_EFR, UART_EFR_ECB);
+                       serial_outp(mtpt, UART_LCR, 0);
+                       serial_outp(mtpt, UART_IER, UART_IERX_SLEEP);
+                       serial_outp(mtpt, UART_LCR, 0xBF);
+                       serial_outp(mtpt, UART_EFR, 0);
+                       serial_outp(mtpt, UART_LCR, 0);
+               }
+
+               if (mtpt->pm)
+                       mtpt->pm(port, state, oldstate);
+       } 
+       else 
+       {
+               if (mtpt->capabilities & UART_STARTECH) {
+                       serial_outp(mtpt, UART_LCR, 0xBF);
+                       serial_outp(mtpt, UART_EFR, UART_EFR_ECB);
+                       serial_outp(mtpt, UART_LCR, 0);
+                       serial_outp(mtpt, UART_IER, 0);
+                       serial_outp(mtpt, UART_LCR, 0xBF);
+                       serial_outp(mtpt, UART_EFR, 0);
+                       serial_outp(mtpt, UART_LCR, 0);
+               }
+
+               if (mtpt->pm)
+                       mtpt->pm(port, state, oldstate);
+       }
+}
+
+static void multi_release_std_resource(struct mp_port *mtpt)
+{
+       unsigned int size = 8 << mtpt->port.regshift;
+
+       switch (mtpt->port.iotype) {
+               case UPIO_MEM:
+                       if (!mtpt->port.mapbase)
+                               break;
+
+                       if (mtpt->port.flags & UPF_IOREMAP) {
+                               iounmap(mtpt->port.membase);
+                               mtpt->port.membase = NULL;
+                       }
+
+                       release_mem_region(mtpt->port.mapbase, size);
+                       break;
+
+               case UPIO_HUB6:
+               case UPIO_PORT:
+                       release_region(mtpt->port.iobase,size);
+                       break;
+       }
+}
+
+static void multi_release_port(struct sb_uart_port *port)
+{
+}
+
+static int multi_request_port(struct sb_uart_port *port)
+{
+       return 0;
+}
+
+static void multi_config_port(struct sb_uart_port *port, int flags)
+{
+       struct mp_port *mtpt = (struct mp_port *)port;
+       int probeflags = PROBE_ANY;
+
+       if (flags & UART_CONFIG_TYPE)
+               autoconfig(mtpt, probeflags);
+       if (mtpt->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ)
+               autoconfig_irq(mtpt);
+
+       if (mtpt->port.type == PORT_UNKNOWN)
+               multi_release_std_resource(mtpt);
+}
+
+static int multi_verify_port(struct sb_uart_port *port, struct serial_struct *ser)
+{
+       if (ser->irq >= NR_IRQS || ser->irq < 0 ||
+                       ser->baud_base < 9600 || ser->type < PORT_UNKNOWN ||
+                       ser->type == PORT_STARTECH)
+               return -EINVAL;
+       return 0;
+}
+
+static const char * multi_type(struct sb_uart_port *port)
+{
+       int type = port->type;
+
+       if (type >= ARRAY_SIZE(uart_config))
+               type = 0;
+       return uart_config[type].name;
+}
+
+static struct sb_uart_ops multi_pops = {
+       .tx_empty   = multi_tx_empty,
+       .set_mctrl  = multi_set_mctrl,
+       .get_mctrl  = multi_get_mctrl,
+       .stop_tx    = multi_stop_tx,
+       .start_tx   = multi_start_tx,
+       .stop_rx    = multi_stop_rx,
+       .enable_ms  = multi_enable_ms,
+       .break_ctl  = multi_break_ctl,
+       .startup    = multi_startup,
+       .shutdown   = multi_shutdown,
+       .set_termios    = multi_set_termios,
+       .pm             = multi_pm,
+       .type           = multi_type,
+       .release_port   = multi_release_port,
+       .request_port   = multi_request_port,
+       .config_port    = multi_config_port,
+       .verify_port    = multi_verify_port,
+};
+
+static struct uart_driver multi_reg = {
+       .owner          = THIS_MODULE,
+       .driver_name    = "goldel_tulip",
+       .dev_name       = "ttyMP",
+       .major          = SB_TTY_MP_MAJOR,
+       .minor          = 0,
+       .nr             = MAX_MP_PORT, 
+       .cons           = NULL,
+};
+
+static void __init multi_init_ports(void)
+{
+       struct mp_port *mtpt;
+       static int first = 1;
+       int i,j,k;
+       unsigned char osc;
+       unsigned char b_ret = 0;
+       static struct mp_device_t * sbdev; 
+
+       if (!first)
+               return;
+       first = 0;
+
+       mtpt = multi_ports; 
+
+       for (k=0;k<NR_BOARD;k++)
+       {
+               sbdev = &mp_devs[k];
+
+               for (i = 0; i < sbdev->nr_ports; i++, mtpt++) 
+               {
+                       mtpt->device            = sbdev;
+                       mtpt->port.iobase   = sbdev->uart_access_addr + 8*i;
+                       mtpt->port.irq      = sbdev->irq;
+                       if ( ((sbdev->device_id == PCI_DEVICE_ID_MP4)&&(sbdev->revision==0x91)))
+                               mtpt->interface_config_addr = sbdev->option_reg_addr + 0x08 + i;
+                       else if (sbdev->revision == 0xc0)
+                               mtpt->interface_config_addr = sbdev->option_reg_addr + 0x08 + (i & 0x1);
+                       else
+                               mtpt->interface_config_addr = sbdev->option_reg_addr + 0x08 + i/8;
+
+                       mtpt->option_base_addr = sbdev->option_reg_addr;
+
+                       mtpt->poll_type = sbdev->poll_type;
+
+                       mtpt->port.uartclk  = BASE_BAUD * 16;
+
+                       /* get input clock infomation */
+                       osc = inb(sbdev->option_reg_addr + MP_OPTR_DIR0 + i/8) & 0x0F;
+                       if (osc==0x0f)
+                               osc = 0;
+                       for(j=0;j<osc;j++)
+                               mtpt->port.uartclk *= 2;
+                       mtpt->port.flags    |= STD_COM_FLAGS | UPF_SHARE_IRQ ;
+                       mtpt->port.iotype   = UPIO_PORT;
+                       mtpt->port.ops      = &multi_pops;
+
+                       if (sbdev->revision == 0xc0)
+                       {
+                               /* for SB16C1053APCI */
+                               b_ret = sb1053a_get_interface(mtpt, i);
+                       }
+                       else
+                       {
+                               b_ret = read_option_register(mtpt,(MP_OPTR_IIR0 + i/8));
+                               printk("IIR_RET = %x\n",b_ret);
+                       }
+
+                       if(IIR_RS232 == (b_ret & IIR_RS232))
+                       {
+                               mtpt->interface = RS232;
+                       }
+                       if(IIR_RS422 == (b_ret & IIR_RS422))
+                       {
+                               mtpt->interface = RS422PTP;
+                       }
+                       if(IIR_RS485 == (b_ret & IIR_RS485))
+                       {
+                               mtpt->interface = RS485NE;
+                       }
+               }
+       }
+}
+
+static void __init multi_register_ports(struct uart_driver *drv)
+{
+       int i;
+
+       multi_init_ports();
+
+       for (i = 0; i < NR_PORTS; i++) {
+               struct mp_port *mtpt = &multi_ports[i];
+
+               mtpt->port.line = i;
+               mtpt->port.ops = &multi_pops;
+               init_timer(&mtpt->timer);
+               mtpt->timer.function = multi_timeout;
+               mp_add_one_port(drv, &mtpt->port);
+       }
+}
+
+/**
+ * pci_remap_base - remap BAR value of pci device
+ *
+ * PARAMETERS
+ *  pcidev  - pci_dev structure address
+ *  offset  - BAR offset PCI_BASE_ADDRESS_0 ~ PCI_BASE_ADDRESS_4
+ *  address - address to be changed BAR value
+ *  size       - size of address space 
+ *
+ * RETURNS
+ *  If this function performs successful, it returns 0. Otherwise, It returns -1.
+ */
+static int pci_remap_base(struct pci_dev *pcidev, unsigned int offset, 
+               unsigned int address, unsigned int size) 
+{
+#if 0
+       struct resource *root;
+       unsigned index = (offset - 0x10) >> 2;
+#endif
+
+       pci_write_config_dword(pcidev, offset, address);
+#if 0
+       root = pcidev->resource[index].parent;
+       release_resource(&pcidev->resource[index]);
+       address &= ~0x1;
+       pcidev->resource[index].start = address;
+       pcidev->resource[index].end       = address + size - 1;
+
+       if (request_resource(root, &pcidev->resource[index]) != NULL)
+       {
+               printk(KERN_ERR "pci remap conflict!! 0x%x\n", address);
+               return (-1);
+       }
+#endif
+
+       return (0);
+}
+
+static int init_mp_dev(struct pci_dev *pcidev, mppcibrd_t brd)
+{
+       static struct mp_device_t * sbdev = mp_devs;
+       unsigned long addr = 0;
+       int j;
+       struct resource * ret = NULL;
+
+       sbdev->device_id = brd.device_id;
+       pci_read_config_byte(pcidev, PCI_CLASS_REVISION, &(sbdev->revision));
+       sbdev->name = brd.name;
+       sbdev->uart_access_addr = pcidev->resource[0].start & PCI_BASE_ADDRESS_IO_MASK;
+
+       /* check revision. The SB16C1053APCI's option i/o address is BAR4 */
+       if (sbdev->revision == 0xc0)
+       {
+               /* SB16C1053APCI */
+               sbdev->option_reg_addr = pcidev->resource[4].start & PCI_BASE_ADDRESS_IO_MASK;
+       }
+       else
+       {
+               sbdev->option_reg_addr = pcidev->resource[1].start & PCI_BASE_ADDRESS_IO_MASK;
+       }
+#if 1  
+       if (sbdev->revision == 0xc0)
+       {
+               outb(0x00, sbdev->option_reg_addr + MP_OPTR_GPOCR);
+               inb(sbdev->option_reg_addr + MP_OPTR_GPOCR);
+               outb(0x83, sbdev->option_reg_addr + MP_OPTR_GPOCR);
+       }
+#endif
+
+       sbdev->irq = pcidev->irq;
+
+       if ((brd.device_id & 0x0800) || !(brd.device_id &0xff00))
+       {
+               sbdev->poll_type = TYPE_INTERRUPT;
+       }
+       else
+       {
+               sbdev->poll_type = TYPE_POLL;
+       }
+
+       /* codes which is specific to each board*/
+       switch(brd.device_id){
+               case PCI_DEVICE_ID_MP1 :
+               case PCIE_DEVICE_ID_MP1 :
+               case PCIE_DEVICE_ID_MP1E :
+               case PCIE_DEVICE_ID_GT_MP1 :
+                       sbdev->nr_ports = 1;
+                       break;
+               case PCI_DEVICE_ID_MP2 :
+               case PCIE_DEVICE_ID_MP2 :
+               case PCIE_DEVICE_ID_GT_MP2 :
+               case PCIE_DEVICE_ID_MP2B :
+               case PCIE_DEVICE_ID_MP2E :
+                       sbdev->nr_ports = 2;
+
+                       /* serial base address remap */
+                       if (sbdev->revision == 0xc0)
+                       {
+                               int prev_port_addr = 0;
+
+                               pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_0, &prev_port_addr);
+                               pci_remap_base(pcidev, PCI_BASE_ADDRESS_1, prev_port_addr + 8, 8);
+                       }
+                       break;
+               case PCI_DEVICE_ID_MP4 :
+               case PCI_DEVICE_ID_MP4A :
+               case PCIE_DEVICE_ID_MP4 :
+               case PCI_DEVICE_ID_GT_MP4 :
+               case PCI_DEVICE_ID_GT_MP4A :
+               case PCIE_DEVICE_ID_GT_MP4 :
+               case PCI_DEVICE_ID_MP4M :
+               case PCIE_DEVICE_ID_MP4B :
+                       sbdev->nr_ports = 4;
+
+                       if(sbdev->revision == 0x91){
+                               sbdev->reserved_addr[0] = pcidev->resource[0].start & PCI_BASE_ADDRESS_IO_MASK;
+                               outb(0x03 , sbdev->reserved_addr[0] + 0x01);
+                               outb(0x03 , sbdev->reserved_addr[0] + 0x02);
+                               outb(0x01 , sbdev->reserved_addr[0] + 0x20);
+                               outb(0x00 , sbdev->reserved_addr[0] + 0x21);
+                               request_region(sbdev->reserved_addr[0], 32, sbdev->name);
+                               sbdev->uart_access_addr = pcidev->resource[1].start & PCI_BASE_ADDRESS_IO_MASK;
+                               sbdev->option_reg_addr = pcidev->resource[2].start & PCI_BASE_ADDRESS_IO_MASK;
+                       }
+
+                       /* SB16C1053APCI */
+                       if (sbdev->revision == 0xc0)
+                       {
+                               int prev_port_addr = 0;
+
+                               pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_0, &prev_port_addr);
+                               pci_remap_base(pcidev, PCI_BASE_ADDRESS_1, prev_port_addr + 8, 8);
+                               pci_remap_base(pcidev, PCI_BASE_ADDRESS_2, prev_port_addr + 16, 8);
+                               pci_remap_base(pcidev, PCI_BASE_ADDRESS_3, prev_port_addr + 24, 8);
+                       }
+                       break;
+               case PCI_DEVICE_ID_MP6 :
+               case PCI_DEVICE_ID_MP6A :
+               case PCI_DEVICE_ID_GT_MP6 :
+               case PCI_DEVICE_ID_GT_MP6A :
+                       sbdev->nr_ports = 6;
+
+                       /* SB16C1053APCI */
+                       if (sbdev->revision == 0xc0)
+                       {
+                               int prev_port_addr = 0;
+
+                               pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_0, &prev_port_addr);
+                               pci_remap_base(pcidev, PCI_BASE_ADDRESS_1, prev_port_addr + 8, 8);
+                               pci_remap_base(pcidev, PCI_BASE_ADDRESS_2, prev_port_addr + 16, 16);
+                               pci_remap_base(pcidev, PCI_BASE_ADDRESS_3, prev_port_addr + 32, 16);
+                       }
+                       break;
+               case PCI_DEVICE_ID_MP8 :
+               case PCIE_DEVICE_ID_MP8 :
+               case PCI_DEVICE_ID_GT_MP8 :
+               case PCIE_DEVICE_ID_GT_MP8 :
+               case PCIE_DEVICE_ID_MP8B :
+                       sbdev->nr_ports = 8;
+                       break;
+               case PCI_DEVICE_ID_MP32 :
+               case PCIE_DEVICE_ID_MP32 :
+               case PCI_DEVICE_ID_GT_MP32 :
+               case PCIE_DEVICE_ID_GT_MP32 :
+                       {
+                               int portnum_hex=0;
+                               portnum_hex = inb(sbdev->option_reg_addr);
+                               sbdev->nr_ports = ((portnum_hex/16)*10) + (portnum_hex % 16);
+                       }
+                       break;
+               case PCI_DEVICE_ID_MP2S1P :
+                       sbdev->nr_ports = 2;
+
+                       /* SB16C1053APCI */
+                       if (sbdev->revision == 0xc0)
+                       {
+                               int prev_port_addr = 0;
+
+                               pci_read_config_dword(pcidev, PCI_BASE_ADDRESS_0, &prev_port_addr);
+                               pci_remap_base(pcidev, PCI_BASE_ADDRESS_1, prev_port_addr + 8, 8);
+                       }
+
+                       /* add PC compatible parallel port */
+                       parport_pc_probe_port(pcidev->resource[2].start, pcidev->resource[3].start, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &pcidev->dev, 0);
+                       break;
+               case PCI_DEVICE_ID_MP1P :
+                       /* add PC compatible parallel port */
+                       parport_pc_probe_port(pcidev->resource[2].start, pcidev->resource[3].start, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &pcidev->dev, 0);
+                       break;
+       }
+
+       ret = request_region(sbdev->uart_access_addr, (8*sbdev->nr_ports), sbdev->name);
+
+       if (sbdev->revision == 0xc0)
+       {
+               ret = request_region(sbdev->option_reg_addr, 0x40, sbdev->name);
+       }
+       else
+       {
+               ret = request_region(sbdev->option_reg_addr, 0x20, sbdev->name);
+       }
+
+
+       NR_BOARD++;
+       NR_PORTS += sbdev->nr_ports;
+
+       /* Enable PCI interrupt */
+       addr = sbdev->option_reg_addr + MP_OPTR_IMR0;
+       for(j=0; j < (sbdev->nr_ports/8)+1; j++)
+       {
+               if (sbdev->poll_type == TYPE_INTERRUPT)
+               {
+                       outb(0xff,addr +j);
+               }
+       }
+       sbdev++;
+
+       return 0;
+}
+
+static int __init multi_init(void)
+{
+       int ret, i;
+       struct pci_dev  *dev = NULL;
+
+       if(fcr_count==0)
+       {
+               for(i=0;i<256;i++)
+               {
+                       fcr_arr[i] = 0x01;
+                       
+               }
+       }
+       if(deep_count==0)
+       {
+               for(i=0;i<256;i++)
+               {
+                       deep[i] = 1;
+                       
+               }
+       }
+       if(rtr_count==0)
+        {
+                for(i=0;i<256;i++)
+                {
+                        rtr[i] = 0x10;
+                }
+        }
+       if(ttr_count==0)
+        {
+                for(i=0;i<256;i++)
+                {
+                        ttr[i] = 0x38;
+                }
+        }
+
+
+printk("MULTI INIT\n");
+       for( i=0; i< mp_nrpcibrds; i++)
+       {
+
+               while( (dev = pci_get_device(mp_pciboards[i].vendor_id, mp_pciboards[i].device_id, dev) ) )
+
+               {
+printk("FOUND~~~\n");
+//     Cent OS bug fix
+//                     if (mp_pciboards[i].device_id & 0x0800)
+                       {
+                               int status;
+                               pci_disable_device(dev);
+                               status = pci_enable_device(dev);
+            
+                               if (status != 0)
+                               { 
+                                               printk("Multiport Board Enable Fail !\n\n");
+                                               status = -ENXIO;
+                                       return status;
+                               }
+                       }
+
+                       init_mp_dev(dev, mp_pciboards[i]);      
+               }
+       }
+
+       for (i = 0; i < NR_IRQS; i++)
+               spin_lock_init(&irq_lists[i].lock);
+
+       ret = mp_register_driver(&multi_reg);
+
+       if (ret >= 0)
+               multi_register_ports(&multi_reg);
+
+       return ret;
+}
+
+static void __exit multi_exit(void)
+{
+       int i;
+
+       for (i = 0; i < NR_PORTS; i++)
+               mp_remove_one_port(&multi_reg, &multi_ports[i].port);
+
+       mp_unregister_driver(&multi_reg);
+}
+
+module_init(multi_init);
+module_exit(multi_exit);
+
+MODULE_DESCRIPTION("SystemBase Multiport PCI/PCIe CORE");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/sb105x/sb_pci_mp.h b/drivers/staging/sb105x/sb_pci_mp.h
new file mode 100644 (file)
index 0000000..f33efde
--- /dev/null
@@ -0,0 +1,293 @@
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/tty_driver.h>
+#include <linux/pci.h>
+#include <linux/circ_buf.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/segment.h>
+#include <asm/serial.h>
+#include <linux/interrupt.h>
+
+
+#include <linux/parport.h>
+#include <linux/ctype.h>
+#include <linux/poll.h>
+
+
+#define MP_TERMIOS  ktermios
+
+#include "sb_mp_register.h"
+#include "sb_ser_core.h"
+
+#define DRIVER_VERSION  "1.1"
+#define DRIVER_DATE     "2012/01/05"
+#define DRIVER_AUTHOR  "SYSTEMBASE<tech@sysbas.com>"
+#define DRIVER_DESC  "SystemBase PCI/PCIe Multiport Core"
+
+#define SB_TTY_MP_MAJOR                        54
+#define PCI_VENDOR_ID_MULTIPORT                0x14A1
+
+#define PCI_DEVICE_ID_MP1              0x4d01
+#define PCI_DEVICE_ID_MP2              0x4d02
+#define PCI_DEVICE_ID_MP4              0x4d04
+#define PCI_DEVICE_ID_MP4A             0x4d54
+#define PCI_DEVICE_ID_MP6              0x4d06
+#define PCI_DEVICE_ID_MP6A             0x4d56
+#define PCI_DEVICE_ID_MP8              0x4d08
+#define PCI_DEVICE_ID_MP32             0x4d32
+/* Parallel port */
+#define PCI_DEVICE_ID_MP1P             0x4301
+#define PCI_DEVICE_ID_MP2S1P           0x4303
+
+#define PCIE_DEVICE_ID_MP1             0x4501
+#define PCIE_DEVICE_ID_MP2             0x4502
+#define PCIE_DEVICE_ID_MP4             0x4504
+#define PCIE_DEVICE_ID_MP8             0x4508
+#define PCIE_DEVICE_ID_MP32            0x4532
+
+#define PCIE_DEVICE_ID_MP1E            0x4e01
+#define PCIE_DEVICE_ID_MP2E            0x4e02
+#define PCIE_DEVICE_ID_MP2B            0x4b02
+#define PCIE_DEVICE_ID_MP4B            0x4b04
+#define PCIE_DEVICE_ID_MP8B            0x4b08
+
+#define PCI_DEVICE_ID_GT_MP4           0x0004
+#define PCI_DEVICE_ID_GT_MP4A          0x0054
+#define PCI_DEVICE_ID_GT_MP6           0x0006
+#define PCI_DEVICE_ID_GT_MP6A          0x0056
+#define PCI_DEVICE_ID_GT_MP8           0x0008
+#define PCI_DEVICE_ID_GT_MP32          0x0032
+
+#define PCIE_DEVICE_ID_GT_MP1          0x1501
+#define PCIE_DEVICE_ID_GT_MP2          0x1502
+#define PCIE_DEVICE_ID_GT_MP4          0x1504
+#define PCIE_DEVICE_ID_GT_MP8          0x1508
+#define PCIE_DEVICE_ID_GT_MP32         0x1532
+
+#define PCI_DEVICE_ID_MP4M             0x4604  //modem
+
+#define MAX_MP_DEV  8
+#define BD_MAX_PORT 32         /* Max serial port in one board */
+#define MAX_MP_PORT 256 /* Max serial port in one PC */
+
+#define PORT_16C105XA  3
+#define PORT_16C105X   2
+#define PORT_16C55X            1
+
+#define ENABLE         1
+#define DISABLE                0
+
+/* ioctls */
+#define TIOCGNUMOFPORT         0x545F
+#define TIOCSMULTIECHO         0x5440
+#define TIOCSPTPNOECHO         0x5441
+
+#define TIOCGOPTIONREG         0x5461
+#define TIOCGDISABLEIRQ                0x5462
+#define TIOCGENABLEIRQ         0x5463
+#define TIOCGSOFTRESET         0x5464
+#define TIOCGSOFTRESETR                0x5465
+#define TIOCGREGINFO           0x5466
+#define TIOCGGETLSR            0x5467
+#define TIOCGGETDEVID          0x5468
+#define TIOCGGETBDNO           0x5469
+#define TIOCGGETINTERFACE      0x546A
+#define TIOCGGETREV            0x546B
+#define TIOCGGETNRPORTS                0x546C
+#define TIOCGGETPORTTYPE       0x546D
+#define GETDEEPFIFO            0x54AA
+#define SETDEEPFIFO            0x54AB
+#define SETFCR                 0x54BA
+#define SETTTR                 0x54B1
+#define SETRTR                 0x54B2
+#define GETTTR                 0x54B3
+#define GETRTR                 0x54B4
+
+/* multi-drop mode related ioctl commands */
+#define TIOCSMULTIDROP         0x5470
+#define TIOCSMDADDR            0x5471
+#define TIOCGMDADDR            0x5472
+#define TIOCSENDADDR           0x5473
+
+
+/* serial interface */
+#define RS232          1 
+#define RS422PTP       2
+#define RS422MD                3
+#define RS485NE                4
+#define RS485ECHO      5
+
+#define serial_inp(up, offset)      serial_in(up, offset)
+#define serial_outp(up, offset, value)  serial_out(up, offset, value)
+       
+#define PASS_LIMIT  256
+#define is_real_interrupt(irq)  ((irq) != 0)
+
+#define PROBE_ANY   (~0)
+
+static DEFINE_MUTEX(mp_mutex);
+#define MP_MUTEX_LOCK(x) mutex_lock(&(x)) 
+#define MP_MUTEX_UNLOCK(x) mutex_unlock(&(x)) 
+#define MP_STATE_LOCK(x) mutex_lock(&((x)->mutex)) 
+#define MP_STATE_UNLOCK(x) mutex_unlock(&((x)->mutex)) 
+        
+
+#define UART_LSR_SPECIAL    0x1E
+        
+#define HIGH_BITS_OFFSET        ((sizeof(long)-sizeof(int))*8)
+#define uart_users(state)       ((state)->count + ((state)->info ? (state)->info->blocked_open : 0))
+
+
+//#define MP_DEBUG 1
+#undef MP_DEBUG
+
+#ifdef MP_DEBUG
+#define DPRINTK(x...)   printk(x)
+#else
+#define DPRINTK(x...)   do { } while (0)
+#endif
+
+#ifdef MP_DEBUG
+#define DEBUG_AUTOCONF(fmt...)  printk(fmt)
+#else
+#define DEBUG_AUTOCONF(fmt...)  do { } while (0)
+#endif
+
+#ifdef MP_DEBUG
+#define DEBUG_INTR(fmt...)  printk(fmt)
+#else
+#define DEBUG_INTR(fmt...)  do { } while (0)
+#endif
+
+#if defined(__i386__) && (defined(CONFIG_M386) || defined(CONFIG_M486))
+#define SERIAL_INLINE
+#endif
+#ifdef SERIAL_INLINE
+#define _INLINE_ inline
+#else
+#define _INLINE_
+#endif
+
+#define TYPE_POLL      1
+#define TYPE_INTERRUPT 2
+
+
+struct mp_device_t {
+        unsigned short  device_id;
+        unsigned char   revision;
+        char            *name;
+        unsigned long   uart_access_addr;
+        unsigned long   option_reg_addr;
+        unsigned long   reserved_addr[4];
+        int             irq;
+        int             nr_ports;
+        int             poll_type;
+};
+
+typedef struct mppcibrd {
+        char            *name;
+        unsigned short  vendor_id;
+        unsigned short  device_id;
+} mppcibrd_t;
+
+static mppcibrd_t mp_pciboards[] = {
+
+        { "Multi-1 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP1} ,
+        { "Multi-2 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP2} ,
+        { "Multi-4 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP4} ,
+        { "Multi-4 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP4A} ,
+        { "Multi-6 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP6} ,
+        { "Multi-6 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP6A} ,
+        { "Multi-8 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP8} ,
+        { "Multi-32 PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP32} ,
+
+        { "Multi-1P PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP1P} ,
+        { "Multi-2S1P PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP2S1P} ,
+
+        { "Multi-4(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP4} ,
+        { "Multi-4(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP4A} ,
+        { "Multi-6(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP6} ,
+        { "Multi-6(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP6A} ,
+        { "Multi-8(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP8} ,
+        { "Multi-32(GT) PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_GT_MP32} ,
+
+        { "Multi-1 PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP1} ,
+        { "Multi-2 PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP2} ,
+        { "Multi-4 PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP4} ,
+        { "Multi-8 PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP8} ,
+        { "Multi-32 PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP32} ,
+
+        { "Multi-1 PCIe E", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP1E} ,
+        { "Multi-2 PCIe E", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP2E} ,
+        { "Multi-2 PCIe B", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP2B} ,
+        { "Multi-4 PCIe B", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP4B} ,
+        { "Multi-8 PCIe B", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_MP8B} ,
+
+        { "Multi-1(GT) PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_GT_MP1} ,
+        { "Multi-2(GT) PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_GT_MP2} ,
+        { "Multi-4(GT) PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_GT_MP4} ,
+        { "Multi-8(GT) PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_GT_MP8} ,
+        { "Multi-32(GT) PCIe", PCI_VENDOR_ID_MULTIPORT , PCIE_DEVICE_ID_GT_MP32} ,
+
+        { "Multi-4M PCI", PCI_VENDOR_ID_MULTIPORT , PCI_DEVICE_ID_MP4M} ,
+};
+
+struct mp_port {
+        struct sb_uart_port port;
+
+        struct timer_list   timer;      /* "no irq" timer */
+        struct list_head    list;       /* ports on this IRQ */
+        unsigned int        capabilities;   /* port capabilities */
+        unsigned short      rev;
+        unsigned char       acr;
+        unsigned char       ier;
+        unsigned char       lcr;
+        unsigned char       mcr;
+        unsigned char       mcr_mask;   /* mask of user bits */
+        unsigned char       mcr_force;  /* mask of forced bits */
+        unsigned char       lsr_break_flag;
+
+        void            (*pm)(struct sb_uart_port *port,
+                        unsigned int state, unsigned int old);
+        struct mp_device_t *device;
+        unsigned long   interface_config_addr;
+        unsigned long   option_base_addr;
+        unsigned char   interface;
+        unsigned char   poll_type;
+};
+
+struct irq_info {
+        spinlock_t      lock;
+        struct list_head    *head;
+};
+
+struct sb105x_uart_config {
+       char    *name;
+       int     dfl_xmit_fifo_size;
+       int     flags;
+};
+
+static const struct sb105x_uart_config uart_config[] = {
+        { "unknown",    1,  0 },
+        { "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO },
+        { "SB16C1050",    128,    UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH },
+        { "SB16C1050A",    128,    UART_CLEAR_FIFO | UART_USE_FIFO | UART_STARTECH },
+};
+
+
+
diff --git a/drivers/staging/sb105x/sb_ser_core.h b/drivers/staging/sb105x/sb_ser_core.h
new file mode 100644 (file)
index 0000000..c8fb991
--- /dev/null
@@ -0,0 +1,368 @@
+#include <linux/wait.h>
+
+#define UART_CONFIG_TYPE       (1 << 0)
+#define UART_CONFIG_IRQ                (1 << 1)
+#define UPIO_PORT              (0)
+#define UPIO_HUB6              (1)
+#define UPIO_MEM               (2)
+#define UPIO_MEM32             (3)
+#define UPIO_AU                        (4)                     /* Au1x00 type IO */
+#define UPIO_TSI               (5)                     /* Tsi108/109 type IO */
+#define UPF_FOURPORT           (1 << 1)
+#define UPF_SAK                        (1 << 2)
+#define UPF_SPD_MASK           (0x1030)
+#define UPF_SPD_HI             (0x0010)
+#define UPF_SPD_VHI            (0x0020)
+#define UPF_SPD_CUST           (0x0030)
+#define UPF_SPD_SHI            (0x1000)
+#define UPF_SPD_WARP           (0x1010)
+#define UPF_SKIP_TEST          (1 << 6)
+#define UPF_AUTO_IRQ           (1 << 7)
+#define UPF_HARDPPS_CD         (1 << 11)
+#define UPF_LOW_LATENCY                (1 << 13)
+#define UPF_BUGGY_UART         (1 << 14)
+#define UPF_MAGIC_MULTIPLIER   (1 << 16)
+#define UPF_CONS_FLOW          (1 << 23)
+#define UPF_SHARE_IRQ          (1 << 24)
+#define UPF_BOOT_AUTOCONF      (1 << 28)
+#define UPF_DEAD               (1 << 30)
+#define UPF_IOREMAP            (1 << 31)
+#define UPF_CHANGE_MASK                (0x17fff)
+#define UPF_USR_MASK           (UPF_SPD_MASK|UPF_LOW_LATENCY)
+#define USF_CLOSING_WAIT_INF   (0)
+#define USF_CLOSING_WAIT_NONE  (~0U)
+
+#define UART_XMIT_SIZE PAGE_SIZE
+
+#define UIF_CHECK_CD           (1 << 25)
+#define UIF_CTS_FLOW           (1 << 26)
+#define UIF_NORMAL_ACTIVE      (1 << 29)
+#define UIF_INITIALIZED                (1 << 31)
+#define UIF_SUSPENDED          (1 << 30)
+
+#define WAKEUP_CHARS           256
+
+#define uart_circ_empty(circ)          ((circ)->head == (circ)->tail)
+#define uart_circ_clear(circ)          ((circ)->head = (circ)->tail = 0)
+
+#define uart_circ_chars_pending(circ)  \
+       (CIRC_CNT((circ)->head, (circ)->tail, UART_XMIT_SIZE))
+
+#define uart_circ_chars_free(circ)     \
+       (CIRC_SPACE((circ)->head, (circ)->tail, UART_XMIT_SIZE))
+
+#define uart_tx_stopped(port)          \
+       ((port)->info->tty->stopped || (port)->info->tty->hw_stopped)
+
+#define UART_ENABLE_MS(port,cflag)     ((port)->flags & UPF_HARDPPS_CD || \
+                                        (cflag) & CRTSCTS || \
+                                        !((cflag) & CLOCAL))
+
+
+struct sb_uart_port;
+struct sb_uart_info;
+struct serial_struct;
+struct device;
+
+struct sb_uart_ops {
+       unsigned int    (*tx_empty)(struct sb_uart_port *);
+       void            (*set_mctrl)(struct sb_uart_port *, unsigned int mctrl);
+       unsigned int    (*get_mctrl)(struct sb_uart_port *);
+       void            (*stop_tx)(struct sb_uart_port *);
+       void            (*start_tx)(struct sb_uart_port *);
+       void            (*send_xchar)(struct sb_uart_port *, char ch);
+       void            (*stop_rx)(struct sb_uart_port *);
+       void            (*enable_ms)(struct sb_uart_port *);
+       void            (*break_ctl)(struct sb_uart_port *, int ctl);
+       int             (*startup)(struct sb_uart_port *);
+       void            (*shutdown)(struct sb_uart_port *);
+       void            (*set_termios)(struct sb_uart_port *, struct MP_TERMIOS *new,
+                                      struct MP_TERMIOS *old);
+       void            (*pm)(struct sb_uart_port *, unsigned int state,
+                             unsigned int oldstate);
+       int             (*set_wake)(struct sb_uart_port *, unsigned int state);
+
+       const char *(*type)(struct sb_uart_port *);
+
+       void            (*release_port)(struct sb_uart_port *);
+
+       int             (*request_port)(struct sb_uart_port *);
+       void            (*config_port)(struct sb_uart_port *, int);
+       int             (*verify_port)(struct sb_uart_port *, struct serial_struct *);
+       int             (*ioctl)(struct sb_uart_port *, unsigned int, unsigned long);
+};
+
+
+struct sb_uart_icount {
+       __u32   cts;
+       __u32   dsr;
+       __u32   rng;
+       __u32   dcd;
+       __u32   rx;
+       __u32   tx;
+       __u32   frame;
+       __u32   overrun;
+       __u32   parity;
+       __u32   brk;
+       __u32   buf_overrun;
+};
+typedef unsigned int  upf_t;
+
+struct sb_uart_port {
+       spinlock_t              lock;                   /* port lock */
+       unsigned int            iobase;                 /* in/out[bwl] */
+       unsigned char __iomem   *membase;               /* read/write[bwl] */
+       unsigned int            irq;                    /* irq number */
+       unsigned int            uartclk;                /* base uart clock */
+       unsigned int            fifosize;               /* tx fifo size */
+       unsigned char           x_char;                 /* xon/xoff char */
+       unsigned char           regshift;               /* reg offset shift */
+       unsigned char           iotype;                 /* io access style */
+       unsigned char           unused1;
+
+
+       unsigned int            read_status_mask;       /* driver specific */
+       unsigned int            ignore_status_mask;     /* driver specific */
+       struct sb_uart_info     *info;                  /* pointer to parent info */
+       struct sb_uart_icount   icount;                 /* statistics */
+
+       struct console          *cons;                  /* struct console, if any */
+#ifdef CONFIG_SERIAL_CORE_CONSOLE
+       unsigned long           sysrq;                  /* sysrq timeout */
+#endif
+
+       upf_t                   flags;
+
+       unsigned int            mctrl;                  /* current modem ctrl settings */
+       unsigned int            timeout;                /* character-based timeout */
+       unsigned int            type;                   /* port type */
+       const struct sb_uart_ops        *ops;
+       unsigned int            custom_divisor;
+       unsigned int            line;                   /* port index */
+       unsigned long           mapbase;                /* for ioremap */
+       struct device           *dev;                   /* parent device */
+       unsigned char           hub6;                   /* this should be in the 8250 driver */
+       unsigned char           unused[3];
+};
+
+#define mdmode                 unused[2]
+#define MDMODE_ADDR            0x1
+#define MDMODE_ENABLE  0x2
+#define MDMODE_AUTO            0x4
+#define MDMODE_ADDRSEND        0x8
+
+struct sb_uart_state {
+       unsigned int            close_delay;            /* msec */
+       unsigned int            closing_wait;           /* msec */
+
+
+       int                     count;
+       int                     pm_state;
+       struct sb_uart_info     *info;
+       struct sb_uart_port     *port;
+
+       struct mutex            mutex;
+};
+
+typedef unsigned int  uif_t;
+
+struct sb_uart_info {
+       struct tty_struct       *tty;
+       struct circ_buf         xmit;
+       uif_t                   flags;
+
+       int                     blocked_open;
+
+       struct tasklet_struct   tlet;
+
+       wait_queue_head_t       open_wait;
+       wait_queue_head_t       delta_msr_wait;
+};
+
+
+struct module;
+struct tty_driver;
+
+struct uart_driver {
+       struct module           *owner;
+       const char              *driver_name;
+       const char              *dev_name;
+       int                      major;
+       int                      minor;
+       int                      nr;
+       struct console          *cons;
+
+       struct sb_uart_state    *state;
+        struct tty_driver               *tty_driver;
+};
+
+void sb_uart_write_wakeup(struct sb_uart_port *port)
+{
+    struct sb_uart_info *info = port->info;
+    tasklet_schedule(&info->tlet);
+}
+
+void sb_uart_update_timeout(struct sb_uart_port *port, unsigned int cflag,
+                        unsigned int baud)
+{
+    unsigned int bits;
+
+    switch (cflag & CSIZE)
+    {
+        case CS5:
+            bits = 7;
+            break;
+
+        case CS6:
+            bits = 8;
+            break;
+
+        case CS7:
+            bits = 9;
+            break;
+
+        default:
+            bits = 10;
+            break;
+    }
+
+    if (cflag & CSTOPB)
+    {
+        bits++;
+    }
+
+    if (cflag & PARENB)
+    {
+        bits++;
+    }
+
+    bits = bits * port->fifosize;
+
+    port->timeout = (HZ * bits) / baud + HZ/50;
+}
+unsigned int sb_uart_get_baud_rate(struct sb_uart_port *port, struct MP_TERMIOS *termios,
+                               struct MP_TERMIOS *old, unsigned int min,
+                               unsigned int max)
+{
+        unsigned int try, baud, altbaud = 38400;
+        upf_t flags = port->flags & UPF_SPD_MASK;
+
+        if (flags == UPF_SPD_HI)
+                altbaud = 57600;
+        if (flags == UPF_SPD_VHI)
+                altbaud = 115200;
+        if (flags == UPF_SPD_SHI)
+                altbaud = 230400;
+        if (flags == UPF_SPD_WARP)
+                altbaud = 460800;
+
+        for (try = 0; try < 2; try++) {
+
+                switch (termios->c_cflag & (CBAUD | CBAUDEX))
+                {
+                       case B921600    : baud = 921600;    break;
+                       case B460800    : baud = 460800;    break;
+                       case B230400    : baud = 230400;    break;
+                       case B115200    : baud = 115200;    break;
+                       case B57600     : baud = 57600;     break;
+                       case B38400     : baud = 38400;     break;
+                       case B19200     : baud = 19200;     break;
+                       case B9600      : baud = 9600;      break;
+                       case B4800      : baud = 4800;      break;
+                       case B2400      : baud = 2400;      break;
+                       case B1800      : baud = 1800;      break;
+                       case B1200      : baud = 1200;      break;
+                       case B600       : baud = 600;       break;
+                       case B300       : baud = 300;       break;
+                        case B200       : baud = 200;       break;
+                       case B150       : baud = 150;       break;
+                       case B134       : baud = 134;       break;
+                       case B110       : baud = 110;       break;
+                       case B75        : baud = 75;        break;
+                       case B50        : baud = 50;        break;
+                       default         : baud = 9600;      break;
+                }
+
+                if (baud == 38400)
+                        baud = altbaud;
+
+                if (baud == 0)
+                        baud = 9600;
+
+                if (baud >= min && baud <= max)
+                        return baud;
+
+                termios->c_cflag &= ~CBAUD;
+                if (old) {
+                        termios->c_cflag |= old->c_cflag & CBAUD;
+                        old = NULL;
+                        continue;
+                }
+
+                termios->c_cflag |= B9600;
+        }
+
+        return 0;
+}
+unsigned int sb_uart_get_divisor(struct sb_uart_port *port, unsigned int baud)
+{
+        unsigned int quot;
+
+        if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
+                quot = port->custom_divisor;
+        else
+                quot = (port->uartclk + (8 * baud)) / (16 * baud);
+
+        return quot;
+}
+
+
+
+static inline int sb_uart_handle_break(struct sb_uart_port *port)
+{
+       struct sb_uart_info *info = port->info;
+
+       if (port->flags & UPF_SAK)
+               do_SAK(info->tty);
+       return 0;
+}
+
+static inline void sb_uart_handle_dcd_change(struct sb_uart_port *port, unsigned int status)
+{
+       struct sb_uart_info *info = port->info;
+
+       port->icount.dcd++;
+
+       if (info->flags & UIF_CHECK_CD) {
+               if (status)
+                       wake_up_interruptible(&info->open_wait);
+               else if (info->tty)
+                       tty_hangup(info->tty);
+       }
+}
+
+static inline void sb_uart_handle_cts_change(struct sb_uart_port *port, unsigned int status)
+{
+       struct sb_uart_info *info = port->info;
+       struct tty_struct *tty = info->tty;
+
+       port->icount.cts++;
+
+       if (info->flags & UIF_CTS_FLOW) {
+               if (tty->hw_stopped) {
+                       if (status) {
+                               tty->hw_stopped = 0;
+                               port->ops->start_tx(port);
+                               sb_uart_write_wakeup(port);
+                       }
+               } else {
+                       if (!status) {
+                               tty->hw_stopped = 1;
+                               port->ops->stop_tx(port);
+                       }
+               }
+       }
+}
+
+
+