Char: cyclades, add firmware loading
PATCH 1/2 Char: cyclades, add firmware loading
cyclades, add firmware loading
---
commit 7a9aa4781fc5aa6493bb3a7ac59b3c9e5f20fa76
tree 18d85e52a13cd780cf808a43d68b569a8546e2ab
parent 4ea1257b890befc706f6d43562ba68671db39195
author Jiri Slaby <[email protected]> Fri, 18 May 2007 18:45:35 +0200 committer Jiri Slaby <[email protected]> Fri, 18 May 2007 18:45:35 +0200
drivers/char/cyclades.c | 351 ++++++++++++++++++++++++++++++++++++++++++++--- 1 files changed, 328 insertions(+), 23 deletions(-)
drivers/char/cyclades.c +328 -23
@@ -646,6 +646,7 @@#include <linux/delay.h> #include <linux/spinlock.h> #include <linux/bitops.h> +#include <linux/firmware.h> #include <asm/system.h> #include <asm/io.h>
@@ -680,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
@@ -4738,17 +4777,295 @@ 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 __cyz_load_fw (const struct firmware *fw, +const char *name,const u32 mailbox,void __iomem *base, +void __iomem *fpga) +{ +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 ; + } + + cs = ptr + h->config_offset; + bs = ptr + h->block_offset; + +if ((void *)(cs + h->n_config) > ptr + len || + (void *)(bs + h->n_blocks) > ptr + len) { +printk (BAD_FW "too short" ); +return -EINVAL ; + } + +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 (strncmp (name, h->name,sizeof (h->name))) { +printk (BAD_FW "bad name '%s' (expected '%s')\n" , h->name, name); +return -EINVAL ; + } + + 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); + } + } +#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; +u8 *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; + } + +/* 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; + } + } + +/* 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; (void *)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; (void *)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,
@@ -4830,16 +5147,9 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev, }/* Disable interrupts on the PLX before resetting it */ -cy_writew (addr0 +0x68 , -readw (addr0 +0x68 ) & ~0x0900 ); +cy_writew (addr0 +0x68 ,readw (addr0 +0x68 ) & ~0x0900 ); -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); +plx_init (pdev, irq, addr0); mailbox = (u32 )readl (&ctl_addr->mail_box_0);
@@ -4880,6 +5190,9 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev,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. */
@@ -4956,15 +5269,7 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev,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); - +plx_init (pdev, irq, addr0);cy_writew (addr0 +0x68 ,readw (addr0 +0x68 ) |0x0900 );break ; }
PATCH 2/2 Char: cyclades, fix sparse warning
cyclades, fix sparse warning
+ one possible deadlock (omitted unlock)
---
commit a9dbc0b98956d548b1aee3f55b3799a12946ace4
tree 1e62235a9bf1edb7c206932c9a10d1e9e77cb0a0
parent 7a9aa4781fc5aa6493bb3a7ac59b3c9e5f20fa76
author Jiri Slaby <[email protected]> Fri, 18 May 2007 19:42:55 +0200 committer Jiri Slaby <[email protected]> Fri, 18 May 2007 19:42:55 +0200
drivers/char/cyclades.c | 19 +++++++++---------- 1 files changed, 9 insertions(+), 10 deletions(-)
drivers/char/cyclades.c +9 -10
@@ -1100,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 )) {
@@ -1889,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;
@@ -2037,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;
@@ -2409,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 ;
@@ -4905,7 +4904,7 @@ static int __devinit cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr,struct FIRM_ID __iomem *fid = base_addr +ID_ADDRESS ;struct CUSTOM_REG __iomem *cust = base_addr;struct ZFW_CTRL __iomem *pt_zfwctrl; -u8 *tmp; +void __iomem *tmp;u32 mailbox, status;unsigned int i;int retval;
@@ -4967,13 +4966,13 @@ static int __devinit cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr,udelay (100 );/* clear memory */ -for (tmp = base_addr; (void *)tmp < base_addr +RAM_SIZE ; tmp++) +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; (void *)tmp < base_addr +RAM_SIZE ; tmp++) +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 );
Quoted 2 lines · On Fri, 18 May 2007 19:49:11 +0200 (CEST) Jiri Slaby <[email protected]>…
cyclades, add firmware loading
eww, it adds a variable called "tmp".
This change isn't appropriate to 2.6.22.
The second patch fixes a bug in 2.6.22-rc1 and in 2.6.21 (yes?) but includes irrelevant changes which aren't appropriate to 2.6.22 and which depend on patch #1.
So I split the bugfix out into a separate patch.
Quoted 3 lines · Andrew Morton
Quoted 1 line · On Fri, 18 May 2007 19:49:11 +0200 (CEST) Jiri Slaby <[email protected]>…
cyclades, add firmware loading
[...]
Quoted 1 line
The second patch fixes a bug in 2.6.22-rc1 and in 2.6.21 (yes?) but
I think, the bug is there since the driver merge (original driver available on vendor's site contain this issue too), so yes.
thanks,
Signature · 4 lines
http://www.fi.muni.cz/~xslaby/ Jiri Slaby faculty of informatics, masaryk university, brno, cz e-mail: jirislaby gmail com, gpg pubkey fingerprint: B674 9967 0407 CE62 ACC8 22A0 32CC 55C3 39D4 7A7E