*
* 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>
#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
if (data & info->ignore_status_mask) {
info->icount.rx++;
+ spin_unlock(&cinfo->card_lock);
return;
}
if (tty_buffer_request_room(tty, 1)) {
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;
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;
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;
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) &&
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);
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;
}
/* 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
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;
}
"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 */
"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;
}
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;
}
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: "
} /* 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)
module_exit(cy_cleanup_module);
MODULE_LICENSE("GPL");
+MODULE_VERSION(CY_VERSION);