Staging: ipack: Let interrupts return irqreturn_t.
[cascardo/linux.git] / drivers / staging / ipack / devices / ipoctal.c
index 7e1e6f7..8e61ebd 100644 (file)
 
 static const struct tty_operations ipoctal_fops;
 
+struct ipoctal_channel {
+       struct ipoctal_stats            stats;
+       unsigned int                    nb_bytes;
+       unsigned int                    count_wr;
+       wait_queue_head_t               queue;
+       spinlock_t                      lock;
+       unsigned int                    pointer_read;
+       unsigned int                    pointer_write;
+       atomic_t                        open;
+       struct tty_port                 tty_port;
+       union scc2698_channel __iomem   *regs;
+       union scc2698_block __iomem     *block_regs;
+       unsigned int                    board_id;
+       unsigned char                   *board_write;
+       u8                              isr_rx_rdy_mask;
+       u8                              isr_tx_rdy_mask;
+};
+
 struct ipoctal {
        struct list_head                list;
        struct ipack_device             *dev;
        unsigned int                    board_id;
-       union scc2698_channel __iomem   *chan_regs;
-       union scc2698_block __iomem     *block_regs;
-       struct ipoctal_stats            chan_stats[NR_CHANNELS];
-       unsigned int                    nb_bytes[NR_CHANNELS];
-       unsigned int                    count_wr[NR_CHANNELS];
-       wait_queue_head_t               queue[NR_CHANNELS];
-       spinlock_t                      lock[NR_CHANNELS];
-       unsigned int                    pointer_read[NR_CHANNELS];
-       unsigned int                    pointer_write[NR_CHANNELS];
-       atomic_t                        open[NR_CHANNELS];
+       struct ipoctal_channel          channel[NR_CHANNELS];
        unsigned char                   write;
-       struct tty_port                 tty_port[NR_CHANNELS];
        struct tty_driver               *tty_drv;
 };
 
 /* Linked list to save the registered devices */
 static LIST_HEAD(ipoctal_list);
 
-static inline void ipoctal_write_io_reg(struct ipoctal *ipoctal,
-                                       u8 __iomem *dest,
-                                       u8 value)
-{
-       iowrite8(value, dest);
-}
-
-static inline void ipoctal_write_cr_cmd(struct ipoctal *ipoctal,
-                                       u8 __iomem *dest,
-                                       u8 value)
-{
-       ipoctal_write_io_reg(ipoctal, dest, value);
-}
-
-static inline unsigned char ipoctal_read_io_reg(struct ipoctal *ipoctal,
-                                               u8 __iomem *src)
-{
-       return ioread8(src);
-}
-
 static struct ipoctal *ipoctal_find_board(struct tty_struct *tty)
 {
        struct ipoctal *p;
@@ -87,7 +75,7 @@ static struct ipoctal *ipoctal_find_board(struct tty_struct *tty)
 static int ipoctal_port_activate(struct tty_port *port, struct tty_struct *tty)
 {
        struct ipoctal *ipoctal;
-       int channel = tty->index;
+       struct ipoctal_channel *channel;
 
        ipoctal = ipoctal_find_board(tty);
 
@@ -96,17 +84,17 @@ static int ipoctal_port_activate(struct tty_port *port, struct tty_struct *tty)
                        tty->driver->major);
                return -ENODEV;
        }
+       channel = &ipoctal->channel[tty->index];
 
-       ipoctal_write_io_reg(ipoctal, &ipoctal->chan_regs[channel].w.cr,
-                            CR_ENABLE_RX);
+       iowrite8(CR_ENABLE_RX, &channel->regs->w.cr);
        return 0;
 }
 
 static int ipoctal_open(struct tty_struct *tty, struct file *file)
 {
-       int channel = tty->index;
        int res;
        struct ipoctal *ipoctal;
+       struct ipoctal_channel *channel;
 
        ipoctal = ipoctal_find_board(tty);
 
@@ -115,17 +103,18 @@ static int ipoctal_open(struct tty_struct *tty, struct file *file)
                        tty->driver->major);
                return -ENODEV;
        }
+       channel = &ipoctal->channel[tty->index];
 
-       if (atomic_read(&ipoctal->open[channel]))
+       if (atomic_read(&channel->open))
                return -EBUSY;
 
-       tty->driver_data = ipoctal;
+       tty->driver_data = channel;
 
-       res = tty_port_open(&ipoctal->tty_port[channel], tty, file);
+       res = tty_port_open(&channel->tty_port, tty, file);
        if (res)
                return res;
 
-       atomic_inc(&ipoctal->open[channel]);
+       atomic_inc(&channel->open);
        return 0;
 }
 
@@ -139,175 +128,150 @@ static void ipoctal_reset_stats(struct ipoctal_stats *stats)
        stats->parity_err = 0;
 }
 
-static void ipoctal_free_channel(struct tty_struct *tty)
+static void ipoctal_free_channel(struct ipoctal_channel *channel)
 {
-       int channel = tty->index;
-       struct ipoctal *ipoctal = tty->driver_data;
-
-       if (ipoctal == NULL)
-               return;
-
-       ipoctal_reset_stats(&ipoctal->chan_stats[channel]);
-       ipoctal->pointer_read[channel] = 0;
-       ipoctal->pointer_write[channel] = 0;
-       ipoctal->nb_bytes[channel] = 0;
+       ipoctal_reset_stats(&channel->stats);
+       channel->pointer_read = 0;
+       channel->pointer_write = 0;
+       channel->nb_bytes = 0;
 }
 
 static void ipoctal_close(struct tty_struct *tty, struct file *filp)
 {
-       int channel = tty->index;
-       struct ipoctal *ipoctal = tty->driver_data;
+       struct ipoctal_channel *channel = tty->driver_data;
 
-       tty_port_close(&ipoctal->tty_port[channel], tty, filp);
+       tty_port_close(&channel->tty_port, tty, filp);
 
-       if (atomic_dec_and_test(&ipoctal->open[channel]))
-               ipoctal_free_channel(tty);
+       if (atomic_dec_and_test(&channel->open))
+               ipoctal_free_channel(channel);
 }
 
 static int ipoctal_get_icount(struct tty_struct *tty,
                              struct serial_icounter_struct *icount)
 {
-       struct ipoctal *ipoctal = tty->driver_data;
-       int channel = tty->index;
+       struct ipoctal_channel *channel = tty->driver_data;
 
        icount->cts = 0;
        icount->dsr = 0;
        icount->rng = 0;
        icount->dcd = 0;
-       icount->rx = ipoctal->chan_stats[channel].rx;
-       icount->tx = ipoctal->chan_stats[channel].tx;
-       icount->frame = ipoctal->chan_stats[channel].framing_err;
-       icount->parity = ipoctal->chan_stats[channel].parity_err;
-       icount->brk = ipoctal->chan_stats[channel].rcv_break;
+       icount->rx = channel->stats.rx;
+       icount->tx = channel->stats.tx;
+       icount->frame = channel->stats.framing_err;
+       icount->parity = channel->stats.parity_err;
+       icount->brk = channel->stats.rcv_break;
        return 0;
 }
 
-static int ipoctal_irq_handler(void *arg)
+static void ipoctal_irq_rx(struct ipoctal_channel *channel,
+                          struct tty_struct *tty, u8 sr)
+{
+       unsigned char value = ioread8(&channel->regs->r.rhr);
+       unsigned char flag = TTY_NORMAL;
+
+       /* Error: count statistics */
+       if (sr & SR_ERROR) {
+               iowrite8(CR_CMD_RESET_ERR_STATUS, &channel->regs->w.cr);
+
+               if (sr & SR_OVERRUN_ERROR) {
+                       channel->stats.overrun_err++;
+                       /* Overrun doesn't affect the current character*/
+                       tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+               }
+               if (sr & SR_PARITY_ERROR) {
+                       channel->stats.parity_err++;
+                       flag = TTY_PARITY;
+               }
+               if (sr & SR_FRAMING_ERROR) {
+                       channel->stats.framing_err++;
+                       flag = TTY_FRAME;
+               }
+               if (sr & SR_RECEIVED_BREAK) {
+                       channel->stats.rcv_break++;
+                       flag = TTY_BREAK;
+               }
+       }
+
+       tty_insert_flip_char(tty, value, flag);
+}
+
+static void ipoctal_irq_tx(struct ipoctal_channel *channel)
 {
-       unsigned int channel;
-       unsigned int block;
-       unsigned char isr;
-       unsigned char sr;
-       unsigned char isr_tx_rdy, isr_rx_rdy;
        unsigned char value;
-       unsigned char flag;
-       struct tty_struct *tty;
-       struct ipoctal *ipoctal = (struct ipoctal *) arg;
+       unsigned int *pointer_write = &channel->pointer_write;
 
-       /* Check all channels */
-       for (channel = 0; channel < NR_CHANNELS; channel++) {
-               /* If there is no client, skip the check */
-               if (!atomic_read(&ipoctal->open[channel]))
-                       continue;
+       if (channel->nb_bytes <= 0) {
+               channel->nb_bytes = 0;
+               return;
+       }
 
-               tty = tty_port_tty_get(&ipoctal->tty_port[channel]);
-               if (!tty)
-                       continue;
+       value = channel->tty_port.xmit_buf[*pointer_write];
+       iowrite8(value, &channel->regs->w.thr);
+       channel->stats.tx++;
+       channel->count_wr++;
+       (*pointer_write)++;
+       *pointer_write = *pointer_write % PAGE_SIZE;
+       channel->nb_bytes--;
 
-               /*
-                * The HW is organized in pair of channels.
-                * See which register we need to read from
-                */
-               block = channel / 2;
-               isr = ipoctal_read_io_reg(ipoctal,
-                                         &ipoctal->block_regs[block].r.isr);
-               sr = ipoctal_read_io_reg(ipoctal,
-                                        &ipoctal->chan_regs[channel].r.sr);
-
-               if ((channel % 2) == 1) {
-                       isr_tx_rdy = isr & ISR_TxRDY_B;
-                       isr_rx_rdy = isr & ISR_RxRDY_FFULL_B;
-               } else {
-                       isr_tx_rdy = isr & ISR_TxRDY_A;
-                       isr_rx_rdy = isr & ISR_RxRDY_FFULL_A;
-               }
+       if ((channel->nb_bytes == 0) &&
+           (waitqueue_active(&channel->queue))) {
 
-               /* In case of RS-485, change from TX to RX when finishing TX.
-                * Half-duplex.
-                */
-               if ((ipoctal->board_id == IPACK1_DEVICE_ID_SBS_OCTAL_485) &&
-                   (sr & SR_TX_EMPTY) &&
-                   (ipoctal->nb_bytes[channel] == 0)) {
-                       ipoctal_write_io_reg(ipoctal,
-                                            &ipoctal->chan_regs[channel].w.cr,
-                                            CR_DISABLE_TX);
-                       ipoctal_write_cr_cmd(ipoctal,
-                                            &ipoctal->chan_regs[channel].w.cr,
-                                            CR_CMD_NEGATE_RTSN);
-                       ipoctal_write_io_reg(ipoctal,
-                                            &ipoctal->chan_regs[channel].w.cr,
-                                            CR_ENABLE_RX);
-                       ipoctal->write = 1;
-                       wake_up_interruptible(&ipoctal->queue[channel]);
+               if (channel->board_id != IPACK1_DEVICE_ID_SBS_OCTAL_485) {
+                       *channel->board_write = 1;
+                       wake_up_interruptible(&channel->queue);
                }
+       }
+}
 
-               /* RX data */
-               if (isr_rx_rdy && (sr & SR_RX_READY)) {
-                       value = ipoctal_read_io_reg(ipoctal,
-                                                   &ipoctal->chan_regs[channel].r.rhr);
-                       flag = TTY_NORMAL;
-
-                       /* Error: count statistics */
-                       if (sr & SR_ERROR) {
-                               ipoctal_write_cr_cmd(ipoctal,
-                                                    &ipoctal->chan_regs[channel].w.cr,
-                                                    CR_CMD_RESET_ERR_STATUS);
-
-                               if (sr & SR_OVERRUN_ERROR) {
-                                       ipoctal->chan_stats[channel].overrun_err++;
-                                       /* Overrun doesn't affect the current character*/
-                                       tty_insert_flip_char(tty, 0, TTY_OVERRUN);
-                               }
-                               if (sr & SR_PARITY_ERROR) {
-                                       ipoctal->chan_stats[channel].parity_err++;
-                                       flag = TTY_PARITY;
-                               }
-                               if (sr & SR_FRAMING_ERROR) {
-                                       ipoctal->chan_stats[channel].framing_err++;
-                                       flag = TTY_FRAME;
-                               }
-                               if (sr & SR_RECEIVED_BREAK) {
-                                       ipoctal->chan_stats[channel].rcv_break++;
-                                       flag = TTY_BREAK;
-                               }
-                       }
-
-                       tty_insert_flip_char(tty, value, flag);
-               }
+static void ipoctal_irq_channel(struct ipoctal_channel *channel)
+{
+       u8 isr, sr;
+       struct tty_struct *tty;
 
-               /* TX of each character */
-               if (isr_tx_rdy && (sr & SR_TX_READY)) {
-                       unsigned int *pointer_write =
-                               &ipoctal->pointer_write[channel];
-
-                       if (ipoctal->nb_bytes[channel] <= 0) {
-                               ipoctal->nb_bytes[channel] = 0;
-                               continue;
-                       }
-
-                       value = ipoctal->tty_port[channel].xmit_buf[*pointer_write];
-                       ipoctal_write_io_reg(ipoctal,
-                                            &ipoctal->chan_regs[channel].w.thr,
-                                            value);
-                       ipoctal->chan_stats[channel].tx++;
-                       ipoctal->count_wr[channel]++;
-                       (*pointer_write)++;
-                       *pointer_write = *pointer_write % PAGE_SIZE;
-                       ipoctal->nb_bytes[channel]--;
-
-                       if ((ipoctal->nb_bytes[channel] == 0) &&
-                           (waitqueue_active(&ipoctal->queue[channel]))) {
-
-                               if (ipoctal->board_id != IPACK1_DEVICE_ID_SBS_OCTAL_485) {
-                                       ipoctal->write = 1;
-                                       wake_up_interruptible(&ipoctal->queue[channel]);
-                               }
-                       }
-               }
+       /* If there is no client, skip the check */
+       if (!atomic_read(&channel->open))
+               return;
 
-               tty_flip_buffer_push(tty);
-               tty_kref_put(tty);
+       tty = tty_port_tty_get(&channel->tty_port);
+       if (!tty)
+               return;
+       /* The HW is organized in pair of channels.  See which register we need
+        * to read from */
+       isr = ioread8(&channel->block_regs->r.isr);
+       sr = ioread8(&channel->regs->r.sr);
+
+       /* In case of RS-485, change from TX to RX when finishing TX.
+        * Half-duplex. */
+       if ((channel->board_id == IPACK1_DEVICE_ID_SBS_OCTAL_485) &&
+           (sr & SR_TX_EMPTY) && (channel->nb_bytes == 0)) {
+               iowrite8(CR_DISABLE_TX, &channel->regs->w.cr);
+               iowrite8(CR_CMD_NEGATE_RTSN, &channel->regs->w.cr);
+               iowrite8(CR_ENABLE_RX, &channel->regs->w.cr);
+               *channel->board_write = 1;
+               wake_up_interruptible(&channel->queue);
        }
+
+       /* RX data */
+       if ((isr & channel->isr_rx_rdy_mask) && (sr & SR_RX_READY))
+               ipoctal_irq_rx(channel, tty, sr);
+
+       /* TX of each character */
+       if ((isr & channel->isr_tx_rdy_mask) && (sr & SR_TX_READY))
+               ipoctal_irq_tx(channel);
+
+       tty_flip_buffer_push(tty);
+       tty_kref_put(tty);
+}
+
+static irqreturn_t ipoctal_irq_handler(void *arg)
+{
+       unsigned int i;
+       struct ipoctal *ipoctal = (struct ipoctal *) arg;
+
+       /* Check all channels */
+       for (i = 0; i < NR_CHANNELS; i++)
+               ipoctal_irq_channel(&ipoctal->channel[i]);
+
        return IRQ_HANDLED;
 }
 
@@ -348,6 +312,9 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr,
        struct tty_driver *tty;
        char name[20];
        unsigned char board_id;
+       struct ipoctal_channel *channel;
+       union scc2698_channel __iomem *chan_regs;
+       union scc2698_block __iomem *block_regs;
 
        res = ipoctal->dev->bus->ops->map_space(ipoctal->dev, 0,
                                                IPACK_ID_SPACE);
@@ -385,44 +352,42 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr,
        }
 
        /* Save the virtual address to access the registers easily */
-       ipoctal->chan_regs =
+       chan_regs =
                (union scc2698_channel __iomem *) ipoctal->dev->io_space.address;
-       ipoctal->block_regs =
+       block_regs =
                (union scc2698_block __iomem *) ipoctal->dev->io_space.address;
 
        /* Disable RX and TX before touching anything */
        for (i = 0; i < NR_CHANNELS ; i++) {
-               ipoctal_write_io_reg(ipoctal, &ipoctal->chan_regs[i].w.cr,
-                                    CR_DISABLE_RX | CR_DISABLE_TX);
-               ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[i].w.cr,
-                                    CR_CMD_RESET_RX);
-               ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[i].w.cr,
-                                    CR_CMD_RESET_TX);
-               ipoctal_write_io_reg(ipoctal,
-                                    &ipoctal->chan_regs[i].w.mr,
-                                    MR1_CHRL_8_BITS | MR1_ERROR_CHAR |
-                                    MR1_RxINT_RxRDY); /* mr1 */
-               ipoctal_write_io_reg(ipoctal,
-                                    &ipoctal->chan_regs[i].w.mr,
-                                    0); /* mr2 */
-               ipoctal_write_io_reg(ipoctal,
-                                    &ipoctal->chan_regs[i].w.csr,
-                                    TX_CLK_9600  | RX_CLK_9600);
+               struct ipoctal_channel *channel = &ipoctal->channel[i];
+               channel->regs = chan_regs + i;
+               channel->block_regs = block_regs + (i >> 1);
+               channel->board_write = &ipoctal->write;
+               channel->board_id = ipoctal->board_id;
+               if (i & 1) {
+                       channel->isr_tx_rdy_mask = ISR_TxRDY_B;
+                       channel->isr_rx_rdy_mask = ISR_RxRDY_FFULL_B;
+               } else {
+                       channel->isr_tx_rdy_mask = ISR_TxRDY_A;
+                       channel->isr_rx_rdy_mask = ISR_RxRDY_FFULL_A;
+               }
+
+               iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr);
+               iowrite8(CR_CMD_RESET_RX, &channel->regs->w.cr);
+               iowrite8(CR_CMD_RESET_TX, &channel->regs->w.cr);
+               iowrite8(MR1_CHRL_8_BITS | MR1_ERROR_CHAR | MR1_RxINT_RxRDY,
+                        &channel->regs->w.mr); /* mr1 */
+               iowrite8(0, &channel->regs->w.mr); /* mr2 */
+               iowrite8(TX_CLK_9600  | RX_CLK_9600, &channel->regs->w.csr);
        }
 
        for (i = 0; i < IP_OCTAL_NB_BLOCKS; i++) {
-               ipoctal_write_io_reg(ipoctal,
-                                    &ipoctal->block_regs[i].w.acr,
-                                    ACR_BRG_SET2);
-               ipoctal_write_io_reg(ipoctal,
-                                    &ipoctal->block_regs[i].w.opcr,
-                                    OPCR_MPP_OUTPUT | OPCR_MPOa_RTSN |
-                                    OPCR_MPOb_RTSN);
-               ipoctal_write_io_reg(ipoctal,
-                                    &ipoctal->block_regs[i].w.imr,
-                                    IMR_TxRDY_A | IMR_RxRDY_FFULL_A |
-                                    IMR_DELTA_BREAK_A | IMR_TxRDY_B |
-                                    IMR_RxRDY_FFULL_B | IMR_DELTA_BREAK_B);
+               iowrite8(ACR_BRG_SET2, &block_regs[i].w.acr);
+               iowrite8(OPCR_MPP_OUTPUT | OPCR_MPOa_RTSN | OPCR_MPOb_RTSN,
+                        &block_regs[i].w.opcr);
+               iowrite8(IMR_TxRDY_A | IMR_RxRDY_FFULL_A | IMR_DELTA_BREAK_A |
+                        IMR_TxRDY_B | IMR_RxRDY_FFULL_B | IMR_DELTA_BREAK_B,
+                        &block_regs[i].w.imr);
        }
 
        /*
@@ -472,26 +437,26 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr,
        ipoctal->tty_drv = tty;
 
        for (i = 0; i < NR_CHANNELS; i++) {
-               tty_port_init(&ipoctal->tty_port[i]);
-               tty_port_alloc_xmit_buf(&ipoctal->tty_port[i]);
-               ipoctal->tty_port[i].ops = &ipoctal_tty_port_ops;
-
-               ipoctal_reset_stats(&ipoctal->chan_stats[i]);
-               ipoctal->nb_bytes[i] = 0;
-               init_waitqueue_head(&ipoctal->queue[i]);
-
-               spin_lock_init(&ipoctal->lock[i]);
-               ipoctal->pointer_read[i] = 0;
-               ipoctal->pointer_write[i] = 0;
-               ipoctal->nb_bytes[i] = 0;
+               channel = &ipoctal->channel[i];
+               tty_port_init(&channel->tty_port);
+               tty_port_alloc_xmit_buf(&channel->tty_port);
+               channel->tty_port.ops = &ipoctal_tty_port_ops;
+
+               ipoctal_reset_stats(&channel->stats);
+               channel->nb_bytes = 0;
+               init_waitqueue_head(&channel->queue);
+
+               spin_lock_init(&channel->lock);
+               channel->pointer_read = 0;
+               channel->pointer_write = 0;
+               channel->nb_bytes = 0;
                tty_register_device(tty, i, NULL);
 
                /*
                 * Enable again the RX. TX will be enabled when
                 * there is something to send
                 */
-               ipoctal_write_io_reg(ipoctal, &ipoctal->chan_regs[i].w.cr,
-                                    CR_ENABLE_RX);
+               iowrite8(CR_ENABLE_RX, &channel->regs->w.cr);
        }
 
        return 0;
@@ -505,23 +470,22 @@ out_unregister_id_space:
        return res;
 }
 
-static inline int ipoctal_copy_write_buffer(struct ipoctal *ipoctal,
-                                           unsigned int channel,
+static inline int ipoctal_copy_write_buffer(struct ipoctal_channel *channel,
                                            const unsigned char *buf,
                                            int count)
 {
        unsigned long flags;
        int i;
-       unsigned int *pointer_read = &ipoctal->pointer_read[channel];
+       unsigned int *pointer_read = &channel->pointer_read;
 
        /* Copy the bytes from the user buffer to the internal one */
        for (i = 0; i < count; i++) {
-               if (i <= (PAGE_SIZE - ipoctal->nb_bytes[channel])) {
-                       spin_lock_irqsave(&ipoctal->lock[channel], flags);
-                       ipoctal->tty_port[channel].xmit_buf[*pointer_read] = buf[i];
+               if (i <= (PAGE_SIZE - channel->nb_bytes)) {
+                       spin_lock_irqsave(&channel->lock, flags);
+                       channel->tty_port.xmit_buf[*pointer_read] = buf[i];
                        *pointer_read = (*pointer_read + 1) % PAGE_SIZE;
-                       ipoctal->nb_bytes[channel]++;
-                       spin_unlock_irqrestore(&ipoctal->lock[channel], flags);
+                       channel->nb_bytes++;
+                       spin_unlock_irqrestore(&channel->lock, flags);
                } else {
                        break;
                }
@@ -529,63 +493,46 @@ static inline int ipoctal_copy_write_buffer(struct ipoctal *ipoctal,
        return i;
 }
 
-static int ipoctal_write(struct ipoctal *ipoctal, unsigned int channel,
-                        const unsigned char *buf, int count)
+static int ipoctal_write_tty(struct tty_struct *tty,
+                            const unsigned char *buf, int count)
 {
-       ipoctal->nb_bytes[channel] = 0;
-       ipoctal->count_wr[channel] = 0;
+       struct ipoctal_channel *channel = tty->driver_data;
 
-       ipoctal_copy_write_buffer(ipoctal, channel, buf, count);
+       channel->nb_bytes = 0;
+       channel->count_wr = 0;
+
+       ipoctal_copy_write_buffer(channel, buf, count);
 
        /* As the IP-OCTAL 485 only supports half duplex, do it manually */
-       if (ipoctal->board_id == IPACK1_DEVICE_ID_SBS_OCTAL_485) {
-               ipoctal_write_io_reg(ipoctal,
-                                    &ipoctal->chan_regs[channel].w.cr,
-                                    CR_DISABLE_RX);
-               ipoctal_write_cr_cmd(ipoctal,
-                                    &ipoctal->chan_regs[channel].w.cr,
-                                    CR_CMD_ASSERT_RTSN);
+       if (channel->board_id == IPACK1_DEVICE_ID_SBS_OCTAL_485) {
+               iowrite8(CR_DISABLE_RX, &channel->regs->w.cr);
+               iowrite8(CR_CMD_ASSERT_RTSN, &channel->regs->w.cr);
        }
 
        /*
         * Send a packet and then disable TX to avoid failure after several send
         * operations
         */
-       ipoctal_write_io_reg(ipoctal,
-                            &ipoctal->chan_regs[channel].w.cr,
-                            CR_ENABLE_TX);
-       wait_event_interruptible(ipoctal->queue[channel], ipoctal->write);
-       ipoctal_write_io_reg(ipoctal,
-                            &ipoctal->chan_regs[channel].w.cr,
-                            CR_DISABLE_TX);
-
-       ipoctal->write = 0;
-       return ipoctal->count_wr[channel];
-}
-
-static int ipoctal_write_tty(struct tty_struct *tty,
-                            const unsigned char *buf, int count)
-{
-       unsigned int channel = tty->index;
-       struct ipoctal *ipoctal = tty->driver_data;
+       iowrite8(CR_ENABLE_TX, &channel->regs->w.cr);
+       wait_event_interruptible(channel->queue, *channel->board_write);
+       iowrite8(CR_DISABLE_TX, &channel->regs->w.cr);
 
-       return ipoctal_write(ipoctal, channel, buf, count);
+       *channel->board_write = 0;
+       return channel->count_wr;
 }
 
 static int ipoctal_write_room(struct tty_struct *tty)
 {
-       int channel = tty->index;
-       struct ipoctal *ipoctal = tty->driver_data;
+       struct ipoctal_channel *channel = tty->driver_data;
 
-       return PAGE_SIZE - ipoctal->nb_bytes[channel];
+       return PAGE_SIZE - channel->nb_bytes;
 }
 
 static int ipoctal_chars_in_buffer(struct tty_struct *tty)
 {
-       int channel = tty->index;
-       struct ipoctal *ipoctal = tty->driver_data;
+       struct ipoctal_channel *channel = tty->driver_data;
 
-       return ipoctal->nb_bytes[channel];
+       return channel->nb_bytes;
 }
 
 static void ipoctal_set_termios(struct tty_struct *tty,
@@ -595,23 +542,17 @@ static void ipoctal_set_termios(struct tty_struct *tty,
        unsigned char mr1 = 0;
        unsigned char mr2 = 0;
        unsigned char csr = 0;
-       unsigned int channel = tty->index;
-       struct ipoctal *ipoctal = tty->driver_data;
+       struct ipoctal_channel *channel = tty->driver_data;
        speed_t baud;
 
        cflag = tty->termios->c_cflag;
 
        /* Disable and reset everything before change the setup */
-       ipoctal_write_io_reg(ipoctal, &ipoctal->chan_regs[channel].w.cr,
-                            CR_DISABLE_RX | CR_DISABLE_TX);
-       ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[channel].w.cr,
-                            CR_CMD_RESET_RX);
-       ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[channel].w.cr,
-                            CR_CMD_RESET_TX);
-       ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[channel].w.cr,
-                            CR_CMD_RESET_ERR_STATUS);
-       ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[channel].w.cr,
-                            CR_CMD_RESET_MR);
+       iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr);
+       iowrite8(CR_CMD_RESET_RX, &channel->regs->w.cr);
+       iowrite8(CR_CMD_RESET_TX, &channel->regs->w.cr);
+       iowrite8(CR_CMD_RESET_ERR_STATUS, &channel->regs->w.cr);
+       iowrite8(CR_CMD_RESET_MR, &channel->regs->w.cr);
 
        /* Set Bits per chars */
        switch (cflag & CSIZE) {
@@ -648,7 +589,7 @@ static void ipoctal_set_termios(struct tty_struct *tty,
                mr2 |= MR2_STOP_BITS_LENGTH_1;
 
        /* Set the flow control */
-       switch (ipoctal->board_id) {
+       switch (channel->board_id) {
        case IPACK1_DEVICE_ID_SBS_OCTAL_232:
                if (cflag & CRTSCTS) {
                        mr1 |= MR1_RxRTS_CONTROL_ON;
@@ -724,45 +665,38 @@ static void ipoctal_set_termios(struct tty_struct *tty,
        mr1 |= MR1_RxINT_RxRDY;
 
        /* Write the control registers */
-       ipoctal_write_io_reg(ipoctal, &ipoctal->chan_regs[channel].w.mr, mr1);
-       ipoctal_write_io_reg(ipoctal, &ipoctal->chan_regs[channel].w.mr, mr2);
-       ipoctal_write_io_reg(ipoctal, &ipoctal->chan_regs[channel].w.csr, csr);
+       iowrite8(mr1, &channel->regs->w.mr);
+       iowrite8(mr2, &channel->regs->w.mr);
+       iowrite8(csr, &channel->regs->w.csr);
 
        /* Enable again the RX */
-       ipoctal_write_io_reg(ipoctal, &ipoctal->chan_regs[channel].w.cr,
-                            CR_ENABLE_RX);
+       iowrite8(CR_ENABLE_RX, &channel->regs->w.cr);
 }
 
 static void ipoctal_hangup(struct tty_struct *tty)
 {
        unsigned long flags;
-       int channel = tty->index;
-       struct ipoctal *ipoctal = tty->driver_data;
+       struct ipoctal_channel *channel = tty->driver_data;
 
-       if (ipoctal == NULL)
+       if (channel == NULL)
                return;
 
-       spin_lock_irqsave(&ipoctal->lock[channel], flags);
-       ipoctal->nb_bytes[channel] = 0;
-       ipoctal->pointer_read[channel] = 0;
-       ipoctal->pointer_write[channel] = 0;
-       spin_unlock_irqrestore(&ipoctal->lock[channel], flags);
-
-       tty_port_hangup(&ipoctal->tty_port[channel]);
-
-       ipoctal_write_io_reg(ipoctal, &ipoctal->chan_regs[channel].w.cr,
-                            CR_DISABLE_RX | CR_DISABLE_TX);
-       ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[channel].w.cr,
-                            CR_CMD_RESET_RX);
-       ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[channel].w.cr,
-                            CR_CMD_RESET_TX);
-       ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[channel].w.cr,
-                            CR_CMD_RESET_ERR_STATUS);
-       ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[channel].w.cr,
-                            CR_CMD_RESET_MR);
-
-       clear_bit(ASYNCB_INITIALIZED, &ipoctal->tty_port[channel].flags);
-       wake_up_interruptible(&ipoctal->tty_port[channel].open_wait);
+       spin_lock_irqsave(&channel->lock, flags);
+       channel->nb_bytes = 0;
+       channel->pointer_read = 0;
+       channel->pointer_write = 0;
+       spin_unlock_irqrestore(&channel->lock, flags);
+
+       tty_port_hangup(&channel->tty_port);
+
+       iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr);
+       iowrite8(CR_CMD_RESET_RX, &channel->regs->w.cr);
+       iowrite8(CR_CMD_RESET_TX, &channel->regs->w.cr);
+       iowrite8(CR_CMD_RESET_ERR_STATUS, &channel->regs->w.cr);
+       iowrite8(CR_CMD_RESET_MR, &channel->regs->w.cr);
+
+       clear_bit(ASYNCB_INITIALIZED, &channel->tty_port.flags);
+       wake_up_interruptible(&channel->tty_port.open_wait);
 }
 
 static const struct tty_operations ipoctal_fops = {
@@ -777,27 +711,6 @@ static const struct tty_operations ipoctal_fops = {
        .hangup =               ipoctal_hangup,
 };
 
-static int ipoctal_match(struct ipack_device *dev)
-{
-       int res;
-       unsigned char board_id;
-
-       if ((!dev->bus->ops) || (!dev->bus->ops->map_space) ||
-           (!dev->bus->ops->unmap_space))
-               return 0;
-
-       res = dev->bus->ops->map_space(dev, 0, IPACK_ID_SPACE);
-       if (res)
-               return 0;
-
-       res = ipoctal_check_model(dev, &board_id);
-       dev->bus->ops->unmap_space(dev, IPACK_ID_SPACE);
-       if (!res)
-               return 1;
-
-       return 0;
-}
-
 static int ipoctal_probe(struct ipack_device *dev)
 {
        int res;
@@ -824,9 +737,12 @@ static void __ipoctal_remove(struct ipoctal *ipoctal)
 {
        int i;
 
+       ipoctal->dev->bus->ops->free_irq(ipoctal->dev);
+
        for (i = 0; i < NR_CHANNELS; i++) {
+               struct ipoctal_channel *channel = &ipoctal->channel[i];
                tty_unregister_device(ipoctal->tty_drv, i);
-               tty_port_free_xmit_buf(&ipoctal->tty_port[i]);
+               tty_port_free_xmit_buf(&channel->tty_port);
        }
 
        tty_unregister_driver(ipoctal->tty_drv);
@@ -845,14 +761,26 @@ static void ipoctal_remove(struct ipack_device *device)
        }
 }
 
+static DEFINE_IPACK_DEVICE_TABLE(ipoctal_ids) = {
+       { IPACK_DEVICE(IPACK_ID_VERSION_1, IPACK1_VENDOR_ID_SBS,
+                       IPACK1_DEVICE_ID_SBS_OCTAL_232) },
+       { IPACK_DEVICE(IPACK_ID_VERSION_1, IPACK1_VENDOR_ID_SBS,
+                       IPACK1_DEVICE_ID_SBS_OCTAL_422) },
+       { IPACK_DEVICE(IPACK_ID_VERSION_1, IPACK1_VENDOR_ID_SBS,
+                       IPACK1_DEVICE_ID_SBS_OCTAL_485) },
+       { 0, },
+};
+
+MODULE_DEVICE_TABLE(ipack, ipoctal_ids);
+
 static const struct ipack_driver_ops ipoctal_drv_ops = {
-       .match = ipoctal_match,
-       .probe = ipoctal_probe,
+       .probe  = ipoctal_probe,
        .remove = ipoctal_remove,
 };
 
 static struct ipack_driver driver = {
        .ops      = &ipoctal_drv_ops,
+       .id_table = ipoctal_ids,
 };
 
 static int __init ipoctal_init(void)
@@ -862,11 +790,6 @@ static int __init ipoctal_init(void)
 
 static void __exit ipoctal_exit(void)
 {
-       struct ipoctal *p, *next;
-
-       list_for_each_entry_safe(p, next, &ipoctal_list, list)
-               p->dev->bus->ops->remove_device(p->dev);
-
        ipack_driver_unregister(&driver);
 }