x86: PAT use reserve free memtype in mmap of /dev/mem
[safe/jmp/linux-2.6] / drivers / char / isicom.c
index c837ade..eba2883 100644 (file)
 #define InterruptTheCard(base) outw(0, (base) + 0xc)
 #define ClearInterrupt(base) inw((base) + 0x0a)
 
+#define pr_dbg(str...) pr_debug("ISICOM: " str)
 #ifdef DEBUG
-#define pr_dbg(str...) printk(KERN_DEBUG "ISICOM: " str)
 #define isicom_paranoia_check(a, b, c) __isicom_paranoia_check((a), (b), (c))
 #else
-#define pr_dbg(str...) do { } while (0)
 #define isicom_paranoia_check(a, b, c) 0
 #endif
 
@@ -172,9 +171,6 @@ static struct pci_driver isicom_driver = {
 static int prev_card = 3;      /*      start servicing isi_card[0]     */
 static struct tty_driver *isicom_normal;
 
-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);
 
@@ -183,12 +179,12 @@ 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 {
        unsigned long           base;
-       unsigned char           irq;
+       int                     irq;
        unsigned char           port_count;
        unsigned short          status;
        unsigned short          port_status; /* each bit for each port */
@@ -228,7 +224,7 @@ static struct isi_port  isi_ports[PORT_COUNT];
  *     it wants to talk.
  */
 
-static inline int WaitTillCardIsFree(u16 base)
+static inline int WaitTillCardIsFree(unsigned long base)
 {
        unsigned int count = 0;
        unsigned int a = in_atomic(); /* do we run under spinlock? */
@@ -244,17 +240,18 @@ static inline int WaitTillCardIsFree(u16 base)
 
 static int lock_card(struct isi_board *card)
 {
-       char            retries;
        unsigned long base = card->base;
+       unsigned int retries, a;
 
-       for (retries = 0; retries < 100; retries++) {
+       for (retries = 0; retries < 10; retries++) {
                spin_lock_irqsave(&card->card_lock, card->flags);
-               if (inw(base + 0xe) & 0x1) {
-                       return 1;
-               } else {
-                       spin_unlock_irqrestore(&card->card_lock, card->flags);
-                       udelay(1000);   /* 1ms */
+               for (a = 0; a < 10; a++) {
+                       if (inw(base + 0xe) & 0x1)
+                               return 1;
+                       udelay(10);
                }
+               spin_unlock_irqrestore(&card->card_lock, card->flags);
+               msleep(10);
        }
        printk(KERN_WARNING "ISICOM: Failed to lock Card (0x%lx)\n",
                card->base);
@@ -262,23 +259,6 @@ static int lock_card(struct isi_board *card)
        return 0;       /* Failed to acquire the card! */
 }
 
-static int lock_card_at_interrupt(struct isi_board *card)
-{
-       unsigned char           retries;
-       unsigned long base = card->base;
-
-       for (retries = 0; retries < 200; retries++) {
-               spin_lock_irqsave(&card->card_lock, card->flags);
-
-               if (inw(base + 0xe) & 0x1)
-                       return 1;
-               else
-                       spin_unlock_irqrestore(&card->card_lock, card->flags);
-       }
-       /* Failing in interrupt is an acceptable event */
-       return 0;       /* Failed to acquire the card! */
-}
-
 static void unlock_card(struct isi_board *card)
 {
        spin_unlock_irqrestore(&card->card_lock, card->flags);
@@ -416,7 +396,9 @@ static inline int __isicom_paranoia_check(struct isi_port const *port,
 
 static void isicom_tx(unsigned long _data)
 {
-       short count = (BOARD_COUNT-1), card, base;
+       unsigned long flags, base;
+       unsigned int retries;
+       short count = (BOARD_COUNT-1), card;
        short txcount, wrd, residue, word_count, cnt;
        struct isi_port *port;
        struct tty_struct *tty;
@@ -436,32 +418,34 @@ static void isicom_tx(unsigned long _data)
        count = isi_card[card].port_count;
        port = isi_card[card].ports;
        base = isi_card[card].base;
+
+       spin_lock_irqsave(&isi_card[card].card_lock, flags);
+       for (retries = 0; retries < 100; retries++) {
+               if (inw(base + 0xe) & 0x1)
+                       break;
+               udelay(2);
+       }
+       if (retries >= 100)
+               goto unlock;
+
        for (;count > 0;count--, port++) {
-               if (!lock_card_at_interrupt(&isi_card[card]))
-                       continue;
                /* port not active or tx disabled to force flow control */
                if (!(port->flags & ASYNC_INITIALIZED) ||
                                !(port->status & ISI_TXOK))
-                       unlock_card(&isi_card[card]);
                        continue;
 
                tty = port->tty;
 
-
-               if (tty == NULL) {
-                       unlock_card(&isi_card[card]);
+               if (tty == NULL)
                        continue;
-               }
 
                txcount = min_t(short, TX_SIZE, port->xmit_cnt);
-               if (txcount <= 0 || tty->stopped || tty->hw_stopped) {
-                       unlock_card(&isi_card[card]);
+               if (txcount <= 0 || tty->stopped || tty->hw_stopped)
                        continue;
-               }
-               if (!(inw(base + 0x02) & (1 << port->channel))) {
-                       unlock_card(&isi_card[card]);
+
+               if (!(inw(base + 0x02) & (1 << port->channel)))
                        continue;
-               }
+
                pr_dbg("txing %d bytes, port%d.\n", txcount,
                        port->channel + 1);
                outw((port->channel << isi_card[card].shift_count) | txcount,
@@ -509,16 +493,12 @@ static void isicom_tx(unsigned long _data)
                        port->status &= ~ISI_TXOK;
                if (port->xmit_cnt <= WAKEUP_CHARS)
                        tty_wakeup(tty);
-               unlock_card(&isi_card[card]);
        }
 
+unlock:
+       spin_unlock_irqrestore(&isi_card[card].card_lock, flags);
        /*      schedule another tx for hopefully in about 10ms */
 sched_again:
-       if (!re_schedule) {
-               complete(&isi_timerdone);
-               return;
-       }
-
        mod_timer(&tx, jiffies + msecs_to_jiffies(10));
 }
 
@@ -540,6 +520,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);
 
        /*
@@ -705,7 +690,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;
@@ -721,6 +707,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 */
@@ -1477,7 +1467,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);
 }
 
@@ -1510,7 +1499,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,
@@ -1518,27 +1507,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);
+
+       sig = inw(base + 0x4) & 0xff;
 
-       *signature = 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.\n",
+                       card + 1);
                retval = -EIO;
                goto end;
        }
 
-       switch (*signature) {
+       switch (sig) {
        case 0xa5:
        case 0xbb:
        case 0xdd:
@@ -1546,16 +1543,13 @@ 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;
@@ -1628,7 +1622,9 @@ static int __devinit load_firmware(struct pci_dev *pdev,
 
                if ((status = inw(base + 0x4)) != 0) {
                        dev_warn(&pdev->dev, "Card%d rejected load header:\n"
-                               "Address:0x%x\nCount:0x%x\nStatus:0x%x\n",
+                               KERN_WARNING "Address:0x%x\n"
+                               KERN_WARNING "Count:0x%x\n"
+                               KERN_WARNING "Status:0x%x\n",
                                index + 1, frame->addr, frame->count, status);
                        goto errrelfw;
                }
@@ -1672,7 +1668,9 @@ static int __devinit load_firmware(struct pci_dev *pdev,
 
                if ((status = inw(base + 0x4)) != 0) {
                        dev_warn(&pdev->dev, "Card%d rejected verify header:\n"
-                               "Address:0x%x\nCount:0x%x\nStatus: 0x%x\n",
+                               KERN_WARNING "Address:0x%x\n"
+                               KERN_WARNING "Count:0x%x\n"
+                               KERN_WARNING "Status: 0x%x\n",
                                index + 1, frame->addr, frame->count, status);
                        goto errrelfw;
                }
@@ -1731,22 +1729,18 @@ 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)
 {
-       unsigned int ioaddr, signature, index;
+       unsigned int signature, index;
        int retval = -EPERM;
-       u8 pciirq;
        struct isi_board *board = NULL;
 
-       if (card >= BOARD_COUNT)
+       if (card_count >= BOARD_COUNT)
                goto err;
 
-       ioaddr = pci_resource_start(pdev, 3);
-       /* i.e at offset 0x1c in the PCI configuration register space. */
-       pciirq = pdev->irq;
        dev_info(&pdev->dev, "ISI PCI Card(Device ID 0x%x)\n", ent->device);
 
        /* allot the first empty slot in the array */
@@ -1757,9 +1751,9 @@ static int __devinit isicom_probe(struct pci_dev *pdev,
                }
 
        board->index = index;
-       board->base = ioaddr;
-       board->irq = pciirq;
-       card++;
+       board->base = pci_resource_start(pdev, 3);
+       board->irq = pdev->irq;
+       card_count++;
 
        pci_set_drvdata(pdev, board);
 
@@ -1769,7 +1763,7 @@ static int __devinit isicom_probe(struct pci_dev *pdev,
                        "will be disabled.\n", board->base, board->base + 15,
                        index + 1);
                retval = -EBUSY;
-               goto err;
+               goto errdec;
        }
 
        retval = request_irq(board->irq, isicom_interrupt,
@@ -1798,8 +1792,10 @@ errunri:
        free_irq(board->irq, board);
 errunrr:
        pci_release_region(pdev, 3);
-err:
+errdec:
        board->base = 0;
+       card_count--;
+err:
        return retval;
 }
 
@@ -1813,6 +1809,8 @@ static void __devexit isicom_remove(struct pci_dev *pdev)
 
        free_irq(board->irq, board);
        pci_release_region(pdev, 3);
+       board->base = 0;
+       card_count--;
 }
 
 static int __init isicom_init(void)
@@ -1820,8 +1818,6 @@ static int __init isicom_init(void)
        int retval, idx, channel;
        struct isi_port *port;
 
-       card = 0;
-
        for(idx = 0; idx < BOARD_COUNT; idx++) {
                port = &isi_ports[idx * 16];
                isi_card[idx].ports = port;
@@ -1886,9 +1882,7 @@ error:
 
 static void __exit isicom_exit(void)
 {
-       re_schedule = 0;
-
-       wait_for_completion_timeout(&isi_timerdone, HZ);
+       del_timer_sync(&tx);
 
        pci_unregister_driver(&isicom_driver);
        tty_unregister_driver(isicom_normal);