]> err.no Git - linux-2.6/blobdiff - drivers/char/cyclades.c
Merge branch 'for-2.6.22' of git://git.kernel.dk/data/git/linux-2.6-block
[linux-2.6] / drivers / char / cyclades.c
index 6e5fca9c393a8b59041d9d04f8f115571f570b5b..c72ee97d3892543f6e8d2ebd053657776bba49dc 100644 (file)
  *
  * Initially written by Randolph Bentson <bentson@grieg.seaslug.org>.
  * Modified and maintained by Marcio Saito <marcio@cyclades.com>.
- * Currently maintained by Cyclades team <async@cyclades.com>.
  *
- * For Technical support and installation problems, please send e-mail
- * to support@cyclades.com.
+ * Copyright (C) 2007 Jiri Slaby <jirislaby@gmail.com>
  *
  * Much of the design and some of the code came from serial.c
  * which was copyright (C) 1991, 1992  Linus Torvalds.  It was
  * extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92,
  * and then fixed as suggested by Michael K. Johnson 12/12/92.
+ * Converted to pci probing and cleaned up by Jiri Slaby.
  *
  * This version supports shared IRQ's (only for PCI boards).
  *
  *
  */
 
-#define CY_VERSION     "2.4"
+#define CY_VERSION     "2.5"
 
 /* If you need to install more boards than NR_CARDS, change the constant
    in the definition below. No other change is necessary to support up to
@@ -722,11 +721,6 @@ module_param_array(irq, int, NULL, 0);
 */
 static struct cyclades_card cy_card[NR_CARDS];
 
-/* This is the per-channel data structure containing pointers, flags
- and variables for the port. This driver supports a maximum of NR_PORTS.
-*/
-static struct cyclades_port cy_port[NR_PORTS];
-
 static int cy_next_channel;    /* next minor available */
 
 /*
@@ -855,13 +849,6 @@ static inline int serial_paranoia_check(struct cyclades_port *info,
                return 1;
        }
 
-       if ((long)info < (long)(&cy_port[0]) ||
-                       (long)(&cy_port[NR_PORTS]) < (long)info) {
-               printk(KERN_WARNING "cyc Warning: cyclades_port out of range "
-                               "for (%s) in %s\n", name, routine);
-               return 1;
-       }
-
        if (info->magic != CYCLADES_MAGIC) {
                printk(KERN_WARNING "cyc Warning: bad magic number for serial "
                                "struct (%s) in %s\n", name, routine);
@@ -1025,7 +1012,7 @@ static void cyy_intr_chip(struct cyclades_card *cinfo, int chip,
        struct cyclades_port *info;
        struct tty_struct *tty;
        int char_count;
-       int i, j, len, mdm_change, mdm_status, outch;
+       int j, len, mdm_change, mdm_status, outch;
        int save_xir, channel, save_car;
        char data;
 
@@ -1037,13 +1024,12 @@ static void cyy_intr_chip(struct cyclades_card *cinfo, int chip,
                spin_lock(&cinfo->card_lock);
                save_xir = (u_char) readb(base_addr + (CyRIR << index));
                channel = (u_short) (save_xir & CyIRChannel);
-               i = channel + chip * 4 + cinfo->first_line;
-               info = &cy_port[i];
+               info = &cinfo->ports[channel + chip * 4];
                save_car = readb(base_addr + (CyCAR << index));
                cy_writeb(base_addr + (CyCAR << index), save_xir);
 
                /* if there is nowhere to put the data, discard it */
-               if (info->tty == 0) {
+               if (info->tty == NULL) {
                        j = (readb(base_addr + (CyRIVR << index)) &
                                CyIVRMask);
                        if (j == CyIVRRxEx) {   /* exception */
@@ -1202,19 +1188,18 @@ static void cyy_intr_chip(struct cyclades_card *cinfo, int chip,
                spin_lock(&cinfo->card_lock);
                save_xir = (u_char) readb(base_addr + (CyTIR << index));
                channel = (u_short) (save_xir & CyIRChannel);
-               i = channel + chip * 4 + cinfo->first_line;
                save_car = readb(base_addr + (CyCAR << index));
                cy_writeb(base_addr + (CyCAR << index), save_xir);
 
                /* validate the port# (as configured and open) */
-               if ((i < 0) || (NR_PORTS <= i)) {
+               if (channel + chip * 4 >= cinfo->nports) {
                        cy_writeb(base_addr + (CySRER << index),
                                  readb(base_addr + (CySRER << index)) &
                                  ~CyTxRdy);
                        goto txend;
                }
-               info = &cy_port[i];
-               if (info->tty == 0) {
+               info = &cinfo->ports[channel + chip * 4];
+               if (info->tty == NULL) {
                        cy_writeb(base_addr + (CySRER << index),
                                  readb(base_addr + (CySRER << index)) &
                                  ~CyTxRdy);
@@ -1263,7 +1248,7 @@ static void cyy_intr_chip(struct cyclades_card *cinfo, int chip,
                                }
                                goto txdone;
                        }
-                       if (info->xmit_buf == 0) {
+                       if (info->xmit_buf == NULL) {
                                cy_writeb(base_addr + (CySRER << index),
                                        readb(base_addr + (CySRER << index)) &
                                        ~CyTxRdy);
@@ -1304,7 +1289,6 @@ static void cyy_intr_chip(struct cyclades_card *cinfo, int chip,
                                                0);
                                        info->icount.tx++;
                                        char_count--;
-                               } else {
                                }
                        }
                }
@@ -1326,16 +1310,14 @@ txend:
                spin_lock(&cinfo->card_lock);
                save_xir = (u_char) readb(base_addr + (CyMIR << index));
                channel = (u_short) (save_xir & CyIRChannel);
-               info = &cy_port[channel + chip * 4 + cinfo->first_line];
+               info = &cinfo->ports[channel + chip * 4];
                save_car = readb(base_addr + (CyCAR << index));
                cy_writeb(base_addr + (CyCAR << index), save_xir);
 
                mdm_change = readb(base_addr + (CyMISR << index));
                mdm_status = readb(base_addr + (CyMSVR1 << index));
 
-               if (info->tty == 0) {   /* no place for data, ignore it */
-                       ;
-               } else {
+               if (info->tty) {
                        if (mdm_change & CyANY_DELTA) {
                                /* For statistics only */
                                if (mdm_change & CyDCD)
@@ -1389,10 +1371,10 @@ txend:
                                        }
                                }
                        }
-                       if (mdm_change & CyDSR) {
+/*                     if (mdm_change & CyDSR) {
                        }
                        if (mdm_change & CyRI) {
-                       }
+                       }*/
                }
                /* end of service */
                cy_writeb(base_addr + (CyMIR << index), (save_xir & 0x3f));
@@ -1408,14 +1390,14 @@ txend:
 static irqreturn_t cyy_interrupt(int irq, void *dev_id)
 {
        int status;
-       struct cyclades_card *cinfo;
+       struct cyclades_card *cinfo = dev_id;
        void __iomem *base_addr, *card_base_addr;
        int chip;
        int index;
        int too_many;
        int had_work;
 
-       if ((cinfo = (struct cyclades_card *)dev_id) == 0) {
+       if (unlikely(cinfo == NULL)) {
 #ifdef CY_DEBUG_INTERRUPTS
                printk(KERN_DEBUG "cyy_interrupt: spurious interrupt %d\n",irq);
 #endif
@@ -1564,7 +1546,7 @@ cyz_handle_rx(struct cyclades_port *info, struct CH_CTRL __iomem *ch_ctrl,
                        info->mon.char_max = char_count;
                info->mon.char_last = char_count;
 #endif
-               if (tty == 0) {
+               if (tty == NULL) {
                        /* flush received characters */
                        new_rx_get = (new_rx_get + char_count) &
                                        (rx_bufsize - 1);
@@ -1650,9 +1632,8 @@ cyz_handle_tx(struct cyclades_port *info, struct CH_CTRL __iomem *ch_ctrl,
 
        if (char_count) {
 
-               if (tty == 0) {
+               if (tty == NULL)
                        goto ztxdone;
-               }
 
                if (info->x_char) {     /* send special char */
                        data = info->x_char;
@@ -1730,10 +1711,10 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo)
        while (cyz_fetch_msg(cinfo, &channel, &cmd, &param) == 1) {
                special_count = 0;
                delta_count = 0;
-               info = &cy_port[channel + cinfo->first_line];
-               if ((tty = info->tty) == 0) {
+               info = &cinfo->ports[channel];
+               if ((tty = info->tty) == NULL)
                        continue;
-               }
+
                ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]);
                buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]);
 
@@ -1822,16 +1803,16 @@ static void cyz_handle_cmd(struct cyclades_card *cinfo)
 #ifdef CONFIG_CYZ_INTR
 static irqreturn_t cyz_interrupt(int irq, void *dev_id)
 {
-       struct cyclades_card *cinfo;
+       struct cyclades_card *cinfo = dev_id;
 
-       if ((cinfo = (struct cyclades_card *)dev_id) == 0) {
+       if (unlikely(cinfo == NULL)) {
 #ifdef CY_DEBUG_INTERRUPTS
                printk(KERN_DEBUG "cyz_interrupt: spurious interrupt %d\n",irq);
 #endif
                return IRQ_NONE;        /* spurious interrupt */
        }
 
-       if (!ISZLOADED(*cinfo)) {
+       if (unlikely(!ISZLOADED(*cinfo))) {
 #ifdef CY_DEBUG_INTERRUPTS
                printk(KERN_DEBUG "cyz_interrupt: board not yet loaded "
                                "(IRQ%d).\n", irq);
@@ -1900,7 +1881,7 @@ static void cyz_poll(unsigned long arg)
                cyz_handle_cmd(cinfo);
 
                for (port = 0; port < cinfo->nports; port++) {
-                       info = &cy_port[port + cinfo->first_line];
+                       info = &cinfo->ports[port];
                        tty = info->tty;
                        ch_ctrl = &(zfw_ctrl->ch_ctrl[port]);
                        buf_ctrl = &(zfw_ctrl->buf_ctrl[port]);
@@ -2472,13 +2453,20 @@ block_til_ready(struct tty_struct *tty, struct file *filp,
 static int cy_open(struct tty_struct *tty, struct file *filp)
 {
        struct cyclades_port *info;
+       unsigned int i;
        int retval, line;
 
        line = tty->index;
        if ((line < 0) || (NR_PORTS <= line)) {
                return -ENODEV;
        }
-       info = &cy_port[line];
+       for (i = 0; i < NR_CARDS; i++)
+               if (line < cy_card[i].first_line + cy_card[i].nports &&
+                               line >= cy_card[i].first_line)
+                       break;
+       if (i >= NR_CARDS)
+               return -ENODEV;
+       info = &cy_card[i].ports[line - cy_card[i].first_line];
        if (info->line < 0) {
                return -ENODEV;
        }
@@ -2661,8 +2649,6 @@ static void cy_wait_until_sent(struct tty_struct *tty, int timeout)
                                        timeout))
                                break;
                }
-       } else {
-               /* Nothing to do! */
        }
        /* Run one more char cycle */
        msleep_interruptible(jiffies_to_msecs(char_time * 5));
@@ -3857,8 +3843,6 @@ static int set_threshold(struct cyclades_port *info, unsigned long value)
                cy_writeb(base_addr + (CyCOR3 << index), info->cor3);
                cyy_issue_cmd(base_addr, CyCOR_CHANGE | CyCOR3ch, index);
                spin_unlock_irqrestore(&card->card_lock, flags);
-       } else {
-               /* Nothing to do! */
        }
        return 0;
 }                              /* set_threshold */
@@ -3881,10 +3865,8 @@ get_threshold(struct cyclades_port *info, unsigned long __user * value)
 
                tmp = readb(base_addr + (CyCOR3 << index)) & CyREC_FIFO;
                return put_user(tmp, value);
-       } else {
-               /* Nothing to do! */
-               return 0;
        }
+       return 0;
 }                              /* get_threshold */
 
 static int
@@ -3918,8 +3900,6 @@ static int set_timeout(struct cyclades_port *info, unsigned long value)
                spin_lock_irqsave(&card->card_lock, flags);
                cy_writeb(base_addr + (CyRTPR << index), value & 0xff);
                spin_unlock_irqrestore(&card->card_lock, flags);
-       } else {
-               /* Nothing to do! */
        }
        return 0;
 }                              /* set_timeout */
@@ -3941,10 +3921,8 @@ static int get_timeout(struct cyclades_port *info, unsigned long __user * value)
 
                tmp = readb(base_addr + (CyRTPR << index));
                return put_user(tmp, value);
-       } else {
-               /* Nothing to do! */
-               return 0;
        }
+       return 0;
 }                              /* get_timeout */
 
 static int set_default_timeout(struct cyclades_port *info, unsigned long value)
@@ -4348,8 +4326,6 @@ static void cy_stop(struct tty_struct *tty)
                cy_writeb(base_addr + (CySRER << index),
                          readb(base_addr + (CySRER << index)) & ~CyTxRdy);
                spin_unlock_irqrestore(&cinfo->card_lock, flags);
-       } else {
-               /* Nothing to do! */
        }
 }                              /* cy_stop */
 
@@ -4381,8 +4357,6 @@ static void cy_start(struct tty_struct *tty)
                cy_writeb(base_addr + (CySRER << index),
                          readb(base_addr + (CySRER << index)) | CyTxRdy);
                spin_unlock_irqrestore(&cinfo->card_lock, flags);
-       } else {
-               /* Nothing to do! */
        }
 }                              /* cy_start */
 
@@ -4455,7 +4429,7 @@ static void cy_hangup(struct tty_struct *tty)
  * ---------------------------------------------------------------------
  */
 
-static void __devinit cy_init_card(struct cyclades_card *cinfo)
+static int __devinit cy_init_card(struct cyclades_card *cinfo)
 {
        struct cyclades_port *info;
        u32 mailbox;
@@ -4477,10 +4451,16 @@ static void __devinit cy_init_card(struct cyclades_card *cinfo)
                nports = cinfo->nports = CyPORTS_PER_CHIP * cinfo->num_chips;
        }
 
+       cinfo->ports = kzalloc(sizeof(*cinfo->ports) * nports, GFP_KERNEL);
+       if (cinfo->ports == NULL) {
+               printk(KERN_ERR "Cyclades: cannot allocate ports\n");
+               cinfo->nports = 0;
+               return -ENOMEM;
+       }
+
        for (port = cinfo->first_line; port < cinfo->first_line + nports;
                        port++) {
-               info = &cy_port[port];
-               memset(info, 0, sizeof(*info));
+               info = &cinfo->ports[port - cinfo->first_line];
                info->magic = CYCLADES_MAGIC;
                info->card = cinfo;
                info->line = port;
@@ -4543,6 +4523,7 @@ static void __devinit cy_init_card(struct cyclades_card *cinfo)
 #endif
        }
 #endif
+       return 0;
 }
 
 /* initialize chips on Cyclom-Y card -- return number of valid
@@ -4666,9 +4647,15 @@ static int __init cy_detect_isa(void)
 
                /* probe for CD1400... */
                cy_isa_address = ioremap(isa_address, CyISA_Ywin);
+               if (cy_isa_address == NULL) {
+                       printk(KERN_ERR "Cyclom-Y/ISA: can't remap base "
+                                       "address\n");
+                       continue;
+               }
                cy_isa_nchan = CyPORTS_PER_CHIP *
                        cyy_init_card(cy_isa_address, 0);
                if (cy_isa_nchan == 0) {
+                       iounmap(cy_isa_address);
                        continue;
                }
 #ifdef MODULE
@@ -4682,6 +4669,7 @@ static int __init cy_detect_isa(void)
                        printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but the "
                                "IRQ could not be detected.\n",
                                (unsigned long)cy_isa_address);
+                       iounmap(cy_isa_address);
                        continue;
                }
 
@@ -4690,11 +4678,12 @@ static int __init cy_detect_isa(void)
                                "more channels are available. Change NR_PORTS "
                                "in cyclades.c and recompile kernel.\n",
                                (unsigned long)cy_isa_address);
+                       iounmap(cy_isa_address);
                        return nboard;
                }
                /* fill the next cy_card structure available */
                for (j = 0; j < NR_CARDS; j++) {
-                       if (cy_card[j].base_addr == 0)
+                       if (cy_card[j].base_addr == NULL)
                                break;
                }
                if (j == NR_CARDS) {    /* no more cy_cards available */
@@ -4702,6 +4691,7 @@ static int __init cy_detect_isa(void)
                                "more cards can be used. Change NR_CARDS in "
                                "cyclades.c and recompile kernel.\n",
                                (unsigned long)cy_isa_address);
+                       iounmap(cy_isa_address);
                        return nboard;
                }
 
@@ -4711,6 +4701,7 @@ static int __init cy_detect_isa(void)
                        printk(KERN_ERR "Cyclom-Y/ISA found at 0x%lx, but "
                                "could not allocate IRQ#%d.\n",
                                (unsigned long)cy_isa_address, cy_isa_irq);
+                       iounmap(cy_isa_address);
                        return nboard;
                }
 
@@ -4721,7 +4712,12 @@ static int __init cy_detect_isa(void)
                cy_card[j].bus_index = 0;
                cy_card[j].first_line = cy_next_channel;
                cy_card[j].num_chips = cy_isa_nchan / 4;
-               cy_init_card(&cy_card[j]);
+               if (cy_init_card(&cy_card[j])) {
+                       cy_card[j].base_addr = NULL;
+                       free_irq(cy_isa_irq, &cy_card[j]);
+                       iounmap(cy_isa_address);
+                       continue;
+               }
                nboard++;
 
                printk(KERN_INFO "Cyclom-Y/ISA #%d: 0x%lx-0x%lx, IRQ%d found: "
@@ -4755,333 +4751,245 @@ static void __devinit plx_init(void __iomem * addr, __u32 initctl)
        cy_writel(addr + initctl, readl(addr + initctl) & ~0x20000000);
 }
 
-static int __devinit cy_init_Ze(struct RUNTIME_9060 __iomem *cy_pci_addr0,
-               int cy_pci_irq, struct pci_dev *pdev)
-{
-       void __iomem *cy_pci_addr2;
-       unsigned int j;
-       unsigned short cy_pci_nchan;
-
-       cy_pci_addr2 = pci_iomap(pdev, 2, CyPCI_Ze_win);
-
-       readl(&cy_pci_addr0->mail_box_0);
-       dev_dbg(&pdev->dev, "new Cyclades-Z board.  FPGA not loaded\n");
-
-       /* This must be the new Cyclades-Ze/PCI. */
-       cy_pci_nchan = ZE_V1_NPORTS;
-
-       if ((cy_next_channel + cy_pci_nchan) > NR_PORTS) {
-               dev_err(&pdev->dev, "Cyclades-Ze/PCI found, but no channels "
-                       "are available.\nChange NR_PORTS in cyclades.c "
-                       "and recompile kernel.\n");
-               return -EIO;
-       }
-
-       /* fill the next cy_card structure available */
-       for (j = 0; j < NR_CARDS; j++) {
-               if (cy_card[j].base_addr == 0)
-                       break;
-       }
-       if (j == NR_CARDS) {    /* no more cy_cards available */
-               dev_err(&pdev->dev, "Cyclades-Ze/PCI found, but no more "
-                       "cards can be used.\nChange NR_CARDS in "
-                       "cyclades.c and recompile kernel.\n");
-               return -EIO;
-       }
-#ifdef CONFIG_CYZ_INTR
-       /* allocate IRQ only if board has an IRQ */
-       if ((cy_pci_irq != 0) && (cy_pci_irq != 255)) {
-               if (request_irq(cy_pci_irq, cyz_interrupt,
-                               IRQF_SHARED, "Cyclades-Z",
-                               &cy_card[j])) {
-                       dev_err(&pdev->dev, "could not allocate IRQ.\n");
-                       return -EIO;
-               }
-       }
-#endif                         /* CONFIG_CYZ_INTR */
-
-       /* set cy_card */
-       cy_card[j].base_addr = cy_pci_addr2;
-       cy_card[j].ctl_addr = cy_pci_addr0;
-       cy_card[j].irq = cy_pci_irq;
-       cy_card[j].bus_index = 1;
-       cy_card[j].first_line = cy_next_channel;
-       cy_card[j].num_chips = -1;
-       cy_init_card(&cy_card[j]);
-       pci_set_drvdata(pdev, &cy_card[j]);
-
-       dev_info(&pdev->dev, "Cyclades-Ze/PCI #%d found: %d channels starting "
-               "from port %d.\n", j + 1, cy_pci_nchan, cy_next_channel);
-
-       for (j = cy_next_channel; j < cy_next_channel + cy_pci_nchan; j++)
-               tty_register_device(cy_serial_driver, j, &pdev->dev);
-       cy_next_channel += cy_pci_nchan;
-
-       return 0;
-}
-
 static int __devinit cy_pci_probe(struct pci_dev *pdev,
                const struct pci_device_id *ent)
 {
-       unsigned char cyy_rev_id;
-       int cy_pci_irq;
-       __u32 mailbox;
-       void __iomem *cy_pci_addr0, *cy_pci_addr2;
-       unsigned int device_id;
-       unsigned short j, cy_pci_nchan, plx_ver;
-       int retval;
+       void __iomem *addr0 = NULL, *addr2 = NULL;
+       char *card_name = NULL;
+       u32 mailbox;
+       unsigned int device_id, nchan = 0, card_no, i;
+       unsigned char plx_ver;
+       int retval, irq;
 
        retval = pci_enable_device(pdev);
        if (retval) {
                dev_err(&pdev->dev, "cannot enable device\n");
-               return retval;
+               goto err;
        }
 
        /* read PCI configuration area */
-       cy_pci_irq = pdev->irq;
-       pci_read_config_byte(pdev, PCI_REVISION_ID, &cyy_rev_id);
-
+       irq = pdev->irq;
        device_id = pdev->device & ~PCI_DEVICE_ID_MASK;
 
+#if defined(__alpha__)
+       if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) {   /* below 1M? */
+               dev_err(&pdev->dev, "Cyclom-Y/PCI not supported for low "
+                       "addresses on Alpha systems.\n");
+               retval = -EIO;
+               goto err_dis;
+       }
+#endif
+       if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Lo) {
+               dev_err(&pdev->dev, "Cyclades-Z/PCI not supported for low "
+                       "addresses\n");
+               retval = -EIO;
+               goto err_dis;
+       }
+
+       if (pci_resource_flags(pdev, 2) & IORESOURCE_IO) {
+               dev_warn(&pdev->dev, "PCI I/O bit incorrectly set. Ignoring "
+                               "it...\n");
+               pdev->resource[2].flags &= ~IORESOURCE_IO;
+       }
+
+       retval = pci_request_regions(pdev, "cyclades");
+       if (retval) {
+               dev_err(&pdev->dev, "failed to reserve resources\n");
+               goto err_dis;
+       }
+
+       retval = -EIO;
        if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo ||
                        device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) {
-               dev_dbg(&pdev->dev, "Cyclom-Y/PCI found\n");
+               card_name = "Cyclom-Y";
 
-               if (pci_resource_flags(pdev, 2) & IORESOURCE_IO) {
-                       dev_warn(&pdev->dev, "PCI I/O bit incorrectly "
-                               "set. Ignoring it...\n");
-                       pdev->resource[2].flags &= ~IORESOURCE_IO;
+               addr0 = pci_iomap(pdev, 0, CyPCI_Yctl);
+               if (addr0 == NULL) {
+                       dev_err(&pdev->dev, "can't remap ctl region\n");
+                       goto err_reg;
                }
-
-               /* Although we don't use this I/O region, we should
-                  request it from the kernel anyway, to avoid problems
-                  with other drivers accessing it. */
-               retval = pci_request_regions(pdev, "Cyclom-Y");
-               if (retval) {
-                       dev_err(&pdev->dev, "failed to reserve resources\n");
-                       return retval;
+               addr2 = pci_iomap(pdev, 2, CyPCI_Ywin);
+               if (addr2 == NULL) {
+                       dev_err(&pdev->dev, "can't remap base region\n");
+                       goto err_unmap;
                }
-#if defined(__alpha__)
-               if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo) {   /* below 1M? */
-                       dev_err(&pdev->dev, "Cyclom-Y/PCI not supported for "
-                               "low addresses on Alpha systems.\n");
-                       return -EIO;
-               }
-#endif
-               cy_pci_addr0 = pci_iomap(pdev, 0, CyPCI_Yctl);
-               cy_pci_addr2 = pci_iomap(pdev, 2, CyPCI_Ywin);
-
-               dev_dbg(&pdev->dev, "Cyclom-Y/PCI: relocate winaddr=0x%p "
-                       "ctladdr=0x%p\n", cy_pci_addr2, cy_pci_addr0);
 
-               cy_pci_nchan = (unsigned short)(CyPORTS_PER_CHIP *
-                               cyy_init_card(cy_pci_addr2, 1));
-               if (cy_pci_nchan == 0) {
+               nchan = CyPORTS_PER_CHIP * cyy_init_card(addr2, 1);
+               if (nchan == 0) {
                        dev_err(&pdev->dev, "Cyclom-Y PCI host card with no "
                                        "Serial-Modules\n");
                        return -EIO;
                }
-               if ((cy_next_channel + cy_pci_nchan) > NR_PORTS) {
-                       dev_err(&pdev->dev, "Cyclom-Y/PCI found, but no "
-                               "channels are available. Change NR_PORTS in "
-                               "cyclades.c and recompile kernel.\n");
-                       return -EIO;
-               }
-               /* fill the next cy_card structure available */
-               for (j = 0; j < NR_CARDS; j++) {
-                       if (cy_card[j].base_addr == 0)
-                               break;
-               }
-               if (j == NR_CARDS) {    /* no more cy_cards available */
-                       dev_err(&pdev->dev, "Cyclom-Y/PCI found, but no more "
-                               "cards can be used. Change NR_CARDS in "
-                               "cyclades.c and recompile kernel.\n");
-                       return -EIO;
-               }
-
-               /* allocate IRQ */
-               retval = request_irq(cy_pci_irq, cyy_interrupt,
-                               IRQF_SHARED, "Cyclom-Y", &cy_card[j]);
-               if (retval) {
-                       dev_err(&pdev->dev, "could not allocate IRQ\n");
-                       return retval;
-               }
-
-               /* set cy_card */
-               cy_card[j].base_addr = cy_pci_addr2;
-               cy_card[j].ctl_addr = cy_pci_addr0;
-               cy_card[j].irq = cy_pci_irq;
-               cy_card[j].bus_index = 1;
-               cy_card[j].first_line = cy_next_channel;
-               cy_card[j].num_chips = cy_pci_nchan / 4;
-               cy_init_card(&cy_card[j]);
-               pci_set_drvdata(pdev, &cy_card[j]);
-
-               /* enable interrupts in the PCI interface */
-               plx_ver = readb(cy_pci_addr2 + CyPLX_VER) & 0x0f;
-               switch (plx_ver) {
-               case PLX_9050:
-
-                       cy_writeb(cy_pci_addr0 + 0x4c, 0x43);
-                       break;
-
-               case PLX_9060:
-               case PLX_9080:
-               default:        /* Old boards, use PLX_9060 */
-
-                       plx_init(cy_pci_addr0, 0x6c);
-               /* For some yet unknown reason, once the PLX9060 reloads
-                  the EEPROM, the IRQ is lost and, thus, we have to
-                  re-write it to the PCI config. registers.
-                  This will remain here until we find a permanent
-                  fix. */
-                       pci_write_config_byte(pdev, PCI_INTERRUPT_LINE,
-                                       cy_pci_irq);
-
-                       cy_writew(cy_pci_addr0 + 0x68,
-                               readw(cy_pci_addr0 + 0x68) | 0x0900);
-                       break;
-               }
-
-               dev_info(&pdev->dev, "Cyclom-Y/PCI #%d found: %d channels "
-                       "starting from port %d.\n", j + 1, cy_pci_nchan,
-                       cy_next_channel);
-
-               for (j = cy_next_channel;
-                               j < cy_next_channel + cy_pci_nchan; j++)
-                       tty_register_device(cy_serial_driver, j, &pdev->dev);
-
-               cy_next_channel += cy_pci_nchan;
-       } else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Lo) {
-               dev_err(&pdev->dev, "Cyclades-Z/PCI not supported for "
-                       "low addresses\n");
-               return -EIO;
        } else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi) {
-               dev_dbg(&pdev->dev, "Cyclades-Z/PCI found\n");
+               struct RUNTIME_9060 __iomem *ctl_addr;
 
-               cy_pci_addr0 = pci_iomap(pdev, 0, CyPCI_Zctl);
+               ctl_addr = addr0 = pci_iomap(pdev, 0, CyPCI_Zctl);
+               if (addr0 == NULL) {
+                       dev_err(&pdev->dev, "can't remap ctl region\n");
+                       goto err_reg;
+               }
 
                /* Disable interrupts on the PLX before resetting it */
-               cy_writew(cy_pci_addr0 + 0x68,
-                       readw(cy_pci_addr0 + 0x68) & ~0x0900);
+               cy_writew(addr0 + 0x68,
+                       readw(addr0 + 0x68) & ~0x0900);
 
-               plx_init(cy_pci_addr0, 0x6c);
+               plx_init(addr0, 0x6c);
                /* For some yet unknown reason, once the PLX9060 reloads
                   the EEPROM, the IRQ is lost and, thus, we have to
                   re-write it to the PCI config. registers.
                   This will remain here until we find a permanent
                   fix. */
-               pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, cy_pci_irq);
+               pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, irq);
 
-               mailbox = (__u32)readl(&((struct RUNTIME_9060 __iomem *)
-                               cy_pci_addr0)->mail_box_0);
+               mailbox = (u32)readl(&ctl_addr->mail_box_0);
 
-               if (pci_resource_flags(pdev, 2) & IORESOURCE_IO) {
-                       dev_warn(&pdev->dev, "PCI I/O bit incorrectly "
-                               "set. Ignoring it...\n");
-                       pdev->resource[2].flags &= ~IORESOURCE_IO;
-               }
-
-               /* Although we don't use this I/O region, we should
-                  request it from the kernel anyway, to avoid problems
-                  with other drivers accessing it. */
-               retval = pci_request_regions(pdev, "Cyclades-Z");
-               if (retval) {
-                       dev_err(&pdev->dev, "failed to reserve resources\n");
-                       return retval;
+               addr2 = pci_iomap(pdev, 2, mailbox == ZE_V1 ?
+                               CyPCI_Ze_win : CyPCI_Zwin);
+               if (addr2 == NULL) {
+                       dev_err(&pdev->dev, "can't remap base region\n");
+                       goto err_unmap;
                }
 
                if (mailbox == ZE_V1) {
-                       retval = cy_init_Ze(cy_pci_addr0, cy_pci_irq, pdev);
-                       return retval;
+                       card_name = "Cyclades-Ze";
+
+                       readl(&ctl_addr->mail_box_0);
+                       nchan = ZE_V1_NPORTS;
                } else {
-                       cy_pci_addr2 = pci_iomap(pdev, 2, CyPCI_Zwin);
-               }
+                       card_name = "Cyclades-8Zo";
 
-               dev_dbg(&pdev->dev, "Cyclades-Z/PCI: relocate winaddr=0x%p "
-                       "ctladdr=0x%p\n", cy_pci_addr2, cy_pci_addr0);
 #ifdef CY_PCI_DEBUG
-               if (mailbox == ZO_V1) {
-                       cy_writel(&((struct RUNTIME_9060 *)
-                               (cy_pci_addr0))->loc_addr_base,
-                               WIN_CREG);
-                       dev_info(&pdev->dev, "Cyclades-8Zo/PCI: FPGA id %lx, "
-                               "ver %lx\n", (ulong)(0xff &
-                               readl(&((struct CUSTOM_REG *)
-                                       cy_pci_addr2)->fpga_id)),
-                               (ulong)(0xff & readl(&((struct CUSTOM_REG *)
-                                       cy_pci_addr2)->fpga_version)));
-                       cy_writel(&((struct RUNTIME_9060 *)
-                               cy_pci_addr0)->loc_addr_base, WIN_RAM);
-               } else {
-                       dev_info(&pdev->dev, "Cyclades-Z/PCI: New Cyclades-Z "
-                               "board.  FPGA not loaded\n");
-               }
+                       if (mailbox == ZO_V1) {
+                               cy_writel(&ctl_addr->loc_addr_base, WIN_CREG);
+                               dev_info(&pdev->dev, "Cyclades-8Zo/PCI: FPGA "
+                                       "id %lx, ver %lx\n", (ulong)(0xff &
+                                       readl(&((struct CUSTOM_REG *)addr2)->
+                                               fpga_id)), (ulong)(0xff &
+                                       readl(&((struct CUSTOM_REG *)addr2)->
+                                               fpga_version)));
+                               cy_writel(&ctl_addr->loc_addr_base, WIN_RAM);
+                       } else {
+                               dev_info(&pdev->dev, "Cyclades-Z/PCI: New "
+                                       "Cyclades-Z board.  FPGA not loaded\n");
+                       }
 #endif
-               /* The following clears the firmware id word.  This
-                  ensures that the driver will not attempt to talk to
-                  the board until it has been properly initialized.
-                */
-               if ((mailbox == ZO_V1) || (mailbox == ZO_V2))
-                       cy_writel(cy_pci_addr2 + ID_ADDRESS, 0L);
-
-               /* This must be a Cyclades-8Zo/PCI.  The extendable
-                  version will have a different device_id and will
-                  be allocated its maximum number of ports. */
-               cy_pci_nchan = 8;
-
-               if ((cy_next_channel + cy_pci_nchan) > NR_PORTS) {
-                       dev_err(&pdev->dev, "Cyclades-8Zo/PCI found, but no "
-                               "channels are available. Change NR_PORTS in "
-                               "cyclades.c and recompile kernel.\n");
-                       return -EIO;
-               }
+                       /* The following clears the firmware id word.  This
+                          ensures that the driver will not attempt to talk to
+                          the board until it has been properly initialized.
+                        */
+                       if ((mailbox == ZO_V1) || (mailbox == ZO_V2))
+                               cy_writel(addr2 + ID_ADDRESS, 0L);
 
-               /* fill the next cy_card structure available */
-               for (j = 0; j < NR_CARDS; j++) {
-                       if (cy_card[j].base_addr == 0)
-                               break;
+                       /* This must be a Cyclades-8Zo/PCI.  The extendable
+                          version will have a different device_id and will
+                          be allocated its maximum number of ports. */
+                       nchan = 8;
                }
-               if (j == NR_CARDS) {    /* no more cy_cards available */
-                       dev_err(&pdev->dev, "Cyclades-8Zo/PCI found, but no "
-                               "more cards can be used. Change NR_CARDS in "
-                               "cyclades.c and recompile kernel.\n");
-                       return -EIO;
+       }
+
+       if ((cy_next_channel + nchan) > NR_PORTS) {
+               dev_err(&pdev->dev, "Cyclades-8Zo/PCI found, but no "
+                       "channels are available. Change NR_PORTS in "
+                       "cyclades.c and recompile kernel.\n");
+               goto err_unmap;
+       }
+       /* fill the next cy_card structure available */
+       for (card_no = 0; card_no < NR_CARDS; card_no++) {
+               if (cy_card[card_no].base_addr == NULL)
+                       break;
+       }
+       if (card_no == NR_CARDS) {      /* no more cy_cards available */
+               dev_err(&pdev->dev, "Cyclades-8Zo/PCI found, but no "
+                       "more cards can be used. Change NR_CARDS in "
+                       "cyclades.c and recompile kernel.\n");
+               goto err_unmap;
+       }
+
+       if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo ||
+                       device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) {
+               /* allocate IRQ */
+               retval = request_irq(irq, cyy_interrupt,
+                               IRQF_SHARED, "Cyclom-Y", &cy_card[card_no]);
+               if (retval) {
+                       dev_err(&pdev->dev, "could not allocate IRQ\n");
+                       goto err_unmap;
                }
+               cy_card[card_no].num_chips = nchan / 4;
+       } else {
 #ifdef CONFIG_CYZ_INTR
                /* allocate IRQ only if board has an IRQ */
-               if ((cy_pci_irq != 0) && (cy_pci_irq != 255)) {
-                       retval = request_irq(cy_pci_irq, cyz_interrupt,
+               if (irq != 0 && irq != 255) {
+                       retval = request_irq(irq, cyz_interrupt,
                                        IRQF_SHARED, "Cyclades-Z",
-                                       &cy_card[j]);
+                                       &cy_card[card_no]);
                        if (retval) {
                                dev_err(&pdev->dev, "could not allocate IRQ\n");
-                               return retval;
+                               goto err_unmap;
                        }
                }
 #endif                         /* CONFIG_CYZ_INTR */
+               cy_card[card_no].num_chips = -1;
+       }
 
-               /* set cy_card */
-               cy_card[j].base_addr = cy_pci_addr2;
-               cy_card[j].ctl_addr = cy_pci_addr0;
-               cy_card[j].irq = cy_pci_irq;
-               cy_card[j].bus_index = 1;
-               cy_card[j].first_line = cy_next_channel;
-               cy_card[j].num_chips = -1;
-               cy_init_card(&cy_card[j]);
-               pci_set_drvdata(pdev, &cy_card[j]);
+       /* set cy_card */
+       cy_card[card_no].base_addr = addr2;
+       cy_card[card_no].ctl_addr = addr0;
+       cy_card[card_no].irq = irq;
+       cy_card[card_no].bus_index = 1;
+       cy_card[card_no].first_line = cy_next_channel;
+       retval = cy_init_card(&cy_card[card_no]);
+       if (retval)
+               goto err_null;
 
-               dev_info(&pdev->dev, "Cyclades-8Zo/PCI #%d found: %d channels "
-                       "starting from port %d.\n", j + 1, cy_pci_nchan,
-                       cy_next_channel);
+       pci_set_drvdata(pdev, &cy_card[card_no]);
 
-               for (j = cy_next_channel;
-                               j < cy_next_channel + cy_pci_nchan; j++)
-                       tty_register_device(cy_serial_driver, j, &pdev->dev);
-               cy_next_channel += cy_pci_nchan;
+       if (device_id == PCI_DEVICE_ID_CYCLOM_Y_Lo ||
+                       device_id == PCI_DEVICE_ID_CYCLOM_Y_Hi) {
+               /* enable interrupts in the PCI interface */
+               plx_ver = readb(addr2 + CyPLX_VER) & 0x0f;
+               switch (plx_ver) {
+               case PLX_9050:
+
+                       cy_writeb(addr0 + 0x4c, 0x43);
+                       break;
+
+               case PLX_9060:
+               case PLX_9080:
+               default:        /* Old boards, use PLX_9060 */
+
+                       plx_init(addr0, 0x6c);
+               /* For some yet unknown reason, once the PLX9060 reloads
+                  the EEPROM, the IRQ is lost and, thus, we have to
+                  re-write it to the PCI config. registers.
+                  This will remain here until we find a permanent
+                  fix. */
+                       pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, irq);
+
+                       cy_writew(addr0 + 0x68, readw(addr0 + 0x68) | 0x0900);
+                       break;
+               }
        }
 
+       dev_info(&pdev->dev, "%s/PCI #%d found: %d channels starting from "
+               "port %d.\n", card_name, card_no + 1, nchan, cy_next_channel);
+       for (i = cy_next_channel; i < cy_next_channel + nchan; i++)
+               tty_register_device(cy_serial_driver, i, &pdev->dev);
+       cy_next_channel += nchan;
+
        return 0;
+err_null:
+       cy_card[card_no].base_addr = NULL;
+       free_irq(irq, &cy_card[card_no]);
+err_unmap:
+       pci_iounmap(pdev, addr0);
+       if (addr2)
+               pci_iounmap(pdev, addr2);
+err_reg:
+       pci_release_regions(pdev);
+err_dis:
+       pci_disable_device(pdev);
+err:
+       return retval;
 }
 
 static void __devexit cy_pci_remove(struct pci_dev *pdev)
@@ -5112,13 +5020,11 @@ static void __devexit cy_pci_remove(struct pci_dev *pdev)
        pci_release_regions(pdev);
 
        cinfo->base_addr = NULL;
-       for (i = cinfo->first_line; i < cinfo->first_line + cinfo->nports; i++){
-               cy_port[i].line = -1;
-               cy_port[i].magic = -1;
-       }
        for (i = cinfo->first_line; i < cinfo->first_line +
                        cinfo->nports; i++)
                tty_unregister_device(cy_serial_driver, i);
+       cinfo->nports = 0;
+       kfree(cinfo->ports);
 }
 
 static struct pci_driver cy_pci_driver = {
@@ -5134,7 +5040,7 @@ cyclades_get_proc_info(char *buf, char **start, off_t offset, int length,
                int *eof, void *data)
 {
        struct cyclades_port *info;
-       int i;
+       unsigned int i, j;
        int len = 0;
        off_t begin = 0;
        off_t pos = 0;
@@ -5148,33 +5054,34 @@ cyclades_get_proc_info(char *buf, char **start, off_t offset, int length,
        len += size;
 
        /* Output one line for each known port */
-       for (i = 0; i < NR_PORTS && cy_port[i].line >= 0; i++) {
-               info = &cy_port[i];
-
-               if (info->count)
-                       size = sprintf(buf + len, "%3d %8lu %10lu %8lu %10lu "
-                               "%8lu %9lu %6ld\n", info->line,
-                               (cur_jifs - info->idle_stats.in_use) / HZ,
-                               info->idle_stats.xmit_bytes,
-                               (cur_jifs - info->idle_stats.xmit_idle) / HZ,
-                               info->idle_stats.recv_bytes,
-                               (cur_jifs - info->idle_stats.recv_idle) / HZ,
-                               info->idle_stats.overruns,
-                               (long)info->tty->ldisc.num);
-               else
-                       size = sprintf(buf + len, "%3d %8lu %10lu %8lu %10lu "
-                               "%8lu %9lu %6ld\n",
-                               info->line, 0L, 0L, 0L, 0L, 0L, 0L, 0L);
-               len += size;
-               pos = begin + len;
-
-               if (pos < offset) {
-                       len = 0;
-                       begin = pos;
+       for (i = 0; i < NR_CARDS; i++)
+               for (j = 0; j < cy_card[i].nports; j++) {
+                       info = &cy_card[i].ports[j];
+
+                       if (info->count)
+                               size = sprintf(buf + len, "%3d %8lu %10lu %8lu "
+                                       "%10lu %8lu %9lu %6ld\n", info->line,
+                                       (cur_jifs - info->idle_stats.in_use) /
+                                       HZ, info->idle_stats.xmit_bytes,
+                                       (cur_jifs - info->idle_stats.xmit_idle)/
+                                       HZ, info->idle_stats.recv_bytes,
+                                       (cur_jifs - info->idle_stats.recv_idle)/
+                                       HZ, info->idle_stats.overruns,
+                                       (long)info->tty->ldisc.num);
+                       else
+                               size = sprintf(buf + len, "%3d %8lu %10lu %8lu "
+                                       "%10lu %8lu %9lu %6ld\n",
+                                       info->line, 0L, 0L, 0L, 0L, 0L, 0L, 0L);
+                       len += size;
+                       pos = begin + len;
+
+                       if (pos < offset) {
+                               len = 0;
+                               begin = pos;
+                       }
+                       if (pos > offset + length)
+                               goto done;
                }
-               if (pos > offset + length)
-                       goto done;
-       }
        *eof = 1;
 done:
        *start = buf + (offset - begin);        /* Start of wanted data */
@@ -5229,7 +5136,7 @@ static const struct tty_operations cy_ops = {
 
 static int __init cy_init(void)
 {
-       unsigned int i, nboards;
+       unsigned int nboards;
        int retval = -ENOMEM;
 
        cy_serial_driver = alloc_tty_driver(NR_PORTS);
@@ -5260,11 +5167,6 @@ static int __init cy_init(void)
                goto err_frtty;
        }
 
-       for (i = 0; i < NR_PORTS; i++) {
-               cy_port[i].line = -1;
-               cy_port[i].magic = -1;
-       }
-
        /* the code below is responsible to find the boards. Each different
           type of board has its own detection routine. If a board is found,
           the next cy_card structure available is set by the detection
@@ -5293,6 +5195,7 @@ err:
 
 static void __exit cy_cleanup_module(void)
 {
+       struct cyclades_card *card;
        int i, e1;
 
 #ifndef CONFIG_CYZ_INTR
@@ -5303,34 +5206,37 @@ static void __exit cy_cleanup_module(void)
                printk(KERN_ERR "failed to unregister Cyclades serial "
                                "driver(%d)\n", e1);
 
-       put_tty_driver(cy_serial_driver);
-
 #ifdef CONFIG_PCI
        pci_unregister_driver(&cy_pci_driver);
 #endif
 
        for (i = 0; i < NR_CARDS; i++) {
-               if (cy_card[i].base_addr) {
+               card = &cy_card[i];
+               if (card->base_addr) {
                        /* clear interrupt */
-                       cy_writeb(cy_card[i].base_addr + Cy_ClrIntr, 0);
-                       iounmap(cy_card[i].base_addr);
-                       if (cy_card[i].ctl_addr)
-                               iounmap(cy_card[i].ctl_addr);
-                       if (cy_card[i].irq
+                       cy_writeb(card->base_addr + Cy_ClrIntr, 0);
+                       iounmap(card->base_addr);
+                       if (card->ctl_addr)
+                               iounmap(card->ctl_addr);
+                       if (card->irq
 #ifndef CONFIG_CYZ_INTR
-                               && !IS_CYC_Z(cy_card[i])
+                               && !IS_CYC_Z(*card)
 #endif /* CONFIG_CYZ_INTR */
                                )
-                               free_irq(cy_card[i].irq, &cy_card[i]);
-                       for (e1 = cy_card[i].first_line;
-                                       e1 < cy_card[i].first_line +
-                                       cy_card[i].nports; e1++)
+                               free_irq(card->irq, card);
+                       for (e1 = card->first_line;
+                                       e1 < card->first_line +
+                                       card->nports; e1++)
                                tty_unregister_device(cy_serial_driver, e1);
+                       kfree(card->ports);
                }
        }
+
+       put_tty_driver(cy_serial_driver);
 } /* cy_cleanup_module */
 
 module_init(cy_init);
 module_exit(cy_cleanup_module);
 
 MODULE_LICENSE("GPL");
+MODULE_VERSION(CY_VERSION);