]> err.no Git - linux-2.6/blobdiff - drivers/char/cyclades.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc
[linux-2.6] / drivers / char / cyclades.c
index 7fe4bb60f690c25883efdf6b7baa6a8e66acd047..9e0adfe27c12d56cf8d36515f0c6061a47db4a8a 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
 #include <linux/delay.h>
 #include <linux/spinlock.h>
 #include <linux/bitops.h>
+#include <linux/firmware.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
@@ -681,6 +681,44 @@ static void cy_send_xchar(struct tty_struct *tty, char ch);
 
 #define STD_COM_FLAGS (0)
 
+/* firmware stuff */
+#define ZL_MAX_BLOCKS  16
+#define DRIVER_VERSION 0x02010203
+#define RAM_SIZE 0x80000
+
+#define Z_FPGA_LOADED(X)       ((readl(&(X)->init_ctrl) & (1<<17)) != 0)
+
+enum zblock_type {
+       ZBLOCK_PRG = 0,
+       ZBLOCK_FPGA = 1
+};
+
+struct zfile_header {
+       char name[64];
+       char date[32];
+       char aux[32];
+       u32 n_config;
+       u32 config_offset;
+       u32 n_blocks;
+       u32 block_offset;
+       u32 reserved[9];
+} __attribute__ ((packed));
+
+struct zfile_config {
+       char name[64];
+       u32 mailbox;
+       u32 function;
+       u32 n_blocks;
+       u32 block_list[ZL_MAX_BLOCKS];
+} __attribute__ ((packed));
+
+struct zfile_block {
+       u32 type;
+       u32 file_offset;
+       u32 ram_offset;
+       u32 size;
+} __attribute__ ((packed));
+
 static struct tty_driver *cy_serial_driver;
 
 #ifdef CONFIG_ISA
@@ -1062,6 +1100,7 @@ static void cyy_intr_chip(struct cyclades_card *cinfo, int chip,
 
                                if (data & info->ignore_status_mask) {
                                        info->icount.rx++;
+                                       spin_unlock(&cinfo->card_lock);
                                        return;
                                }
                                if (tty_buffer_request_room(tty, 1)) {
@@ -1851,11 +1890,11 @@ static void cyz_poll(unsigned long arg)
        struct cyclades_card *cinfo;
        struct cyclades_port *info;
        struct tty_struct *tty;
-       static struct FIRM_ID *firm_id;
-       static struct ZFW_CTRL *zfw_ctrl;
-       static struct BOARD_CTRL *board_ctrl;
-       static struct CH_CTRL *ch_ctrl;
-       static struct BUF_CTRL *buf_ctrl;
+       struct FIRM_ID __iomem *firm_id;
+       struct ZFW_CTRL __iomem *zfw_ctrl;
+       struct BOARD_CTRL __iomem *board_ctrl;
+       struct CH_CTRL __iomem *ch_ctrl;
+       struct BUF_CTRL __iomem *buf_ctrl;
        unsigned long expires = jiffies + HZ;
        int card, port;
 
@@ -1999,7 +2038,6 @@ static int startup(struct cyclades_port *info)
                struct ZFW_CTRL __iomem *zfw_ctrl;
                struct BOARD_CTRL __iomem *board_ctrl;
                struct CH_CTRL __iomem *ch_ctrl;
-               int retval;
 
                base_addr = card->base_addr;
 
@@ -2371,7 +2409,6 @@ block_til_ready(struct tty_struct *tty, struct file *filp,
                struct ZFW_CTRL __iomem *zfw_ctrl;
                struct BOARD_CTRL __iomem *board_ctrl;
                struct CH_CTRL __iomem *ch_ctrl;
-               int retval;
 
                base_addr = cinfo->base_addr;
                firm_id = base_addr + ID_ADDRESS;
@@ -4127,10 +4164,6 @@ static void cy_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
        printk(KERN_DEBUG "cyc:cy_set_termios ttyC%d\n", info->line);
 #endif
 
-       if (tty->termios->c_cflag == old_termios->c_cflag &&
-                       (tty->termios->c_iflag & (IXON | IXANY)) ==
-                       (old_termios->c_iflag & (IXON | IXANY)))
-               return;
        set_line_char(info);
 
        if ((old_termios->c_cflag & CRTSCTS) &&
@@ -4433,10 +4466,10 @@ static void cy_hangup(struct tty_struct *tty)
 static int __devinit cy_init_card(struct cyclades_card *cinfo)
 {
        struct cyclades_port *info;
-       u32 mailbox;
+       u32 uninitialized_var(mailbox);
        unsigned int nports;
        unsigned short chip_number;
-       int index, port;
+       int uninitialized_var(index), port;
 
        spin_lock_init(&cinfo->card_lock);
 
@@ -4455,6 +4488,7 @@ static int __devinit cy_init_card(struct cyclades_card *cinfo)
        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;
        }
 
@@ -4647,9 +4681,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
@@ -4663,6 +4703,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;
                }
 
@@ -4671,6 +4712,7 @@ 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 */
@@ -4683,6 +4725,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;
                }
 
@@ -4692,6 +4735,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;
                }
 
@@ -4702,7 +4746,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: "
@@ -4723,346 +4772,524 @@ static int __init cy_detect_isa(void)
 }                              /* cy_detect_isa */
 
 #ifdef CONFIG_PCI
-static void __devinit plx_init(void __iomem * addr, __u32 initctl)
+static inline int __devinit cyc_isfwstr(const char *str, unsigned int size)
+{
+       unsigned int a;
+
+       for (a = 0; a < size && *str; a++, str++)
+               if (*str & 0x80)
+                       return -EINVAL;
+
+       for (; a < size; a++, str++)
+               if (*str)
+                       return -EINVAL;
+
+       return 0;
+}
+
+static inline void __devinit cyz_fpga_copy(void __iomem *fpga, u8 *data,
+               unsigned int size)
+{
+       for (; size > 0; size--) {
+               cy_writel(fpga, *data++);
+               udelay(10);
+       }
+}
+
+static void __devinit plx_init(struct pci_dev *pdev, int irq,
+               struct RUNTIME_9060 __iomem *addr)
 {
        /* Reset PLX */
-       cy_writel(addr + initctl, readl(addr + initctl) | 0x40000000);
+       cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) | 0x40000000);
        udelay(100L);
-       cy_writel(addr + initctl, readl(addr + initctl) & ~0x40000000);
+       cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) & ~0x40000000);
 
        /* Reload Config. Registers from EEPROM */
-       cy_writel(addr + initctl, readl(addr + initctl) | 0x20000000);
+       cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) | 0x20000000);
        udelay(100L);
-       cy_writel(addr + initctl, readl(addr + initctl) & ~0x20000000);
+       cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) & ~0x20000000);
+
+       /* 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);
 }
 
-static int __devinit cy_init_Ze(struct RUNTIME_9060 __iomem *cy_pci_addr0,
-               int cy_pci_irq, struct pci_dev *pdev)
+static int __devinit __cyz_load_fw(const struct firmware *fw,
+               const char *name, const u32 mailbox, void __iomem *base,
+               void __iomem *fpga)
 {
-       void __iomem *cy_pci_addr2;
-       unsigned int j;
-       unsigned short cy_pci_nchan;
+       void *ptr = fw->data;
+       struct zfile_header *h = ptr;
+       struct zfile_config *c, *cs;
+       struct zfile_block *b, *bs;
+       unsigned int a, tmp, len = fw->size;
+#define BAD_FW KERN_ERR "Bad firmware: "
+       if (len < sizeof(*h)) {
+               printk(BAD_FW "too short: %u<%zu\n", len, sizeof(*h));
+               return -EINVAL;
+       }
 
-       cy_pci_addr2 = pci_iomap(pdev, 2, CyPCI_Ze_win);
+       cs = ptr + h->config_offset;
+       bs = ptr + h->block_offset;
 
-       readl(&cy_pci_addr0->mail_box_0);
-       dev_dbg(&pdev->dev, "new Cyclades-Z board.  FPGA not loaded\n");
+       if ((void *)(cs + h->n_config) > ptr + len ||
+                       (void *)(bs + h->n_blocks) > ptr + len) {
+               printk(BAD_FW "too short");
+               return  -EINVAL;
+       }
 
-       /* This must be the new Cyclades-Ze/PCI. */
-       cy_pci_nchan = ZE_V1_NPORTS;
+       if (cyc_isfwstr(h->name, sizeof(h->name)) ||
+                       cyc_isfwstr(h->date, sizeof(h->date))) {
+               printk(BAD_FW "bad formatted header string\n");
+               return -EINVAL;
+       }
 
-       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;
+       if (strncmp(name, h->name, sizeof(h->name))) {
+               printk(BAD_FW "bad name '%s' (expected '%s')\n", h->name, name);
+               return -EINVAL;
        }
 
-       /* fill the next cy_card structure available */
-       for (j = 0; j < NR_CARDS; j++) {
-               if (cy_card[j].base_addr == NULL)
+       tmp = 0;
+       for (c = cs; c < cs + h->n_config; c++) {
+               for (a = 0; a < c->n_blocks; a++)
+                       if (c->block_list[a] > h->n_blocks) {
+                               printk(BAD_FW "bad block ref number in cfgs\n");
+                               return -EINVAL;
+                       }
+               if (c->mailbox == mailbox && c->function == 0) /* 0 is normal */
+                       tmp++;
+       }
+       if (!tmp) {
+               printk(BAD_FW "nothing appropriate\n");
+               return -EINVAL;
+       }
+
+       for (b = bs; b < bs + h->n_blocks; b++)
+               if (b->file_offset + b->size > len) {
+                       printk(BAD_FW "bad block data offset\n");
+                       return -EINVAL;
+               }
+
+       /* everything is OK, let's seek'n'load it */
+       for (c = cs; c < cs + h->n_config; c++)
+               if (c->mailbox == mailbox && c->function == 0)
                        break;
+
+       for (a = 0; a < c->n_blocks; a++) {
+               b = &bs[c->block_list[a]];
+               if (b->type == ZBLOCK_FPGA) {
+                       if (fpga != NULL)
+                               cyz_fpga_copy(fpga, ptr + b->file_offset,
+                                               b->size);
+               } else {
+                       if (base != NULL)
+                               memcpy_toio(base + b->ram_offset,
+                                              ptr + b->file_offset, b->size);
+               }
        }
-       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;
+#undef BAD_FW
+       return 0;
+}
+
+static int __devinit cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr,
+               struct RUNTIME_9060 __iomem *ctl_addr, int irq)
+{
+       const struct firmware *fw;
+       struct FIRM_ID __iomem *fid = base_addr + ID_ADDRESS;
+       struct CUSTOM_REG __iomem *cust = base_addr;
+       struct ZFW_CTRL __iomem *pt_zfwctrl;
+       void __iomem *tmp;
+       u32 mailbox, status;
+       unsigned int i;
+       int retval;
+
+       retval = request_firmware(&fw, "cyzfirm.bin", &pdev->dev);
+       if (retval) {
+               dev_err(&pdev->dev, "can't get firmware\n");
+               goto err;
        }
-#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;
+
+       /* Check whether the firmware is already loaded and running. If
+          positive, skip this board */
+       if (Z_FPGA_LOADED(ctl_addr) && readl(&fid->signature) == ZFIRM_ID) {
+               u32 cntval = readl(base_addr + 0x190);
+
+               udelay(100);
+               if (cntval != readl(base_addr + 0x190)) {
+                       /* FW counter is working, FW is running */
+                       dev_dbg(&pdev->dev, "Cyclades-Z FW already loaded. "
+                                       "Skipping board.\n");
+                       retval = 0;
+                       goto err_rel;
                }
        }
-#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;
+       /* start boot */
+       cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) &
+                       ~0x00030800UL);
+
+       mailbox = readl(&ctl_addr->mail_box_0);
+
+       if (mailbox == 0 || Z_FPGA_LOADED(ctl_addr)) {
+               /* stops CPU and set window to beginning of RAM */
+               cy_writel(&ctl_addr->loc_addr_base, WIN_CREG);
+               cy_writel(&cust->cpu_stop, 0);
+               cy_writel(&ctl_addr->loc_addr_base, WIN_RAM);
+               udelay(100);
+       }
+
+       plx_init(pdev, irq, ctl_addr);
+
+       if (mailbox != 0) {
+               /* load FPGA */
+               retval = __cyz_load_fw(fw, "Cyclom-Z", mailbox, NULL,
+                               base_addr);
+               if (retval)
+                       goto err_rel;
+               if (!Z_FPGA_LOADED(ctl_addr)) {
+                       dev_err(&pdev->dev, "fw upload successful, but fw is "
+                                       "not loaded\n");
+                       goto err_rel;
+               }
+       }
+
+       /* stops CPU and set window to beginning of RAM */
+       cy_writel(&ctl_addr->loc_addr_base, WIN_CREG);
+       cy_writel(&cust->cpu_stop, 0);
+       cy_writel(&ctl_addr->loc_addr_base, WIN_RAM);
+       udelay(100);
+
+       /* clear memory */
+       for (tmp = base_addr; tmp < base_addr + RAM_SIZE; tmp++)
+               cy_writeb(tmp, 255);
+       if (mailbox != 0) {
+               /* set window to last 512K of RAM */
+               cy_writel(&ctl_addr->loc_addr_base, WIN_RAM + RAM_SIZE);
+               //sleep(1);
+               for (tmp = base_addr; tmp < base_addr + RAM_SIZE; tmp++)
+                       cy_writeb(tmp, 255);
+               /* set window to beginning of RAM */
+               cy_writel(&ctl_addr->loc_addr_base, WIN_RAM);
+               //sleep(1);
+       }
+
+       retval = __cyz_load_fw(fw, "Cyclom-Z", mailbox, base_addr, NULL);
+       release_firmware(fw);
+       if (retval)
+               goto err;
+
+       /* finish boot and start boards */
+       cy_writel(&ctl_addr->loc_addr_base, WIN_CREG);
+       cy_writel(&cust->cpu_start, 0);
+       cy_writel(&ctl_addr->loc_addr_base, WIN_RAM);
+       i = 0;
+       while ((status = readl(&fid->signature)) != ZFIRM_ID && i++ < 40)
+               msleep(100);
+       if (status != ZFIRM_ID) {
+               if (status == ZFIRM_HLT) {
+                       dev_err(&pdev->dev, "you need an external power supply "
+                               "for this number of ports. Firmware halted and "
+                               "board reset.\n");
+                       retval = -EIO;
+                       goto err;
+               }
+               dev_warn(&pdev->dev, "fid->signature = 0x%x... Waiting "
+                               "some more time\n", status);
+               while ((status = readl(&fid->signature)) != ZFIRM_ID &&
+                               i++ < 200)
+                       msleep(100);
+               if (status != ZFIRM_ID) {
+                       dev_err(&pdev->dev, "Board not started in 20 seconds! "
+                                       "Giving up. (fid->signature = 0x%x)\n",
+                                       status);
+                       dev_info(&pdev->dev, "*** Warning ***: if you are "
+                               "upgrading the FW, please power cycle the "
+                               "system before loading the new FW to the "
+                               "Cyclades-Z.\n");
+
+                       if (Z_FPGA_LOADED(ctl_addr))
+                               plx_init(pdev, irq, ctl_addr);
+
+                       retval = -EIO;
+                       goto err;
+               }
+               dev_dbg(&pdev->dev, "Firmware started after %d seconds.\n",
+                               i / 10);
+       }
+       pt_zfwctrl = base_addr + readl(&fid->zfwctrl_addr);
+
+       dev_dbg(&pdev->dev, "fid=> %p, zfwctrl_addr=> %x, npt_zfwctrl=> %p\n",
+                       base_addr + ID_ADDRESS, readl(&fid->zfwctrl_addr),
+                       base_addr + readl(&fid->zfwctrl_addr));
+
+       dev_info(&pdev->dev, "Cyclades-Z FW loaded: version = %x, ports = %u\n",
+               readl(&pt_zfwctrl->board_ctrl.fw_version),
+               readl(&pt_zfwctrl->board_ctrl.n_channel));
+
+       if (readl(&pt_zfwctrl->board_ctrl.n_channel) == 0) {
+               dev_warn(&pdev->dev, "no Cyclades-Z ports were found. Please "
+                       "check the connection between the Z host card and the "
+                       "serial expanders.\n");
+
+               if (Z_FPGA_LOADED(ctl_addr))
+                       plx_init(pdev, irq, ctl_addr);
+
+               dev_info(&pdev->dev, "Null number of ports detected. Board "
+                               "reset.\n");
+               retval = 0;
+               goto err;
+       }
+
+       cy_writel(&pt_zfwctrl->board_ctrl.op_system, C_OS_LINUX);
+       cy_writel(&pt_zfwctrl->board_ctrl.dr_version, DRIVER_VERSION);
+
+       /*
+          Early firmware failed to start looking for commands.
+          This enables firmware interrupts for those commands.
+        */
+       cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) |
+                       (1 << 17));
+       cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) |
+                       0x00030800UL);
+
+       plx_init(pdev, irq, ctl_addr);
 
        return 0;
+err_rel:
+       release_firmware(fw);
+err:
+       return retval;
 }
 
 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 == NULL)
-                               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 */
+       } else if (device_id == PCI_DEVICE_ID_CYCLOM_Z_Hi) {
+               struct RUNTIME_9060 __iomem *ctl_addr;
 
-                       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;
+               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;
                }
 
-               dev_info(&pdev->dev, "Cyclom-Y/PCI #%d found: %d channels "
-                       "starting from port %d.\n", j + 1, cy_pci_nchan,
-                       cy_next_channel);
+               /* Disable interrupts on the PLX before resetting it */
+               cy_writew(addr0 + 0x68, readw(addr0 + 0x68) & ~0x0900);
 
-               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");
+               plx_init(pdev, irq, addr0);
 
-               cy_pci_addr0 = pci_iomap(pdev, 0, CyPCI_Zctl);
+               mailbox = (u32)readl(&ctl_addr->mail_box_0);
 
-               /* Disable interrupts on the PLX before resetting it */
-               cy_writew(cy_pci_addr0 + 0x68,
-                       readw(cy_pci_addr0 + 0x68) & ~0x0900);
-
-               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);
-
-               mailbox = (__u32)readl(&((struct RUNTIME_9060 __iomem *)
-                               cy_pci_addr0)->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);
+
+                       retval = cyz_load_fw(pdev, addr2, addr0, irq);
+                       if (retval)
+                               goto err_unmap;
+                       /* 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;
                }
+       }
 
-               /* fill the next cy_card structure available */
-               for (j = 0; j < NR_CARDS; j++) {
-                       if (cy_card[j].base_addr == NULL)
-                               break;
-               }
-               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(pdev, irq, addr0);
+                       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)
@@ -5312,3 +5539,4 @@ module_init(cy_init);
 module_exit(cy_cleanup_module);
 
 MODULE_LICENSE("GPL");
+MODULE_VERSION(CY_VERSION);