Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / drivers / serial / ioc4_serial.c
index ba4e13a..c620209 100644 (file)
@@ -3,7 +3,7 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  *
- * Copyright (C) 2003-2005 Silicon Graphics, Inc.  All Rights Reserved.
+ * Copyright (C) 2003-2006 Silicon Graphics, Inc.  All Rights Reserved.
  */
 
 
@@ -20,7 +20,7 @@
 #include <linux/serial_reg.h>
 #include <linux/module.h>
 #include <linux/pci.h>
-#include <linux/ioc4_common.h>
+#include <linux/ioc4.h>
 #include <linux/serial_core.h>
 
 /*
                                 IOC4_SIO_IR_S3_TX_EXPLICIT)
 
 /* Bitmasks for IOC4_OTHER_IR, IOC4_OTHER_IEC, and IOC4_OTHER_IES  */
-#define IOC4_OTHER_IR_ATA_INT           0x00000001  /* ATAPI intr pass-thru */
-#define IOC4_OTHER_IR_ATA_MEMERR        0x00000002  /* ATAPI DMA PCI error */
-#define IOC4_OTHER_IR_S0_MEMERR         0x00000004  /* Port 0 PCI error */
-#define IOC4_OTHER_IR_S1_MEMERR         0x00000008  /* Port 1 PCI error */
-#define IOC4_OTHER_IR_S2_MEMERR         0x00000010  /* Port 2 PCI error */
-#define IOC4_OTHER_IR_S3_MEMERR         0x00000020  /* Port 3 PCI error */
+#define IOC4_OTHER_IR_ATA_INT          0x00000001  /* ATAPI intr pass-thru */
+#define IOC4_OTHER_IR_ATA_MEMERR       0x00000002  /* ATAPI DMA PCI error */
+#define IOC4_OTHER_IR_S0_MEMERR                0x00000004  /* Port 0 PCI error */
+#define IOC4_OTHER_IR_S1_MEMERR                0x00000008  /* Port 1 PCI error */
+#define IOC4_OTHER_IR_S2_MEMERR                0x00000010  /* Port 2 PCI error */
+#define IOC4_OTHER_IR_S3_MEMERR                0x00000020  /* Port 3 PCI error */
+#define IOC4_OTHER_IR_KBD_INT          0x00000040  /* Keyboard/mouse */
+#define IOC4_OTHER_IR_RESERVED         0x007fff80  /* Reserved */
+#define IOC4_OTHER_IR_RT_INT           0x00800000  /* INT_OUT section output */
+#define IOC4_OTHER_IR_GEN_INT          0xff000000  /* Generic pins */
+
+#define IOC4_OTHER_IR_SER_MEMERR (IOC4_OTHER_IR_S0_MEMERR | IOC4_OTHER_IR_S1_MEMERR | \
+                                 IOC4_OTHER_IR_S2_MEMERR | IOC4_OTHER_IR_S3_MEMERR)
 
 /* Bitmasks for IOC4_SIO_CR */
 #define IOC4_SIO_CR_CMD_PULSE_SHIFT              0  /* byte bus strobe shift */
@@ -274,70 +281,24 @@ struct ioc4_uartregs {
 #define i4u_dlm u2.dlm
 #define i4u_fcr u3.fcr
 
-/* PCI memory space register map addressed using pci_bar0 */
-struct ioc4_memregs {
-       struct ioc4_mem {
-               /* Miscellaneous IOC4  registers */
-               uint32_t pci_err_addr_l;
-               uint32_t pci_err_addr_h;
-               uint32_t sio_ir;
-               uint32_t other_ir;
-
-               /* These registers are read-only for general kernel code.  */
-               uint32_t sio_ies_ro;
-               uint32_t other_ies_ro;
-               uint32_t sio_iec_ro;
-               uint32_t other_iec_ro;
-               uint32_t sio_cr;
-               uint32_t misc_fill1;
-               uint32_t int_out;
-               uint32_t misc_fill2;
-               uint32_t gpcr_s;
-               uint32_t gpcr_c;
-               uint32_t gpdr;
-               uint32_t misc_fill3;
-               uint32_t gppr_0;
-               uint32_t gppr_1;
-               uint32_t gppr_2;
-               uint32_t gppr_3;
-               uint32_t gppr_4;
-               uint32_t gppr_5;
-               uint32_t gppr_6;
-               uint32_t gppr_7;
-       } ioc4_mem;
-
-       char misc_fill4[0x100 - 0x5C - 4];
-
-       /* ATA/ATAP registers */
-       uint32_t ata_notused[9];
-       char ata_fill1[0x140 - 0x120 - 4];
-       uint32_t ata_notused1[8];
-       char ata_fill2[0x200 - 0x15C - 4];
-
-       /* Keyboard and mouse registers */
-       uint32_t km_notused[5];;
-       char km_fill1[0x300 - 0x210 - 4];
-
-       /* Serial port registers used for DMA serial I/O */
-       struct ioc4_serial {
-               uint32_t sbbr01_l;
-               uint32_t sbbr01_h;
-               uint32_t sbbr23_l;
-               uint32_t sbbr23_h;
-
-               struct ioc4_serialregs port_0;
-               struct ioc4_serialregs port_1;
-               struct ioc4_serialregs port_2;
-               struct ioc4_serialregs port_3;
-               struct ioc4_uartregs uart_0;
-               struct ioc4_uartregs uart_1;
-               struct ioc4_uartregs uart_2;
-               struct ioc4_uartregs uart_3;
-       } ioc4_serial;
-};
+/* Serial port registers used for DMA serial I/O */
+struct ioc4_serial {
+       uint32_t sbbr01_l;
+       uint32_t sbbr01_h;
+       uint32_t sbbr23_l;
+       uint32_t sbbr23_h;
+
+       struct ioc4_serialregs port_0;
+       struct ioc4_serialregs port_1;
+       struct ioc4_serialregs port_2;
+       struct ioc4_serialregs port_3;
+       struct ioc4_uartregs uart_0;
+       struct ioc4_uartregs uart_1;
+       struct ioc4_uartregs uart_2;
+       struct ioc4_uartregs uart_3;
+} ioc4_serial;
 
 /* UART clock speed */
-#define IOC4_SER_XIN_CLK        IOC4_SER_XIN_CLK_66
 #define IOC4_SER_XIN_CLK_66     66666667
 #define IOC4_SER_XIN_CLK_33     33333333
 
@@ -347,6 +308,8 @@ struct ioc4_memregs {
 typedef void ioc4_intr_func_f(void *, uint32_t);
 typedef ioc4_intr_func_f *ioc4_intr_func_t;
 
+static unsigned int Num_of_ioc4_cards;
+
 /* defining this will get you LOTS of great debug info */
 //#define DEBUG_INTERRUPTS
 #define DPRINT_CONFIG(_x...)   ;
@@ -356,12 +319,16 @@ typedef ioc4_intr_func_f *ioc4_intr_func_t;
 #define WAKEUP_CHARS   256
 
 /* number of characters we want to transmit to the lower level at a time */
-#define IOC4_MAX_CHARS 128
+#define IOC4_MAX_CHARS 256
+#define IOC4_FIFO_CHARS        255
 
 /* Device name we're using */
-#define DEVICE_NAME    "ttyIOC"
-#define DEVICE_MAJOR 204
-#define DEVICE_MINOR 50
+#define DEVICE_NAME_RS232  "ttyIOC"
+#define DEVICE_NAME_RS422  "ttyAIOC"
+#define DEVICE_MAJOR      204
+#define DEVICE_MINOR_RS232 50
+#define DEVICE_MINOR_RS422 84
+
 
 /* register offsets */
 #define IOC4_SERIAL_OFFSET     0x300
@@ -377,10 +344,8 @@ typedef ioc4_intr_func_f *ioc4_intr_func_t;
 #define MAX_BAUD_SUPPORTED     115200
 
 /* protocol types supported */
-enum sio_proto {
-       PROTO_RS232,
-       PROTO_RS422
-};
+#define PROTO_RS232    3
+#define PROTO_RS422    7
 
 /* Notification types */
 #define N_DATA_READY   0x01
@@ -412,8 +377,8 @@ enum sio_proto {
                                        | UART_LCR_WLEN7 | UART_LCR_WLEN8)
 #define LCR_MASK_STOP_BITS     (UART_LCR_STOP)
 
-#define PENDING(_p)    (readl(&(_p)->ip_mem->sio_ir) & _p->ip_ienb)
-#define READ_SIO_IR(_p) readl(&(_p)->ip_mem->sio_ir)
+#define PENDING(_p)    (readl(&(_p)->ip_mem->sio_ir.raw) & _p->ip_ienb)
+#define READ_SIO_IR(_p) readl(&(_p)->ip_mem->sio_ir.raw)
 
 /* Default to 4k buffers */
 #ifdef IOC4_1K_BUFFERS
@@ -431,11 +396,17 @@ enum sio_proto {
 /*
  * This is the entry saved by the driver - one per card
  */
+
+#define UART_PORT_MIN          0
+#define UART_PORT_RS232                UART_PORT_MIN
+#define UART_PORT_RS422                1
+#define UART_PORT_COUNT                2       /* one for each mode */
+
 struct ioc4_control {
        int ic_irq;
        struct {
-               /* uart ports are allocated here */
-               struct uart_port icp_uart_port;
+               /* uart ports are allocated here - 1 for rs232, 1 for rs422 */
+               struct uart_port icp_uart_port[UART_PORT_COUNT];
                /* Handy reference material */
                struct ioc4_port *icp_port;
        } ic_port[IOC4_NUM_SERIAL_PORTS];
@@ -447,7 +418,7 @@ struct ioc4_control {
  */
 #define MAX_IOC4_INTR_ENTS     (8 * sizeof(uint32_t))
 struct ioc4_soft {
-       struct ioc4_mem __iomem *is_ioc4_mem_addr;
+       struct ioc4_misc_regs __iomem *is_ioc4_misc_addr;
        struct ioc4_serial __iomem *is_ioc4_serial_addr;
 
        /* Each interrupt type has an entry in the array */
@@ -479,14 +450,16 @@ struct ioc4_soft {
 
 /* Local port info for each IOC4 serial ports */
 struct ioc4_port {
-       struct uart_port *ip_port;
+       struct uart_port *ip_port;      /* current active port ptr */
+       /* Ptrs for all ports */
+       struct uart_port *ip_all_ports[UART_PORT_COUNT];
        /* Back ptrs for this port */
        struct ioc4_control *ip_control;
        struct pci_dev *ip_pdev;
        struct ioc4_soft *ip_ioc4_soft;
 
        /* pci mem addresses */
-       struct ioc4_mem __iomem *ip_mem;
+       struct ioc4_misc_regs __iomem *ip_mem;
        struct ioc4_serial __iomem *ip_serial;
        struct ioc4_serialregs __iomem *ip_serial_regs;
        struct ioc4_uartregs __iomem *ip_uart_regs;
@@ -538,6 +511,9 @@ struct ioc4_port {
 #define DCD_ON         0x02
 #define LOWAT_WRITTEN  0x04
 #define READ_ABORTED   0x08
+#define PORT_ACTIVE    0x10
+#define PORT_INACTIVE  0       /* This is the value when "off" */
+
 
 /* Since each port has different register offsets and bitmasks
  * for everything, we'll store those that we need in tables so we
@@ -553,7 +529,7 @@ struct hooks {
        uint32_t intr_dma_error;
        uint32_t intr_clear;
        uint32_t intr_all;
-       char rs422_select_pin;
+       int rs422_select_pin;
 };
 
 static struct hooks hooks_array[IOC4_NUM_SERIAL_PORTS] = {
@@ -659,6 +635,23 @@ struct ring_buffer {
 static void receive_chars(struct uart_port *);
 static void handle_intr(void *arg, uint32_t sio_ir);
 
+/*
+ * port_is_active - determines if this port is currently active
+ * @port: ptr to soft struct for this port
+ * @uart_port: uart port to test for
+ */
+static inline int port_is_active(struct ioc4_port *port,
+               struct uart_port *uart_port)
+{
+       if (port) {
+               if ((port->ip_flags & PORT_ACTIVE)
+                                       && (port->ip_port == uart_port))
+                       return 1;
+       }
+       return 0;
+}
+
+
 /**
  * write_ireg - write the interrupt regs
  * @ioc4_soft: ptr to soft struct for this port
@@ -669,7 +662,7 @@ static void handle_intr(void *arg, uint32_t sio_ir);
 static inline void
 write_ireg(struct ioc4_soft *ioc4_soft, uint32_t val, int which, int type)
 {
-       struct ioc4_mem __iomem *mem = ioc4_soft->is_ioc4_mem_addr;
+       struct ioc4_misc_regs __iomem *mem = ioc4_soft->is_ioc4_misc_addr;
        unsigned long flags;
 
        spin_lock_irqsave(&ioc4_soft->is_ir_lock, flags);
@@ -678,11 +671,11 @@ write_ireg(struct ioc4_soft *ioc4_soft, uint32_t val, int which, int type)
        case IOC4_SIO_INTR_TYPE:
                switch (which) {
                case IOC4_W_IES:
-                       writel(val, &mem->sio_ies_ro);
+                       writel(val, &mem->sio_ies.raw);
                        break;
 
                case IOC4_W_IEC:
-                       writel(val, &mem->sio_iec_ro);
+                       writel(val, &mem->sio_iec.raw);
                        break;
                }
                break;
@@ -690,11 +683,11 @@ write_ireg(struct ioc4_soft *ioc4_soft, uint32_t val, int which, int type)
        case IOC4_OTHER_INTR_TYPE:
                switch (which) {
                case IOC4_W_IES:
-                       writel(val, &mem->other_ies_ro);
+                       writel(val, &mem->other_ies.raw);
                        break;
 
                case IOC4_W_IEC:
-                       writel(val, &mem->other_iec_ro);
+                       writel(val, &mem->other_iec.raw);
                        break;
                }
                break;
@@ -744,18 +737,33 @@ static int set_baud(struct ioc4_port *port, int baud)
 /**
  * get_ioc4_port - given a uart port, return the control structure
  * @port: uart port
+ * @set: set this port as current
  */
-static struct ioc4_port *get_ioc4_port(struct uart_port *the_port)
+static struct ioc4_port *get_ioc4_port(struct uart_port *the_port, int set)
 {
-       struct ioc4_control *control = dev_get_drvdata(the_port->dev);
-       int ii;
+       struct ioc4_driver_data *idd = dev_get_drvdata(the_port->dev);
+       struct ioc4_control *control = idd->idd_serial_data;
+       struct ioc4_port *port;
+       int port_num, port_type;
 
        if (control) {
-               for ( ii = 0; ii < IOC4_NUM_SERIAL_PORTS; ii++ ) {
-                       if (!control->ic_port[ii].icp_port)
+               for ( port_num = 0; port_num < IOC4_NUM_SERIAL_PORTS;
+                                                       port_num++ ) {
+                       port = control->ic_port[port_num].icp_port;
+                       if (!port)
                                continue;
-                       if (the_port == control->ic_port[ii].icp_port->ip_port)
-                               return control->ic_port[ii].icp_port;
+                       for (port_type = UART_PORT_MIN;
+                                               port_type < UART_PORT_COUNT;
+                                               port_type++) {
+                               if (the_port == port->ip_all_ports
+                                                       [port_type]) {
+                                       /* set local copy */
+                                       if (set) {
+                                               port->ip_port = the_port;
+                                       }
+                                       return port;
+                               }
+                       }
                }
        }
        return NULL;
@@ -782,7 +790,7 @@ static struct ioc4_port *get_ioc4_port(struct uart_port *the_port)
 static inline uint32_t
 pending_intrs(struct ioc4_soft *soft, int type)
 {
-       struct ioc4_mem __iomem *mem = soft->is_ioc4_mem_addr;
+       struct ioc4_misc_regs __iomem *mem = soft->is_ioc4_misc_addr;
        unsigned long flag;
        uint32_t intrs = 0;
 
@@ -793,11 +801,11 @@ pending_intrs(struct ioc4_soft *soft, int type)
 
        switch (type) {
        case IOC4_SIO_INTR_TYPE:
-               intrs = readl(&mem->sio_ir) & readl(&mem->sio_ies_ro);
+               intrs = readl(&mem->sio_ir.raw) & readl(&mem->sio_ies.raw);
                break;
 
        case IOC4_OTHER_INTR_TYPE:
-               intrs = readl(&mem->other_ir) & readl(&mem->other_ies_ro);
+               intrs = readl(&mem->other_ir.raw) & readl(&mem->other_ies.raw);
 
                /* Don't process any ATA interrupte */
                intrs &= ~(IOC4_OTHER_IR_ATA_INT | IOC4_OTHER_IR_ATA_MEMERR);
@@ -826,7 +834,7 @@ static int inline port_init(struct ioc4_port *port)
 
        /* Wait until any pending bus activity for this port has ceased */
        do
-               sio_cr = readl(&port->ip_mem->sio_cr);
+               sio_cr = readl(&port->ip_mem->sio_cr.raw);
        while (!(sio_cr & IOC4_SIO_CR_SIO_DIAG_IDLE));
 
        /* Finish reset sequence */
@@ -899,7 +907,7 @@ static int inline port_init(struct ioc4_port *port)
        write_ireg(port->ip_ioc4_soft, hooks->intr_clear,
                       IOC4_W_IEC, IOC4_SIO_INTR_TYPE);
        port->ip_ienb &= ~hooks->intr_clear;
-       writel(hooks->intr_clear, &port->ip_mem->sio_ir);
+       writel(hooks->intr_clear, &port->ip_mem->sio_ir.raw);
        return 0;
 }
 
@@ -918,23 +926,23 @@ static void handle_dma_error_intr(void *arg, uint32_t other_ir)
        spin_lock_irqsave(&port->ip_lock, flags);
 
        /* ACK the interrupt */
-       writel(hooks->intr_dma_error, &port->ip_mem->other_ir);
+       writel(hooks->intr_dma_error, &port->ip_mem->other_ir.raw);
 
-       if (readl(&port->ip_mem->pci_err_addr_l) & IOC4_PCI_ERR_ADDR_VLD) {
+       if (readl(&port->ip_mem->pci_err_addr_l.raw) & IOC4_PCI_ERR_ADDR_VLD) {
                printk(KERN_ERR
                        "PCI error address is 0x%lx, "
                                "master is serial port %c %s\n",
                     (((uint64_t)readl(&port->ip_mem->pci_err_addr_h)
                                                         << 32)
-                               | readl(&port->ip_mem->pci_err_addr_l))
+                               | readl(&port->ip_mem->pci_err_addr_l.raw))
                                        & IOC4_PCI_ERR_ADDR_ADDR_MSK, '1' +
-                    ((char)(readl(&port->ip_mem-> pci_err_addr_l) &
+                    ((char)(readl(&port->ip_mem->pci_err_addr_l.raw) &
                             IOC4_PCI_ERR_ADDR_MST_NUM_MSK) >> 1),
-                    (readl(&port->ip_mem->pci_err_addr_l)
+                    (readl(&port->ip_mem->pci_err_addr_l.raw)
                                & IOC4_PCI_ERR_ADDR_MST_TYP_MSK)
                                ? "RX" : "TX");
 
-               if (readl(&port->ip_mem->pci_err_addr_l)
+               if (readl(&port->ip_mem->pci_err_addr_l.raw)
                                                & IOC4_PCI_ERR_ADDR_MUL_ERR) {
                        printk(KERN_ERR
                                "Multiple errors occurred\n");
@@ -981,6 +989,7 @@ intr_connect(struct ioc4_soft *soft, int type,
  * @arg: handler arg
  * @regs: registers
  */
+
 static irqreturn_t ioc4_intr(int irq, void *arg, struct pt_regs *regs)
 {
        struct ioc4_soft *soft;
@@ -988,7 +997,7 @@ static irqreturn_t ioc4_intr(int irq, void *arg, struct pt_regs *regs)
        int xx, num_intrs = 0;
        int intr_type;
        int handled = 0;
-       struct ioc4_intr_info *ii;
+       struct ioc4_intr_info *intr_info;
 
        soft = arg;
        for (intr_type = 0; intr_type < IOC4_NUM_INTR_TYPES; intr_type++) {
@@ -1001,43 +1010,30 @@ static irqreturn_t ioc4_intr(int irq, void *arg, struct pt_regs *regs)
                 * which interrupt bits are set.
                 */
                for (xx = 0; xx < num_intrs; xx++) {
-                       ii = &soft->is_intr_type[intr_type].is_intr_info[xx];
-                       if ((this_mir = this_ir & ii->sd_bits)) {
+                       intr_info = &soft->is_intr_type[intr_type].is_intr_info[xx];
+                       if ((this_mir = this_ir & intr_info->sd_bits)) {
                                /* Disable owned interrupts, call handler */
                                handled++;
-                               write_ireg(soft, ii->sd_bits, IOC4_W_IEC,
+                               write_ireg(soft, intr_info->sd_bits, IOC4_W_IEC,
                                                                intr_type);
-                               ii->sd_intr(ii->sd_info, this_mir);
+                               intr_info->sd_intr(intr_info->sd_info, this_mir);
                                this_ir &= ~this_mir;
                        }
                }
-               if (this_ir) {
-                       printk(KERN_ERR
-                              "unknown IOC4 %s interrupt 0x%x, sio_ir = 0x%x,"
-                               " sio_ies = 0x%x, other_ir = 0x%x :"
-                               "other_ies = 0x%x\n",
-                              (intr_type == IOC4_SIO_INTR_TYPE) ? "sio" :
-                              "other", this_ir,
-                              readl(&soft->is_ioc4_mem_addr->sio_ir),
-                              readl(&soft->is_ioc4_mem_addr->sio_ies_ro),
-                              readl(&soft->is_ioc4_mem_addr->other_ir),
-                              readl(&soft->is_ioc4_mem_addr->other_ies_ro));
-               }
        }
 #ifdef DEBUG_INTERRUPTS
        {
-               struct ioc4_mem __iomem *mem = soft->is_ioc4_mem_addr;
-               spinlock_t *lp = &soft->is_ir_lock;
+               struct ioc4_misc_regs __iomem *mem = soft->is_ioc4_misc_addr;
                unsigned long flag;
 
                spin_lock_irqsave(&soft->is_ir_lock, flag);
-               printk ("%s : %d : mem 0x%p sio_ir 0x%x sio_ies_ro 0x%x "
-                               "other_ir 0x%x other_ies_ro 0x%x mask 0x%x\n",
+               printk ("%s : %d : mem 0x%p sio_ir 0x%x sio_ies 0x%x "
+                               "other_ir 0x%x other_ies 0x%x mask 0x%x\n",
                     __FUNCTION__, __LINE__,
-                    (void *)mem, readl(&mem->sio_ir),
-                    readl(&mem->sio_ies_ro),
-                    readl(&mem->other_ir),
-                    readl(&mem->other_ies_ro),
+                    (void *)mem, readl(&mem->sio_ir.raw),
+                    readl(&mem->sio_ies.raw),
+                    readl(&mem->other_ir.raw),
+                    readl(&mem->other_ies.raw),
                     IOC4_OTHER_IR_ATA_INT | IOC4_OTHER_IR_ATA_MEMERR);
                spin_unlock_irqrestore(&soft->is_ir_lock, flag);
        }
@@ -1049,21 +1045,20 @@ static irqreturn_t ioc4_intr(int irq, void *arg, struct pt_regs *regs)
  * ioc4_attach_local - Device initialization.
  *                     Called at *_attach() time for each
  *                     IOC4 with serial ports in the system.
- * @control: ioc4_control ptr
- * @pdev: PCI handle for this device
- * @soft: soft struct for this device
- * @ioc4: ioc4 mem space
+ * @idd: Master module data for this IOC4
  */
-static int inline ioc4_attach_local(struct pci_dev *pdev,
-                       struct ioc4_control *control,
-                       struct ioc4_soft *soft, void __iomem *ioc4_mem,
-                       void __iomem *ioc4_serial)
+static int inline ioc4_attach_local(struct ioc4_driver_data *idd)
 {
        struct ioc4_port *port;
        struct ioc4_port *ports[IOC4_NUM_SERIAL_PORTS];
        int port_number;
        uint16_t ioc4_revid_min = 62;
        uint16_t ioc4_revid;
+       struct pci_dev *pdev = idd->idd_pdev;
+       struct ioc4_control* control = idd->idd_serial_data;
+       struct ioc4_soft *soft = control->ic_soft;
+       void __iomem *ioc4_misc = idd->idd_misc_regs;
+       void __iomem *ioc4_serial = soft->is_ioc4_serial_addr;
 
        /* IOC4 firmware must be at least rev 62 */
        pci_read_config_word(pdev, PCI_COMMAND_SPECIAL, &ioc4_revid);
@@ -1076,7 +1071,7 @@ static int inline ioc4_attach_local(struct pci_dev *pdev,
                                ioc4_revid, ioc4_revid_min);
                return -EPERM;
        }
-       BUG_ON(ioc4_mem == NULL);
+       BUG_ON(ioc4_misc == NULL);
        BUG_ON(ioc4_serial == NULL);
 
        /* Create port structures for each port */
@@ -1089,6 +1084,7 @@ static int inline ioc4_attach_local(struct pci_dev *pdev,
                        return -ENOMEM;
                }
                memset(port, 0, sizeof(struct ioc4_port));
+               spin_lock_init(&port->ip_lock);
 
                /* we need to remember the previous ones, to point back to
                 * them farther down - setting up the ring buffers.
@@ -1100,10 +1096,18 @@ static int inline ioc4_attach_local(struct pci_dev *pdev,
                port->ip_ioc4_soft = soft;
                port->ip_pdev = pdev;
                port->ip_ienb = 0;
-               port->ip_pci_bus_speed = IOC4_SER_XIN_CLK;
+               /* Use baud rate calculations based on detected PCI
+                * bus speed.  Simply test whether the PCI clock is
+                * running closer to 66MHz or 33MHz.
+                */
+               if (idd->count_period/IOC4_EXTINT_COUNT_DIVISOR < 20) {
+                       port->ip_pci_bus_speed = IOC4_SER_XIN_CLK_66;
+               } else {
+                       port->ip_pci_bus_speed = IOC4_SER_XIN_CLK_33;
+               }
                port->ip_baud = 9600;
                port->ip_control = control;
-               port->ip_mem = ioc4_mem;
+               port->ip_mem = ioc4_misc;
                port->ip_serial = ioc4_serial;
 
                /* point to the right hook */
@@ -1216,7 +1220,7 @@ static inline int local_open(struct ioc4_port *port)
 {
        int spiniter = 0;
 
-       port->ip_flags = 0;
+       port->ip_flags = PORT_ACTIVE;
 
        /* Pause the DMA interface if necessary */
        if (port->ip_sscr & IOC4_SSCR_DMA_EN) {
@@ -1226,6 +1230,7 @@ static inline int local_open(struct ioc4_port *port)
                                & IOC4_SSCR_PAUSE_STATE) == 0) {
                        spiniter++;
                        if (spiniter > MAXITER) {
+                               port->ip_flags = PORT_INACTIVE;
                                return -1;
                        }
                }
@@ -1545,14 +1550,13 @@ static int set_notification(struct ioc4_port *port, int mask, int set_on)
 /**
  * set_mcr - set the master control reg
  * @the_port: port to use
- * @set: set ?
  * @mask1: mcr mask
  * @mask2: shadow mask
  */
-static inline int set_mcr(struct uart_port *the_port, int set,
+static inline int set_mcr(struct uart_port *the_port,
                int mask1, int mask2)
 {
-       struct ioc4_port *port = get_ioc4_port(the_port);
+       struct ioc4_port *port = get_ioc4_port(the_port, 0);
        uint32_t shadow;
        int spiniter = 0;
        char mcr;
@@ -1575,13 +1579,9 @@ static inline int set_mcr(struct uart_port *the_port, int set,
        mcr = (shadow & 0xff000000) >> 24;
 
        /* Set new value */
-       if (set) {
-               mcr |= mask1;
-               shadow |= mask2;
-       } else {
-               mcr &= ~mask1;
-               shadow &= ~mask2;
-       }
+       mcr |= mask1;
+       shadow |= mask2;
+
        writeb(mcr, &port->ip_uart_regs->i4u_mcr);
        writel(shadow, &port->ip_serial_regs->shadow);
 
@@ -1597,21 +1597,19 @@ static inline int set_mcr(struct uart_port *the_port, int set,
  * @port: port to use
  * @proto: protocol to use
  */
-static int ioc4_set_proto(struct ioc4_port *port, enum sio_proto proto)
+static int ioc4_set_proto(struct ioc4_port *port, int proto)
 {
        struct hooks *hooks = port->ip_hooks;
 
        switch (proto) {
        case PROTO_RS232:
                /* Clear the appropriate GIO pin */
-               writel(0, (&port->ip_mem->gppr_0 +
-                                 hooks->rs422_select_pin));
+               writel(0, (&port->ip_mem->gppr[hooks->rs422_select_pin].raw));
                break;
 
        case PROTO_RS422:
                /* Set the appropriate GIO pin */
-               writel(1, (&port->ip_mem->gppr_0 +
-                                 hooks->rs422_select_pin));
+               writel(1, (&port->ip_mem->gppr[hooks->rs422_select_pin].raw));
                break;
 
        default:
@@ -1630,7 +1628,7 @@ static void transmit_chars(struct uart_port *the_port)
        int result;
        char *start;
        struct tty_struct *tty;
-       struct ioc4_port *port = get_ioc4_port(the_port);
+       struct ioc4_port *port = get_ioc4_port(the_port, 0);
        struct uart_info *info;
 
        if (!the_port)
@@ -1686,7 +1684,7 @@ static void
 ioc4_change_speed(struct uart_port *the_port,
                  struct termios *new_termios, struct termios *old_termios)
 {
-       struct ioc4_port *port = get_ioc4_port(the_port);
+       struct ioc4_port *port = get_ioc4_port(the_port, 0);
        int baud, bits;
        unsigned cflag;
        int new_parity = 0, new_parity_enable = 0, new_stop = 0, new_data = 8;
@@ -1736,12 +1734,14 @@ ioc4_change_speed(struct uart_port *the_port,
                baud = 9600;
 
        if (!the_port->fifosize)
-               the_port->fifosize = IOC4_MAX_CHARS;
+               the_port->fifosize = IOC4_FIFO_CHARS;
        the_port->timeout = ((the_port->fifosize * HZ * bits) / (baud / 10));
        the_port->timeout += HZ / 50;   /* Add .02 seconds of slop */
 
        the_port->ignore_status_mask = N_ALL_INPUT;
 
+       info->tty->low_latency = 1;
+
        if (I_IGNPAR(info->tty))
                the_port->ignore_status_mask &= ~(N_PARITY_ERROR
                                                | N_FRAMING_ERROR);
@@ -1756,11 +1756,9 @@ ioc4_change_speed(struct uart_port *the_port,
        }
 
        if (cflag & CRTSCTS) {
-               info->flags |= ASYNC_CTS_FLOW;
                port->ip_sscr |= IOC4_SSCR_HFC_EN;
        }
        else {
-               info->flags &= ~ASYNC_CTS_FLOW;
                port->ip_sscr &= ~IOC4_SSCR_HFC_EN;
        }
        writel(port->ip_sscr, &port->ip_serial_regs->sscr);
@@ -1787,52 +1785,42 @@ ioc4_change_speed(struct uart_port *the_port,
  */
 static inline int ic4_startup_local(struct uart_port *the_port)
 {
-       int retval = 0;
        struct ioc4_port *port;
        struct uart_info *info;
 
        if (!the_port)
                return -1;
 
-       port = get_ioc4_port(the_port);
+       port = get_ioc4_port(the_port, 0);
        if (!port)
                return -1;
 
        info = the_port->info;
-       if (info->flags & UIF_INITIALIZED) {
-               return retval;
-       }
 
-       if (info->tty) {
-               set_bit(TTY_IO_ERROR, &info->tty->flags);
-               clear_bit(TTY_IO_ERROR, &info->tty->flags);
-               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
-                       info->tty->alt_speed = 57600;
-               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
-                       info->tty->alt_speed = 115200;
-               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
-                       info->tty->alt_speed = 230400;
-               if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
-                       info->tty->alt_speed = 460800;
-       }
        local_open(port);
 
+       /* set the protocol - mapbase has the port type */
+       ioc4_set_proto(port, the_port->mapbase);
+
        /* set the speed of the serial port */
        ioc4_change_speed(the_port, info->tty->termios, (struct termios *)0);
 
-       info->flags |= UIF_INITIALIZED;
        return 0;
 }
 
 /*
  * ioc4_cb_output_lowat - called when the output low water mark is hit
- * @port: port to output
+ * @the_port: port to output
  */
-static void ioc4_cb_output_lowat(struct ioc4_port *port)
+static void ioc4_cb_output_lowat(struct uart_port *the_port)
 {
+       unsigned long pflags;
+
        /* ip_lock is set on the call here */
-       if (port->ip_port) {
-               transmit_chars(port->ip_port);
+       if (the_port) {
+               spin_lock_irqsave(&the_port->lock, pflags);
+               transmit_chars(the_port);
+               spin_unlock_irqrestore(&the_port->lock, pflags);
        }
 }
 
@@ -1885,7 +1873,7 @@ static void handle_intr(void *arg, uint32_t sio_ir)
                if (sio_ir & hooks->intr_delta_dcd) {
                        /* ACK the interrupt */
                        writel(hooks->intr_delta_dcd,
-                               &port->ip_mem->sio_ir);
+                               &port->ip_mem->sio_ir.raw);
 
                        shadow = readl(&port->ip_serial_regs->shadow);
 
@@ -1907,7 +1895,7 @@ static void handle_intr(void *arg, uint32_t sio_ir)
                if (sio_ir & hooks->intr_delta_cts) {
                        /* ACK the interrupt */
                        writel(hooks->intr_delta_cts,
-                                       &port->ip_mem->sio_ir);
+                                       &port->ip_mem->sio_ir.raw);
 
                        shadow = readl(&port->ip_serial_regs->shadow);
 
@@ -1928,7 +1916,7 @@ static void handle_intr(void *arg, uint32_t sio_ir)
                if (sio_ir & hooks->intr_rx_timer) {
                        /* ACK the interrupt */
                        writel(hooks->intr_rx_timer,
-                               &port->ip_mem->sio_ir);
+                               &port->ip_mem->sio_ir.raw);
 
                        if ((port->ip_notify & N_DATA_READY)
                                        && (port->ip_port)) {
@@ -1974,10 +1962,10 @@ static void handle_intr(void *arg, uint32_t sio_ir)
 
                        /* ACK the interrupt */
                        writel(hooks->intr_tx_explicit,
-                                       &port->ip_mem->sio_ir);
+                                       &port->ip_mem->sio_ir.raw);
 
                        if (port->ip_notify & N_OUTPUT_LOWAT)
-                               ioc4_cb_output_lowat(port);
+                               ioc4_cb_output_lowat(port->ip_port);
                }
 
                /* Handle tx_mt.  Must come after tx_explicit.  */
@@ -1990,7 +1978,7 @@ static void handle_intr(void *arg, uint32_t sio_ir)
                         * So send the notification now.
                         */
                        if (port->ip_notify & N_OUTPUT_LOWAT) {
-                               ioc4_cb_output_lowat(port);
+                               ioc4_cb_output_lowat(port->ip_port);
 
                                /* We need to reload the sio_ir since the lowat
                                 * call may have caused another write to occur,
@@ -2077,7 +2065,7 @@ static inline int do_read(struct uart_port *the_port, unsigned char *buf,
                                int len)
 {
        int prod_ptr, cons_ptr, total;
-       struct ioc4_port *port = get_ioc4_port(the_port);
+       struct ioc4_port *port = get_ioc4_port(the_port, 0);
        struct ring *inring;
        struct ring_entry *entry;
        struct hooks *hooks = port->ip_hooks;
@@ -2109,8 +2097,7 @@ static inline int do_read(struct uart_port *the_port, unsigned char *buf,
         * available data as long as it returns some.
         */
        /* Re-arm the timer */
-       writel(port->ip_rx_cons | IOC4_SRCIR_ARM,
-                       &port->ip_serial_regs->srcir);
+       writel(port->ip_rx_cons | IOC4_SRCIR_ARM, &port->ip_serial_regs->srcir);
 
        prod_ptr = readl(&port->ip_serial_regs->srpir) & PROD_CONS_MASK;
        cons_ptr = port->ip_rx_cons;
@@ -2344,6 +2331,7 @@ static inline int do_read(struct uart_port *the_port, unsigned char *buf,
        }
        return total;
 }
+
 /**
  * receive_chars - upper level read. Called with ip_lock.
  * @the_port: port to read from
@@ -2352,9 +2340,10 @@ static void receive_chars(struct uart_port *the_port)
 {
        struct tty_struct *tty;
        unsigned char ch[IOC4_MAX_CHARS];
-       int read_count, request_count;
+       int read_count, request_count = IOC4_MAX_CHARS;
        struct uart_icount *icount;
        struct uart_info *info = the_port->info;
+       unsigned long pflags;
 
        /* Make sure all the pointers are "good" ones */
        if (!info)
@@ -2362,24 +2351,22 @@ static void receive_chars(struct uart_port *the_port)
        if (!info->tty)
                return;
 
+       spin_lock_irqsave(&the_port->lock, pflags);
        tty = info->tty;
 
-       request_count = TTY_FLIPBUF_SIZE - tty->flip.count - 1;
+       request_count = tty_buffer_request_room(tty, IOC4_MAX_CHARS);
 
        if (request_count > 0) {
-               if (request_count > IOC4_MAX_CHARS - 2)
-                       request_count = IOC4_MAX_CHARS - 2;
                icount = &the_port->icount;
                read_count = do_read(the_port, ch, request_count);
                if (read_count > 0) {
-                       memcpy(tty->flip.char_buf_ptr, ch, read_count);
-                       memset(tty->flip.flag_buf_ptr, TTY_NORMAL, read_count);
-                       tty->flip.char_buf_ptr += read_count;
-                       tty->flip.flag_buf_ptr += read_count;
-                       tty->flip.count += read_count;
+                       tty_insert_flip_string(tty, ch, read_count);
                        icount->rx += read_count;
                }
        }
+
+       spin_unlock_irqrestore(&the_port->lock, pflags);
+
        tty_flip_buffer_push(tty);
 }
 
@@ -2390,27 +2377,40 @@ static void receive_chars(struct uart_port *the_port)
  */
 static const char *ic4_type(struct uart_port *the_port)
 {
-       return "SGI IOC4 Serial";
+       if (the_port->mapbase == PROTO_RS232)
+               return "SGI IOC4 Serial [rs232]";
+       else
+               return "SGI IOC4 Serial [rs422]";
 }
 
 /**
- * ic4_tx_empty - Is the transmitter empty?  We pretend we're always empty
- * @port: Port to operate on (we ignore since we always return 1)
+ * ic4_tx_empty - Is the transmitter empty?
+ * @port: Port to operate on
  *
  */
 static unsigned int ic4_tx_empty(struct uart_port *the_port)
 {
-       return 1;
+       struct ioc4_port *port = get_ioc4_port(the_port, 0);
+       unsigned int ret = 0;
+
+       if (port_is_active(port, the_port)) {
+               if (readl(&port->ip_serial_regs->shadow) & IOC4_SHADOW_TEMT)
+                       ret = TIOCSER_TEMT;
+       }
+       return ret;
 }
 
 /**
  * ic4_stop_tx - stop the transmitter
  * @port: Port to operate on
- * @tty_stop: Set to 1 if called via uart_stop
  *
  */
-static void ic4_stop_tx(struct uart_port *the_port, unsigned int tty_stop)
+static void ic4_stop_tx(struct uart_port *the_port)
 {
+       struct ioc4_port *port = get_ioc4_port(the_port, 0);
+
+       if (port_is_active(port, the_port))
+               set_notification(port, N_OUTPUT_LOWAT, 0);
 }
 
 /**
@@ -2433,24 +2433,22 @@ static void ic4_shutdown(struct uart_port *the_port)
        struct ioc4_port *port;
        struct uart_info *info;
 
-       port = get_ioc4_port(the_port);
+       port = get_ioc4_port(the_port, 0);
        if (!port)
                return;
 
        info = the_port->info;
-
-       if (!(info->flags & UIF_INITIALIZED))
-               return;
+       port->ip_port = NULL;
 
        wake_up_interruptible(&info->delta_msr_wait);
 
        if (info->tty)
                set_bit(TTY_IO_ERROR, &info->tty->flags);
 
-       spin_lock_irqsave(&port->ip_lock, port_flags);
+       spin_lock_irqsave(&the_port->lock, port_flags);
        set_notification(port, N_ALL, 0);
-       info->flags &= ~UIF_INITIALIZED;
-       spin_unlock_irqrestore(&port->ip_lock, port_flags);
+       port->ip_flags = PORT_INACTIVE;
+       spin_unlock_irqrestore(&the_port->lock, port_flags);
 }
 
 /**
@@ -2462,6 +2460,11 @@ static void ic4_shutdown(struct uart_port *the_port)
 static void ic4_set_mctrl(struct uart_port *the_port, unsigned int mctrl)
 {
        unsigned char mcr = 0;
+       struct ioc4_port *port;
+
+       port = get_ioc4_port(the_port, 0);
+       if (!port_is_active(port, the_port))
+               return;
 
        if (mctrl & TIOCM_RTS)
                mcr |= UART_MCR_RTS;
@@ -2474,7 +2477,7 @@ static void ic4_set_mctrl(struct uart_port *the_port, unsigned int mctrl)
        if (mctrl & TIOCM_LOOP)
                mcr |= UART_MCR_LOOP;
 
-       set_mcr(the_port, 1, mcr, IOC4_SHADOW_DTR);
+       set_mcr(the_port, mcr, IOC4_SHADOW_DTR);
 }
 
 /**
@@ -2484,11 +2487,11 @@ static void ic4_set_mctrl(struct uart_port *the_port, unsigned int mctrl)
  */
 static unsigned int ic4_get_mctrl(struct uart_port *the_port)
 {
-       struct ioc4_port *port = get_ioc4_port(the_port);
+       struct ioc4_port *port = get_ioc4_port(the_port, 0);
        uint32_t shadow;
        unsigned int ret = 0;
 
-       if (!port)
+       if (!port_is_active(port, the_port))
                return 0;
 
        shadow = readl(&port->ip_serial_regs->shadow);
@@ -2504,18 +2507,15 @@ static unsigned int ic4_get_mctrl(struct uart_port *the_port)
 /**
  * ic4_start_tx - Start transmitter, flush any output
  * @port: Port to operate on
- * @tty_stop: Set to 1 if called via uart_start
  *
  */
-static void ic4_start_tx(struct uart_port *the_port, unsigned int tty_stop)
+static void ic4_start_tx(struct uart_port *the_port)
 {
-       struct ioc4_port *port = get_ioc4_port(the_port);
-       unsigned long flags;
+       struct ioc4_port *port = get_ioc4_port(the_port, 0);
 
-       if (port) {
-               spin_lock_irqsave(&port->ip_lock, flags);
-               transmit_chars(the_port);
-               spin_unlock_irqrestore(&port->ip_lock, flags);
+       if (port_is_active(port, the_port)) {
+               set_notification(port, N_OUTPUT_LOWAT, 1);
+               enable_intrs(port, port->ip_hooks->intr_tx_mt);
        }
 }
 
@@ -2530,7 +2530,7 @@ static void ic4_break_ctl(struct uart_port *the_port, int break_state)
 }
 
 /**
- * ic4_startup - Start up the serial port - always return 0 (We're always on)
+ * ic4_startup - Start up the serial port
  * @port: Port to operate on
  *
  */
@@ -2542,24 +2542,23 @@ static int ic4_startup(struct uart_port *the_port)
        struct uart_info *info;
        unsigned long port_flags;
 
-       if (!the_port) {
+       if (!the_port)
                return -ENODEV;
-       }
-       port = get_ioc4_port(the_port);
-       if (!port) {
+       port = get_ioc4_port(the_port, 1);
+       if (!port)
                return -ENODEV;
-       }
        info = the_port->info;
 
        control = port->ip_control;
        if (!control) {
+               port->ip_port = NULL;
                return -ENODEV;
        }
 
        /* Start up the serial port */
-       spin_lock_irqsave(&port->ip_lock, port_flags);
+       spin_lock_irqsave(&the_port->lock, port_flags);
        retval = ic4_startup_local(the_port);
-       spin_unlock_irqrestore(&port->ip_lock, port_flags);
+       spin_unlock_irqrestore(&the_port->lock, port_flags);
        return retval;
 }
 
@@ -2574,12 +2573,11 @@ static void
 ic4_set_termios(struct uart_port *the_port,
                struct termios *termios, struct termios *old_termios)
 {
-       struct ioc4_port *port = get_ioc4_port(the_port);
        unsigned long port_flags;
 
-       spin_lock_irqsave(&port->ip_lock, port_flags);
+       spin_lock_irqsave(&the_port->lock, port_flags);
        ioc4_change_speed(the_port, termios, old_termios);
-       spin_unlock_irqrestore(&port->ip_lock, port_flags);
+       spin_unlock_irqrestore(&the_port->lock, port_flags);
 }
 
 /**
@@ -2615,27 +2613,104 @@ static struct uart_ops ioc4_ops = {
  * Boot-time initialization code
  */
 
-static struct uart_driver ioc4_uart = {
+static struct uart_driver ioc4_uart_rs232 = {
+       .owner          = THIS_MODULE,
+       .driver_name    = "ioc4_serial_rs232",
+       .dev_name       = DEVICE_NAME_RS232,
+       .major          = DEVICE_MAJOR,
+       .minor          = DEVICE_MINOR_RS232,
+       .nr             = IOC4_NUM_CARDS * IOC4_NUM_SERIAL_PORTS,
+};
+
+static struct uart_driver ioc4_uart_rs422 = {
        .owner          = THIS_MODULE,
-       .driver_name    = "ioc4_serial",
-       .dev_name       = DEVICE_NAME,
+       .driver_name    = "ioc4_serial_rs422",
+       .dev_name       = DEVICE_NAME_RS422,
        .major          = DEVICE_MAJOR,
-       .minor          = DEVICE_MINOR,
+       .minor          = DEVICE_MINOR_RS422,
        .nr             = IOC4_NUM_CARDS * IOC4_NUM_SERIAL_PORTS,
 };
 
+
+/**
+ * ioc4_serial_remove_one - detach function
+ *
+ * @idd: IOC4 master module data for this IOC4
+ */
+
+static int ioc4_serial_remove_one(struct ioc4_driver_data *idd)
+{
+       int port_num, port_type;
+       struct ioc4_control *control;
+       struct uart_port *the_port;
+       struct ioc4_port *port;
+       struct ioc4_soft *soft;
+
+       control = idd->idd_serial_data;
+
+       for (port_num = 0; port_num < IOC4_NUM_SERIAL_PORTS; port_num++) {
+               for (port_type = UART_PORT_MIN;
+                                       port_type < UART_PORT_COUNT;
+                                       port_type++) {
+                       the_port = &control->ic_port[port_num].icp_uart_port
+                                                       [port_type];
+                       if (the_port) {
+                               switch (port_type) {
+                               case UART_PORT_RS422:
+                                       uart_remove_one_port(&ioc4_uart_rs422,
+                                                       the_port);
+                                       break;
+                               default:
+                               case UART_PORT_RS232:
+                                       uart_remove_one_port(&ioc4_uart_rs232,
+                                                       the_port);
+                                       break;
+                               }
+                       }
+               }
+               port = control->ic_port[port_num].icp_port;
+               /* we allocate in pairs */
+               if (!(port_num & 1) && port) {
+                       pci_free_consistent(port->ip_pdev,
+                                       TOTAL_RING_BUF_SIZE,
+                                       port->ip_cpu_ringbuf,
+                                       port->ip_dma_ringbuf);
+                       kfree(port);
+               }
+       }
+       soft = control->ic_soft;
+       if (soft) {
+               free_irq(control->ic_irq, soft);
+               if (soft->is_ioc4_serial_addr) {
+                       release_region((unsigned long)
+                            soft->is_ioc4_serial_addr,
+                               sizeof(struct ioc4_serial));
+               }
+               kfree(soft);
+       }
+       kfree(control);
+       idd->idd_serial_data = NULL;
+
+       return 0;
+}
+
+
 /**
- * ioc4_serial_core_attach - register with serial core
+ * ioc4_serial_core_attach_rs232 - register with serial core
  *             This is done during pci probing
  * @pdev: handle for this card
  */
 static inline int
-ioc4_serial_core_attach(struct pci_dev *pdev)
+ioc4_serial_core_attach(struct pci_dev *pdev, int port_type)
 {
        struct ioc4_port *port;
        struct uart_port *the_port;
-       struct ioc4_control *control = pci_get_drvdata(pdev);
-       int ii;
+       struct ioc4_driver_data *idd = pci_get_drvdata(pdev);
+       struct ioc4_control *control = idd->idd_serial_data;
+       int port_num;
+       int port_type_idx;
+       struct uart_driver *u_driver;
+
 
        DPRINT_CONFIG(("%s: attach pdev 0x%p - control 0x%p\n",
                        __FUNCTION__, pdev, (void *)control));
@@ -2643,92 +2718,74 @@ ioc4_serial_core_attach(struct pci_dev *pdev)
        if (!control)
                return -ENODEV;
 
+       port_type_idx = (port_type == PROTO_RS232) ? UART_PORT_RS232
+                                               : UART_PORT_RS422;
+
+       u_driver = (port_type == PROTO_RS232)   ? &ioc4_uart_rs232
+                                               : &ioc4_uart_rs422;
+
        /* once around for each port on this card */
-       for (ii = 0; ii < IOC4_NUM_SERIAL_PORTS; ii++) {
-               the_port = &control->ic_port[ii].icp_uart_port;
-               port = control->ic_port[ii].icp_port;
-               port->ip_port = the_port;
+       for (port_num = 0; port_num < IOC4_NUM_SERIAL_PORTS; port_num++) {
+               the_port = &control->ic_port[port_num].icp_uart_port
+                                                       [port_type_idx];
+               port = control->ic_port[port_num].icp_port;
+               port->ip_all_ports[port_type_idx] = the_port;
 
-               DPRINT_CONFIG(("%s: attach the_port 0x%p / port 0x%p\n",
+               DPRINT_CONFIG(("%s: attach the_port 0x%p / port 0x%p : type %s\n",
                                __FUNCTION__, (void *)the_port,
-                               (void *)port));
+                               (void *)port,
+                               port_type == PROTO_RS232 ? "rs232" : "rs422"));
 
-               spin_lock_init(&the_port->lock);
                /* membase, iobase and mapbase just need to be non-0 */
                the_port->membase = (unsigned char __iomem *)1;
-               the_port->line = the_port->iobase = ii;
-               the_port->mapbase = 1;
+               the_port->iobase = (pdev->bus->number << 16) |  port_num;
+               the_port->line = (Num_of_ioc4_cards << 2) | port_num;
+               the_port->mapbase = port_type;
                the_port->type = PORT_16550A;
-               the_port->fifosize = IOC4_MAX_CHARS;
+               the_port->fifosize = IOC4_FIFO_CHARS;
                the_port->ops = &ioc4_ops;
                the_port->irq = control->ic_irq;
                the_port->dev = &pdev->dev;
-               if (uart_add_one_port(&ioc4_uart, the_port) < 0) {
+               spin_lock_init(&the_port->lock);
+               if (uart_add_one_port(u_driver, the_port) < 0) {
                        printk(KERN_WARNING
-                                      "%s: unable to add port %d\n",
-                                      __FUNCTION__, the_port->line);
+                          "%s: unable to add port %d bus %d\n",
+                              __FUNCTION__, the_port->line, pdev->bus->number);
                } else {
                        DPRINT_CONFIG(
-                                   ("IOC4 serial driver port %d irq = %d\n",
-                                      the_port->line, the_port->irq));
+                           ("IOC4 serial port %d irq = %d, bus %d\n",
+                              the_port->line, the_port->irq, pdev->bus->number));
                }
-               /* all ports are rs232 for now */
-               ioc4_set_proto(port, PROTO_RS232);
        }
        return 0;
 }
 
 /**
  * ioc4_serial_attach_one - register attach function
- *             called per card found from ioc4_serial_detect as part
- *             of module_init().
- * @pdev: handle for this card
- * @pci_id: pci id for this card
+ *             called per card found from IOC4 master module.
+ * @idd: Master module data for this IOC4
  */
 int
-ioc4_serial_attach_one(struct pci_dev *pdev, const struct pci_device_id *pci_id)
+ioc4_serial_attach_one(struct ioc4_driver_data *idd)
 {
-       struct ioc4_mem __iomem *mem;
-       unsigned long tmp_addr, tmp_addr1;
+       unsigned long tmp_addr1;
        struct ioc4_serial __iomem *serial;
        struct ioc4_soft *soft;
        struct ioc4_control *control;
-       int tmp, ret = 0;
+       int ret = 0;
 
 
-       DPRINT_CONFIG(("%s (0x%p, 0x%p)\n", __FUNCTION__, pdev, pci_id));
-
-       /* Map in the ioc4 memory */
-       tmp_addr = pci_resource_start(pdev, 0);
-       if (!tmp_addr) {
-               printk(KERN_WARNING
-                        "ioc4 (%p) : unable to get PIO mapping for "
-                               "MEM space\n", (void *)pdev);
-               return -ENODEV;
-       }
-       if (!request_region(tmp_addr, sizeof(struct ioc4_mem), "sioc4_mem")) {
-               printk(KERN_ALERT
-                       "ioc4 (%p): unable to get request region for "
-                       "MEM space\n", (void *)pdev);
-               return -ENODEV;
-       }
-       mem = ioremap(tmp_addr, sizeof(struct ioc4_mem));
-       if (!mem) {
-               printk(KERN_WARNING
-                        "ioc4 (%p) : unable to remap ioc4 memory\n",
-                               (void *)pdev);
-               ret = -ENODEV;
-               goto out1;
-       }
+       DPRINT_CONFIG(("%s (0x%p, 0x%p)\n", __FUNCTION__, idd->idd_pdev,
+                                                       idd->idd_pci_id));
 
        /* request serial registers */
-       tmp_addr1 = pci_resource_start(pdev, 0) + IOC4_SERIAL_OFFSET;
+       tmp_addr1 = idd->idd_bar0 + IOC4_SERIAL_OFFSET;
 
        if (!request_region(tmp_addr1, sizeof(struct ioc4_serial),
                                        "sioc4_uart")) {
                printk(KERN_WARNING
                        "ioc4 (%p): unable to get request region for "
-                               "uart space\n", (void *)pdev);
+                               "uart space\n", (void *)idd->idd_pdev);
                ret = -ENODEV;
                goto out1;
        }
@@ -2736,16 +2793,16 @@ ioc4_serial_attach_one(struct pci_dev *pdev, const struct pci_device_id *pci_id)
        if (!serial) {
                printk(KERN_WARNING
                         "ioc4 (%p) : unable to remap ioc4 serial register\n",
-                               (void *)pdev);
+                               (void *)idd->idd_pdev);
                ret = -ENODEV;
                goto out2;
        }
        DPRINT_CONFIG(("%s : mem 0x%p, serial 0x%p\n",
-                               __FUNCTION__, (void *)mem, (void *)serial));
+                               __FUNCTION__, (void *)idd->idd_misc_regs,
+                               (void *)serial));
 
        /* Get memory for the new card */
-       control = kmalloc(sizeof(struct ioc4_control) * IOC4_NUM_SERIAL_PORTS,
-                                               GFP_KERNEL);
+       control = kmalloc(sizeof(struct ioc4_control), GFP_KERNEL);
 
        if (!control) {
                printk(KERN_WARNING "ioc4_attach_one"
@@ -2754,64 +2811,68 @@ ioc4_serial_attach_one(struct pci_dev *pdev, const struct pci_device_id *pci_id)
                goto out2;
        }
        memset(control, 0, sizeof(struct ioc4_control));
-       pci_set_drvdata(pdev, control);
+       idd->idd_serial_data = control;
 
        /* Allocate the soft structure */
        soft = kmalloc(sizeof(struct ioc4_soft), GFP_KERNEL);
        if (!soft) {
                printk(KERN_WARNING
                       "ioc4 (%p): unable to get memory for the soft struct\n",
-                      (void *)pdev);
+                      (void *)idd->idd_pdev);
                ret = -ENOMEM;
                goto out3;
        }
        memset(soft, 0, sizeof(struct ioc4_soft));
 
        spin_lock_init(&soft->is_ir_lock);
-       soft->is_ioc4_mem_addr = mem;
+       soft->is_ioc4_misc_addr = idd->idd_misc_regs;
        soft->is_ioc4_serial_addr = serial;
 
        /* Init the IOC4 */
-       pci_read_config_dword(pdev, PCI_COMMAND, &tmp);
-       pci_write_config_dword(pdev, PCI_COMMAND,
-                              tmp | PCI_COMMAND_PARITY | PCI_COMMAND_SERR);
-
-       writel(0xf << IOC4_SIO_CR_CMD_PULSE_SHIFT, &mem->sio_cr);
+       writel(0xf << IOC4_SIO_CR_CMD_PULSE_SHIFT,
+              &idd->idd_misc_regs->sio_cr.raw);
 
        /* Enable serial port mode select generic PIO pins as outputs */
        writel(IOC4_GPCR_UART0_MODESEL | IOC4_GPCR_UART1_MODESEL
                | IOC4_GPCR_UART2_MODESEL | IOC4_GPCR_UART3_MODESEL,
-               &mem->gpcr_s);
+               &idd->idd_misc_regs->gpcr_s.raw);
 
-       /* Clear and disable all interrupts */
+       /* Clear and disable all serial interrupts */
        write_ireg(soft, ~0, IOC4_W_IEC, IOC4_SIO_INTR_TYPE);
-       writel(~0, &mem->sio_ir);
-       write_ireg(soft, ~(IOC4_OTHER_IR_ATA_INT | IOC4_OTHER_IR_ATA_MEMERR),
-                       IOC4_W_IEC, IOC4_OTHER_INTR_TYPE);
-       writel(~(IOC4_OTHER_IR_ATA_MEMERR | IOC4_OTHER_IR_ATA_MEMERR),
-                                       &mem->other_ir);
+       writel(~0, &idd->idd_misc_regs->sio_ir.raw);
+       write_ireg(soft, IOC4_OTHER_IR_SER_MEMERR, IOC4_W_IEC,
+                  IOC4_OTHER_INTR_TYPE);
+       writel(IOC4_OTHER_IR_SER_MEMERR, &idd->idd_misc_regs->other_ir.raw);
        control->ic_soft = soft;
-       if (!request_irq(pdev->irq, ioc4_intr, SA_SHIRQ,
-                               "sgi-ioc4serial", (void *)soft)) {
-               control->ic_irq = pdev->irq;
+
+       /* Hook up interrupt handler */
+       if (!request_irq(idd->idd_pdev->irq, ioc4_intr, SA_SHIRQ,
+                               "sgi-ioc4serial", soft)) {
+               control->ic_irq = idd->idd_pdev->irq;
        } else {
                printk(KERN_WARNING
                    "%s : request_irq fails for IRQ 0x%x\n ",
-                       __FUNCTION__, pdev->irq);
+                       __FUNCTION__, idd->idd_pdev->irq);
        }
-       if ((ret = ioc4_attach_local(pdev, control, soft,
-                               soft->is_ioc4_mem_addr,
-                               soft->is_ioc4_serial_addr)))
+       ret = ioc4_attach_local(idd);
+       if (ret)
                goto out4;
 
-       /* register port with the serial core */
+       /* register port with the serial core - 1 rs232, 1 rs422 */
 
-       if ((ret = ioc4_serial_core_attach(pdev)))
+       if ((ret = ioc4_serial_core_attach(idd->idd_pdev, PROTO_RS232)))
                goto out4;
 
+       if ((ret = ioc4_serial_core_attach(idd->idd_pdev, PROTO_RS422)))
+               goto out5;
+
+       Num_of_ioc4_cards++;
+
        return ret;
 
        /* error exits that give back resources */
+out5:
+       ioc4_serial_remove_one(idd);
 out4:
        kfree(soft);
 out3:
@@ -2819,58 +2880,17 @@ out3:
 out2:
        release_region(tmp_addr1, sizeof(struct ioc4_serial));
 out1:
-       release_region(tmp_addr, sizeof(struct ioc4_mem));
 
        return ret;
 }
 
 
-/**
- * ioc4_serial_remove_one - detach function
- *
- * @pdev: handle for this card
- */
-
-#if 0
-void ioc4_serial_remove_one(struct pci_dev *pdev)
-{
-       int ii;
-       struct ioc4_control *control;
-       struct uart_port *the_port;
-       struct ioc4_port *port;
-       struct ioc4_soft *soft;
-
-       control = pci_get_drvdata(pdev);
-
-       for (ii = 0; ii < IOC4_NUM_SERIAL_PORTS; ii++) {
-               the_port = &control->ic_port[ii].icp_uart_port;
-               if (the_port) {
-                       uart_remove_one_port(&ioc4_uart, the_port);
-               }
-               port = control->ic_port[ii].icp_port;
-               if (!(ii & 1) && port) {
-                       pci_free_consistent(port->ip_pdev,
-                                       TOTAL_RING_BUF_SIZE,
-                                       (void *)port->ip_cpu_ringbuf,
-                                       port->ip_dma_ringbuf);
-                       kfree(port);
-               }
-       }
-       soft = control->ic_soft;
-       if (soft) {
-               free_irq(control->ic_irq, (void *)soft);
-               if (soft->is_ioc4_serial_addr) {
-                       release_region((unsigned long)
-                            soft->is_ioc4_serial_addr,
-                               sizeof(struct ioc4_serial));
-               }
-               kfree(soft);
-       }
-       kfree(control);
-       pci_set_drvdata(pdev, NULL);
-       uart_unregister_driver(&ioc4_uart);
-}
-#endif
+static struct ioc4_submodule ioc4_serial_submodule = {
+       .is_name = "IOC4_serial",
+       .is_owner = THIS_MODULE,
+       .is_probe = ioc4_serial_attach_one,
+       .is_remove = ioc4_serial_remove_one,
+};
 
 /**
  * ioc4_serial_init - module init
@@ -2880,18 +2900,33 @@ int ioc4_serial_init(void)
        int ret;
 
        /* register with serial core */
-       if ((ret = uart_register_driver(&ioc4_uart)) < 0) {
+       if ((ret = uart_register_driver(&ioc4_uart_rs232)) < 0) {
                printk(KERN_WARNING
-                       "%s: Couldn't register IOC4 serial driver\n",
+                       "%s: Couldn't register rs232 IOC4 serial driver\n",
                        __FUNCTION__);
                return ret;
        }
-       return 0;
+       if ((ret = uart_register_driver(&ioc4_uart_rs422)) < 0) {
+               printk(KERN_WARNING
+                       "%s: Couldn't register rs422 IOC4 serial driver\n",
+                       __FUNCTION__);
+               return ret;
+       }
+
+       /* register with IOC4 main module */
+       return ioc4_register_submodule(&ioc4_serial_submodule);
 }
 
+static void __devexit ioc4_serial_exit(void)
+{
+       ioc4_unregister_submodule(&ioc4_serial_submodule);
+       uart_unregister_driver(&ioc4_uart_rs232);
+       uart_unregister_driver(&ioc4_uart_rs422);
+}
+
+module_init(ioc4_serial_init);
+module_exit(ioc4_serial_exit);
+
 MODULE_AUTHOR("Pat Gefre - Silicon Graphics Inc. (SGI) <pfg@sgi.com>");
 MODULE_DESCRIPTION("Serial PCI driver module for SGI IOC4 Base-IO Card");
 MODULE_LICENSE("GPL");
-
-EXPORT_SYMBOL(ioc4_serial_init);
-EXPORT_SYMBOL(ioc4_serial_attach_one);