Merge branch 'upstream' of git://ftp.linux-mips.org/pub/scm/upstream-linus
[powerpc.git] / drivers / char / isicom.c
index 51fa317..43ab9ed 100644 (file)
@@ -172,16 +172,18 @@ static struct pci_driver isicom_driver = {
 static int prev_card = 3;      /*      start servicing isi_card[0]     */
 static struct tty_driver *isicom_normal;
 
-static struct timer_list tx;
+static DECLARE_COMPLETION(isi_timerdone);
 static char re_schedule = 1;
 
 static void isicom_tx(unsigned long _data);
 static void isicom_start(struct tty_struct *tty);
 
+static DEFINE_TIMER(tx, isicom_tx, 0, 0);
+
 /*   baud index mappings from linux defns to isi */
 
 static signed char linuxb_to_isib[] = {
-       -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 16, 17, 18, 19
+       -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 15, 16, 17, 18, 19, 20, 21
 };
 
 struct isi_board {
@@ -195,6 +197,7 @@ struct      isi_board {
        signed char             count;
        spinlock_t              card_lock; /* Card wide lock 11/5/00 -sameer */
        unsigned long           flags;
+       unsigned int            index;
 };
 
 struct isi_port {
@@ -210,8 +213,6 @@ struct      isi_port {
        struct tty_struct       * tty;
        wait_queue_head_t       close_wait;
        wait_queue_head_t       open_wait;
-       struct work_struct      hangup_tq;
-       struct work_struct      bh_tqueue;
        unsigned char           * xmit_buf;
        int                     xmit_head;
        int                     xmit_tail;
@@ -227,6 +228,20 @@ static struct isi_port  isi_ports[PORT_COUNT];
  *     it wants to talk.
  */
 
+static inline int WaitTillCardIsFree(u16 base)
+{
+       unsigned int count = 0;
+       unsigned int a = in_atomic(); /* do we run under spinlock? */
+
+       while (!(inw(base + 0xe) & 0x1) && count++ < 100)
+               if (a)
+                       mdelay(1);
+               else
+                       msleep(1);
+
+       return !(inw(base + 0xe) & 0x1);
+}
+
 static int lock_card(struct isi_board *card)
 {
        char            retries;
@@ -273,69 +288,71 @@ static void unlock_card(struct isi_board *card)
  *  ISI Card specific ops ...
  */
 
+/* card->lock HAS to be held */
 static void raise_dtr(struct isi_port *port)
 {
        struct isi_board *card = port->card;
        unsigned long base = card->base;
        u16 channel = port->channel;
 
-       if (!lock_card(card))
+       if (WaitTillCardIsFree(base))
                return;
 
        outw(0x8000 | (channel << card->shift_count) | 0x02, base);
        outw(0x0504, base);
        InterruptTheCard(base);
        port->status |= ISI_DTR;
-       unlock_card(card);
 }
 
+/* card->lock HAS to be held */
 static inline void drop_dtr(struct isi_port *port)
 {
        struct isi_board *card = port->card;
        unsigned long base = card->base;
        u16 channel = port->channel;
 
-       if (!lock_card(card))
+       if (WaitTillCardIsFree(base))
                return;
 
        outw(0x8000 | (channel << card->shift_count) | 0x02, base);
        outw(0x0404, base);
        InterruptTheCard(base);
        port->status &= ~ISI_DTR;
-       unlock_card(card);
 }
 
+/* card->lock HAS to be held */
 static inline void raise_rts(struct isi_port *port)
 {
        struct isi_board *card = port->card;
        unsigned long base = card->base;
        u16 channel = port->channel;
 
-       if (!lock_card(card))
+       if (WaitTillCardIsFree(base))
                return;
 
        outw(0x8000 | (channel << card->shift_count) | 0x02, base);
        outw(0x0a04, base);
        InterruptTheCard(base);
        port->status |= ISI_RTS;
-       unlock_card(card);
 }
+
+/* card->lock HAS to be held */
 static inline void drop_rts(struct isi_port *port)
 {
        struct isi_board *card = port->card;
        unsigned long base = card->base;
        u16 channel = port->channel;
 
-       if (!lock_card(card))
+       if (WaitTillCardIsFree(base))
                return;
 
        outw(0x8000 | (channel << card->shift_count) | 0x02, base);
        outw(0x0804, base);
        InterruptTheCard(base);
        port->status &= ~ISI_RTS;
-       unlock_card(card);
 }
 
+/* card->lock MUST NOT be held */
 static inline void raise_dtr_rts(struct isi_port *port)
 {
        struct isi_board *card = port->card;
@@ -352,35 +369,20 @@ static inline void raise_dtr_rts(struct isi_port *port)
        unlock_card(card);
 }
 
+/* card->lock HAS to be held */
 static void drop_dtr_rts(struct isi_port *port)
 {
        struct isi_board *card = port->card;
        unsigned long base = card->base;
        u16 channel = port->channel;
 
-       if (!lock_card(card))
+       if (WaitTillCardIsFree(base))
                return;
 
        outw(0x8000 | (channel << card->shift_count) | 0x02, base);
        outw(0x0c04, base);
        InterruptTheCard(base);
        port->status &= ~(ISI_RTS | ISI_DTR);
-       unlock_card(card);
-}
-
-static inline void kill_queue(struct isi_port *port, short queue)
-{
-       struct isi_board *card = port->card;
-       unsigned long base = card->base;
-       u16 channel = port->channel;
-
-       if (!lock_card(card))
-               return;
-
-       outw(0x8000 | (channel << card->shift_count) | 0x02, base);
-       outw((queue << 8) | 0x06, base);
-       InterruptTheCard(base);
-       unlock_card(card);
 }
 
 /*
@@ -506,39 +508,18 @@ static void isicom_tx(unsigned long _data)
                if (port->xmit_cnt <= 0)
                        port->status &= ~ISI_TXOK;
                if (port->xmit_cnt <= WAKEUP_CHARS)
-                       schedule_work(&port->bh_tqueue);
+                       tty_wakeup(tty);
                unlock_card(&isi_card[card]);
        }
 
        /*      schedule another tx for hopefully in about 10ms */
 sched_again:
        if (!re_schedule) {
-               re_schedule = 2;
+               complete(&isi_timerdone);
                return;
        }
 
-       init_timer(&tx);
-       tx.expires = jiffies + HZ/100;
-       tx.data = 0;
-       tx.function = isicom_tx;
-       add_timer(&tx);
-
-       return;
-}
-
-/*     Interrupt handlers      */
-
-
-static void isicom_bottomhalf(struct work_struct *work)
-{
-       struct isi_port *port = container_of(work, struct isi_port, bh_tqueue);
-       struct tty_struct *tty = port->tty;
-
-       if (!tty)
-               return;
-
-       tty_wakeup(tty);
-       wake_up_interruptible(&tty->write_wait);
+       mod_timer(&tx, jiffies + msecs_to_jiffies(10));
 }
 
 /*
@@ -559,6 +540,11 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id)
                return IRQ_NONE;
 
        base = card->base;
+
+       /* did the card interrupt us? */
+       if (!(inw(base + 0x0e) & 0x02))
+               return IRQ_NONE;
+
        spin_lock(&card->card_lock);
 
        /*
@@ -583,6 +569,7 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id)
        port = card->ports + channel;
        if (!(port->flags & ASYNC_INITIALIZED)) {
                outw(0x0000, base+0x04); /* enable interrupts */
+               spin_unlock(&card->card_lock);
                return IRQ_HANDLED;
        }
 
@@ -611,7 +598,7 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id)
                                                pr_dbg("interrupt: DCD->low.\n"
                                                        );
                                                port->status &= ~ISI_DCD;
-                                               schedule_work(&port->hangup_tq);
+                                               tty_hangup(tty);
                                        }
                                } else if (header & ISI_DCD) {
                                /* Carrier has been detected */
@@ -633,7 +620,7 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id)
                                                /* start tx ing */
                                                port->status |= (ISI_TXOK
                                                        | ISI_CTS);
-                                               schedule_work(&port->bh_tqueue);
+                                               tty_wakeup(tty);
                                        }
                                } else if (!(header & ISI_CTS)) {
                                        port->tty->hw_stopped = 1;
@@ -697,6 +684,7 @@ static irqreturn_t isicom_interrupt(int irq, void *dev_id)
                tty_flip_buffer_push(tty);
        }
        outw(0x0000, base+0x04); /* enable interrupts */
+       spin_unlock(&card->card_lock);
 
        return IRQ_HANDLED;
 }
@@ -722,7 +710,8 @@ static void isicom_config_port(struct isi_port *port)
                 *  respectively.
                 */
 
-               if (baud < 1 || baud > 2)
+               /* 1,2,3,4 => 57.6, 115.2, 230, 460 kbps resp. */
+               if (baud < 1 || baud > 4)
                        port->tty->termios->c_cflag &= ~CBAUDEX;
                else
                        baud += 15;
@@ -738,6 +727,10 @@ static void isicom_config_port(struct isi_port *port)
                        baud++; /*  57.6 Kbps */
                if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
                        baud +=2; /*  115  Kbps */
+               if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+                       baud += 3; /* 230 kbps*/
+               if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+                       baud += 4; /* 460 kbps*/
        }
        if (linuxb_to_isib[baud] == -1) {
                /* hang up */
@@ -747,7 +740,7 @@ static void isicom_config_port(struct isi_port *port)
        else
                raise_dtr(port);
 
-       if (lock_card(card)) {
+       if (WaitTillCardIsFree(base) == 0) {
                outw(0x8000 | (channel << shift_count) |0x03, base);
                outw(linuxb_to_isib[baud] << 8 | 0x03, base);
                channel_setup = 0;
@@ -775,7 +768,6 @@ static void isicom_config_port(struct isi_port *port)
                }
                outw(channel_setup, base);
                InterruptTheCard(base);
-               unlock_card(card);
        }
        if (C_CLOCAL(tty))
                port->flags &= ~ASYNC_CHECK_CD;
@@ -794,12 +786,11 @@ static void isicom_config_port(struct isi_port *port)
        if (I_IXOFF(tty))
                flow_ctrl |= ISICOM_INITIATE_XONXOFF;
 
-       if (lock_card(card)) {
+       if (WaitTillCardIsFree(base) == 0) {
                outw(0x8000 | (channel << shift_count) |0x04, base);
                outw(flow_ctrl << 8 | 0x05, base);
                outw((STOP_CHAR(tty)) << 8 | (START_CHAR(tty)), base);
                InterruptTheCard(base);
-               unlock_card(card);
        }
 
        /*      rx enabled -> enable port for rx on the card    */
@@ -824,10 +815,9 @@ static inline void isicom_setup_board(struct isi_board *bp)
        }
        port = bp->ports;
        bp->status |= BOARD_ACTIVE;
-       spin_unlock_irqrestore(&bp->card_lock, flags);
        for (channel = 0; channel < bp->port_count; channel++, port++)
                drop_dtr_rts(port);
-       return;
+       spin_unlock_irqrestore(&bp->card_lock, flags);
 }
 
 static int isicom_setup_port(struct isi_port *port)
@@ -860,7 +850,12 @@ static int isicom_setup_port(struct isi_port *port)
        port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
 
        /*      discard any residual data       */
-       kill_queue(port, ISICOM_KILLTX | ISICOM_KILLRX);
+       if (WaitTillCardIsFree(card->base) == 0) {
+               outw(0x8000 | (port->channel << card->shift_count) | 0x02,
+                               card->base);
+               outw(((ISICOM_KILLTX | ISICOM_KILLRX) << 8) | 0x06, card->base);
+               InterruptTheCard(card->base);
+       }
 
        isicom_config_port(port);
        port->flags |= ASYNC_INITIALIZED;
@@ -949,8 +944,8 @@ static int isicom_open(struct tty_struct *tty, struct file *filp)
 {
        struct isi_port *port;
        struct isi_board *card;
-       unsigned int line, board;
-       int error;
+       unsigned int board;
+       int error, line;
 
        line = tty->index;
        if (line < 0 || line > PORT_COUNT-1)
@@ -986,28 +981,22 @@ static int isicom_open(struct tty_struct *tty, struct file *filp)
 
 static inline void isicom_shutdown_board(struct isi_board *bp)
 {
-       unsigned long flags;
-
-       spin_lock_irqsave(&bp->card_lock, flags);
        if (bp->status & BOARD_ACTIVE) {
                bp->status &= ~BOARD_ACTIVE;
        }
-       spin_unlock_irqrestore(&bp->card_lock, flags);
 }
 
+/* card->lock HAS to be held */
 static void isicom_shutdown_port(struct isi_port *port)
 {
        struct isi_board *card = port->card;
        struct tty_struct *tty;
-       unsigned long flags;
 
        tty = port->tty;
 
-       spin_lock_irqsave(&card->card_lock, flags);
-       if (!(port->flags & ASYNC_INITIALIZED)) {
-               spin_unlock_irqrestore(&card->card_lock, flags);
+       if (!(port->flags & ASYNC_INITIALIZED))
                return;
-       }
+
        if (port->xmit_buf) {
                free_page((unsigned long) port->xmit_buf);
                port->xmit_buf = NULL;
@@ -1015,7 +1004,6 @@ static void isicom_shutdown_port(struct isi_port *port)
        port->flags &= ~ASYNC_INITIALIZED;
        /* 3rd October 2000 : Vinayak P Risbud */
        port->tty = NULL;
-       spin_unlock_irqrestore(&card->card_lock, flags);
 
        /*Fix done by Anil .S on 30-04-2001
        remote login through isi port has dtr toggle problem
@@ -1261,10 +1249,12 @@ static int isicom_tiocmset(struct tty_struct *tty, struct file *file,
        unsigned int set, unsigned int clear)
 {
        struct isi_port *port = tty->driver_data;
+       unsigned long flags;
 
        if (isicom_paranoia_check(port, tty->name, "isicom_ioctl"))
                return -ENODEV;
 
+       spin_lock_irqsave(&port->card->card_lock, flags);
        if (set & TIOCM_RTS)
                raise_rts(port);
        if (set & TIOCM_DTR)
@@ -1274,6 +1264,7 @@ static int isicom_tiocmset(struct tty_struct *tty, struct file *file,
                drop_rts(port);
        if (clear & TIOCM_DTR)
                drop_dtr(port);
+       spin_unlock_irqrestore(&port->card->card_lock, flags);
 
        return 0;
 }
@@ -1306,7 +1297,10 @@ static int isicom_set_serial_info(struct isi_port *port,
                                (newinfo.flags & ASYNC_FLAGS));
        }
        if (reconfig_port) {
+               unsigned long flags;
+               spin_lock_irqsave(&port->card->card_lock, flags);
                isicom_config_port(port);
+               spin_unlock_irqrestore(&port->card->card_lock, flags);
        }
        return 0;
 }
@@ -1387,6 +1381,7 @@ static void isicom_set_termios(struct tty_struct *tty,
        struct ktermios *old_termios)
 {
        struct isi_port *port = tty->driver_data;
+       unsigned long flags;
 
        if (isicom_paranoia_check(port, tty->name, "isicom_set_termios"))
                return;
@@ -1395,7 +1390,9 @@ static void isicom_set_termios(struct tty_struct *tty,
                        tty->termios->c_iflag == old_termios->c_iflag)
                return;
 
+       spin_lock_irqsave(&port->card->card_lock, flags);
        isicom_config_port(port);
+       spin_unlock_irqrestore(&port->card->card_lock, flags);
 
        if ((old_termios->c_cflag & CRTSCTS) &&
                        !(tty->termios->c_cflag & CRTSCTS)) {
@@ -1458,25 +1455,18 @@ static void isicom_start(struct tty_struct *tty)
        port->status |= ISI_TXOK;
 }
 
-/* hangup et all */
-static void do_isicom_hangup(struct work_struct *work)
-{
-       struct isi_port *port = container_of(work, struct isi_port, hangup_tq);
-       struct tty_struct *tty;
-
-       tty = port->tty;
-       if (tty)
-               tty_hangup(tty);
-}
-
 static void isicom_hangup(struct tty_struct *tty)
 {
        struct isi_port *port = tty->driver_data;
+       unsigned long flags;
 
        if (isicom_paranoia_check(port, tty->name, "isicom_hangup"))
                return;
 
+       spin_lock_irqsave(&port->card->card_lock, flags);
        isicom_shutdown_port(port);
+       spin_unlock_irqrestore(&port->card->card_lock, flags);
+
        port->count = 0;
        port->flags &= ~ASYNC_NORMAL_ACTIVE;
        port->tty = NULL;
@@ -1497,7 +1487,6 @@ static void isicom_flush_buffer(struct tty_struct *tty)
        port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
        spin_unlock_irqrestore(&card->card_lock, flags);
 
-       wake_up_interruptible(&tty->write_wait);
        tty_wakeup(tty);
 }
 
@@ -1530,7 +1519,7 @@ static int __devinit reset_card(struct pci_dev *pdev,
 {
        struct isi_board *board = pci_get_drvdata(pdev);
        unsigned long base = board->base;
-       unsigned int portcount = 0;
+       unsigned int sig, portcount = 0;
        int retval = 0;
 
        dev_dbg(&pdev->dev, "ISILoad:Resetting Card%d at 0x%lx\n", card + 1,
@@ -1538,27 +1527,35 @@ static int __devinit reset_card(struct pci_dev *pdev,
 
        inw(base + 0x8);
 
-       mdelay(10);
+       msleep(10);
 
        outw(0, base + 0x8); /* Reset */
 
-       msleep(3000);
+       msleep(1000);
 
-       *signature = inw(base + 0x4) & 0xff;
+       sig = inw(base + 0x4) & 0xff;
+
+       if (sig != 0xa5 && sig != 0xbb && sig != 0xcc && sig != 0xdd &&
+                       sig != 0xee) {
+               dev_warn(&pdev->dev, "ISILoad:Card%u reset failure (Possible "
+                       "bad I/O Port Address 0x%lx).\n", card + 1, base);
+               dev_dbg(&pdev->dev, "Sig=0x%x\n", sig);
+               retval = -EIO;
+               goto end;
+       }
+
+       msleep(10);
 
        portcount = inw(base + 0x2);
-       if (!(inw(base + 0xe) & 0x1) || ((portcount != 0) &&
-                       (portcount != 4) && (portcount != 8))) {
-               dev_dbg(&pdev->dev, "base+0x2=0x%lx, base+0xe=0x%lx\n",
-                       inw(base + 0x2), inw(base + 0xe));
-               dev_err(&pdev->dev, "ISILoad:PCI Card%d reset failure "
-                       "(Possible bad I/O Port Address 0x%lx).\n",
-                       card + 1, base);
+       if (!inw(base + 0xe) & 0x1 || (portcount != 0 && portcount != 4 &&
+                               portcount != 8 && portcount != 16)) {
+               dev_err(&pdev->dev, "ISILoad:PCI Card%d reset failure.",
+                       card + 1);
                retval = -EIO;
                goto end;
        }
 
-       switch (*signature) {
+       switch (sig) {
        case 0xa5:
        case 0xbb:
        case 0xdd:
@@ -1566,31 +1563,18 @@ static int __devinit reset_card(struct pci_dev *pdev,
                board->shift_count = 12;
                break;
        case 0xcc:
+       case 0xee:
                board->port_count = 16;
                board->shift_count = 11;
                break;
-       default:
-               dev_warn(&pdev->dev, "ISILoad:Card%d reset failure (Possible "
-                       "bad I/O Port Address 0x%lx).\n", card + 1, base);
-               dev_dbg(&pdev->dev, "Sig=0x%lx\n", signature);
-               retval = -EIO;
        }
        dev_info(&pdev->dev, "-Done\n");
+       *signature = sig;
 
 end:
        return retval;
 }
 
-static inline int WaitTillCardIsFree(u16 base)
-{
-       unsigned long count = 0;
-
-       while (!(inw(base + 0xe) & 0x1) && count++ < 100)
-               msleep(5);
-
-       return !(inw(base + 0xe) & 0x1);
-}
-
 static int __devinit load_firmware(struct pci_dev *pdev,
        const unsigned int index, const unsigned int signature)
 {
@@ -1708,6 +1692,11 @@ static int __devinit load_firmware(struct pci_dev *pdev,
                }
 
                data = kmalloc(word_count * 2, GFP_KERNEL);
+               if (data == NULL) {
+                       dev_err(&pdev->dev, "Card%d, firmware upload "
+                               "failed, not enough memory\n", index + 1);
+                       goto errrelfw;
+               }
                inw(base);
                insw(base, data, word_count);
                InterruptTheCard(base);
@@ -1756,7 +1745,7 @@ end:
 /*
  *     Insmod can set static symbols so keep these static
  */
-static int card;
+static unsigned int card_count;
 
 static int __devinit isicom_probe(struct pci_dev *pdev,
        const struct pci_device_id *ent)
@@ -1766,7 +1755,7 @@ static int __devinit isicom_probe(struct pci_dev *pdev,
        u8 pciirq;
        struct isi_board *board = NULL;
 
-       if (card >= BOARD_COUNT)
+       if (card_count >= BOARD_COUNT)
                goto err;
 
        ioaddr = pci_resource_start(pdev, 3);
@@ -1781,18 +1770,20 @@ static int __devinit isicom_probe(struct pci_dev *pdev,
                        break;
                }
 
+       board->index = index;
        board->base = ioaddr;
        board->irq = pciirq;
-       card++;
+       card_count++;
 
        pci_set_drvdata(pdev, board);
 
-       if (!request_region(board->base, 16, ISICOM_NAME)) {
+       retval = pci_request_region(pdev, 3, ISICOM_NAME);
+       if (retval) {
                dev_err(&pdev->dev, "I/O Region 0x%lx-0x%lx is busy. Card%d "
                        "will be disabled.\n", board->base, board->base + 15,
                        index + 1);
                retval = -EBUSY;
-               goto err;
+               goto errdec;
        }
 
        retval = request_irq(board->irq, isicom_interrupt,
@@ -1811,23 +1802,35 @@ static int __devinit isicom_probe(struct pci_dev *pdev,
        if (retval < 0)
                goto errunri;
 
+       for (index = 0; index < board->port_count; index++)
+               tty_register_device(isicom_normal, board->index * 16 + index,
+                               &pdev->dev);
+
        return 0;
 
 errunri:
        free_irq(board->irq, board);
 errunrr:
-       release_region(board->base, 16);
-err:
+       pci_release_region(pdev, 3);
+errdec:
        board->base = 0;
+       card_count--;
+err:
        return retval;
 }
 
 static void __devexit isicom_remove(struct pci_dev *pdev)
 {
        struct isi_board *board = pci_get_drvdata(pdev);
+       unsigned int i;
+
+       for (i = 0; i < board->port_count; i++)
+               tty_unregister_device(isicom_normal, board->index * 16 + i);
 
        free_irq(board->irq, board);
-       release_region(board->base, 16);
+       pci_release_region(pdev, 3);
+       board->base = 0;
+       card_count--;
 }
 
 static int __init isicom_init(void)
@@ -1835,9 +1838,6 @@ static int __init isicom_init(void)
        int retval, idx, channel;
        struct isi_port *port;
 
-       card = 0;
-       memset(isi_ports, 0, sizeof(isi_ports));
-
        for(idx = 0; idx < BOARD_COUNT; idx++) {
                port = &isi_ports[idx * 16];
                isi_card[idx].ports = port;
@@ -1848,8 +1848,6 @@ static int __init isicom_init(void)
                        port->channel = channel;
                        port->close_delay = 50 * HZ/100;
                        port->closing_wait = 3000 * HZ/100;
-                       INIT_WORK(&port->hangup_tq, do_isicom_hangup);
-                       INIT_WORK(&port->bh_tqueue, isicom_bottomhalf);
                        port->status = 0;
                        init_waitqueue_head(&port->open_wait);
                        init_waitqueue_head(&port->close_wait);
@@ -1875,7 +1873,8 @@ static int __init isicom_init(void)
        isicom_normal->init_termios             = tty_std_termios;
        isicom_normal->init_termios.c_cflag     = B9600 | CS8 | CREAD | HUPCL |
                CLOCAL;
-       isicom_normal->flags                    = TTY_DRIVER_REAL_RAW;
+       isicom_normal->flags                    = TTY_DRIVER_REAL_RAW |
+               TTY_DRIVER_DYNAMIC_DEV;
        tty_set_operations(isicom_normal, &isicom_ops);
 
        retval = tty_register_driver(isicom_normal);
@@ -1890,12 +1889,7 @@ static int __init isicom_init(void)
                goto err_unrtty;
        }
 
-       init_timer(&tx);
-       tx.expires = jiffies + 1;
-       tx.data = 0;
-       tx.function = isicom_tx;
-       re_schedule = 1;
-       add_timer(&tx);
+       mod_timer(&tx, jiffies + 1);
 
        return 0;
 err_unrtty:
@@ -1908,12 +1902,9 @@ error:
 
 static void __exit isicom_exit(void)
 {
-       unsigned int index = 0;
-
        re_schedule = 0;
 
-       while (re_schedule != 2 && index++ < 100)
-               msleep(10);
+       wait_for_completion_timeout(&isi_timerdone, HZ);
 
        pci_unregister_driver(&isicom_driver);
        tty_unregister_driver(isicom_normal);