diff options
author | Jiri Slaby <xslaby@fi.muni.cz> | 2006-01-09 20:54:25 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-01-10 08:02:01 -0800 |
commit | e65c1db19fe8177fa2da53e3e0bddffe585b2d47 (patch) | |
tree | 4d1c54f559812b71c9fde79383d00db908c1fd4f /drivers/char/isicom.c | |
parent | 9ac0948b20f76d9659add91f868c57383ea1e4e5 (diff) | |
download | lwn-e65c1db19fe8177fa2da53e3e0bddffe585b2d47.tar.gz lwn-e65c1db19fe8177fa2da53e3e0bddffe585b2d47.zip |
[PATCH] char/isicom: Firmware loading
Firmware loading via hotplug added.
Cleanup firmware old-way fields in header file.
Signed-off-by: Jiri Slaby <xslaby@fi.muni.cz>
Cc: Greg KH <greg@kroah.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/char/isicom.c')
-rw-r--r-- | drivers/char/isicom.c | 410 |
1 files changed, 174 insertions, 236 deletions
diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c index 9ef8ab301768..55a47b33ff34 100644 --- a/drivers/char/isicom.c +++ b/drivers/char/isicom.c @@ -112,6 +112,7 @@ */ #include <linux/module.h> +#include <linux/firmware.h> #include <linux/kernel.h> #include <linux/tty.h> #include <linux/tty_flip.h> @@ -120,7 +121,6 @@ #include <linux/sched.h> #include <linux/serial.h> #include <linux/mm.h> -#include <linux/miscdevice.h> #include <linux/interrupt.h> #include <linux/timer.h> #include <linux/delay.h> @@ -175,8 +175,6 @@ static struct tty_driver *isicom_normal; static struct timer_list tx; static char re_schedule = 1; -static int ISILoad_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); - static void isicom_tx(unsigned long _data); static void isicom_start(struct tty_struct *tty); @@ -384,233 +382,6 @@ static inline void kill_queue(struct isi_port *port, short queue) unlock_card(card); } - -/* - * Firmware loader driver specific routines. This needs to mostly die - * and be replaced with request_firmware. - */ - -static struct file_operations ISILoad_fops = { - .owner = THIS_MODULE, - .ioctl = ISILoad_ioctl, -}; - -static struct miscdevice isiloader_device = { - ISILOAD_MISC_MINOR, "isictl", &ISILoad_fops -}; - - -static inline int WaitTillCardIsFree(unsigned long base) -{ - unsigned long count=0; - while( (!(inw(base+0xe) & 0x1)) && (count++ < 6000000)); - if (inw(base+0xe)&0x1) - return 0; - else - return 1; -} - -static int ISILoad_ioctl(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg) -{ - unsigned int card, i, j, signature, status, portcount = 0; - unsigned long t, base; - u16 word_count; - bin_frame frame; - void __user *argp = (void __user *)arg; - /* exec_record exec_rec; */ - - if (get_user(card, (int __user *)argp)) - return -EFAULT; - - if (card < 0 || card >= BOARD_COUNT) - return -ENXIO; - - base=isi_card[card].base; - - if (base==0) - return -ENXIO; /* disabled or not used */ - - switch(cmd) { - case MIOCTL_RESET_CARD: - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - printk(KERN_DEBUG "ISILoad:Resetting Card%d at 0x%lx ",card+1,base); - - inw(base+0x8); - - for (t=jiffies+HZ/100;time_before(jiffies, t);); - - outw(0,base+0x8); /* Reset */ - - for (j=1;j<=3;j++) { - for (t=jiffies+HZ;time_before(jiffies, t);); - printk("."); - } - signature=(inw(base+0x4)) & 0xff; - if (isi_card[card].isa) { - - if (!(inw(base+0xe) & 0x1) || (inw(base+0x2))) { -#ifdef ISICOM_DEBUG - printk("\nbase+0x2=0x%x , base+0xe=0x%x",inw(base+0x2),inw(base+0xe)); -#endif - printk("\nISILoad:ISA Card%d reset failure (Possible bad I/O Port Address 0x%lx).\n",card+1,base); - return -EIO; - } - } - else { - portcount = inw(base+0x2); - if (!(inw(base+0xe) & 0x1) || ((portcount!=0) && (portcount!=4) && (portcount!=8))) { -#ifdef ISICOM_DEBUG - printk("\nbase+0x2=0x%x , base+0xe=0x%x",inw(base+0x2),inw(base+0xe)); -#endif - printk("\nISILoad:PCI Card%d reset failure (Possible bad I/O Port Address 0x%lx).\n",card+1,base); - return -EIO; - } - } - switch(signature) { - case 0xa5: - case 0xbb: - case 0xdd: - if (isi_card[card].isa) - isi_card[card].port_count = 8; - else { - if (portcount == 4) - isi_card[card].port_count = 4; - else - isi_card[card].port_count = 8; - } - isi_card[card].shift_count = 12; - break; - - case 0xcc: isi_card[card].port_count = 16; - isi_card[card].shift_count = 11; - break; - - default: printk("ISILoad:Card%d reset failure (Possible bad I/O Port Address 0x%lx).\n",card+1,base); -#ifdef ISICOM_DEBUG - printk("Sig=0x%x\n",signature); -#endif - return -EIO; - } - printk("-Done\n"); - return put_user(signature,(unsigned __user *)argp); - - case MIOCTL_LOAD_FIRMWARE: - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - if (copy_from_user(&frame, argp, sizeof(bin_frame))) - return -EFAULT; - - if (WaitTillCardIsFree(base)) - return -EIO; - - outw(0xf0,base); /* start upload sequence */ - outw(0x00,base); - outw((frame.addr), base); /* lsb of adderess */ - - word_count=(frame.count >> 1) + frame.count % 2; - outw(word_count, base); - InterruptTheCard(base); - - for (i=0;i<=0x2f;i++); /* a wee bit of delay */ - - if (WaitTillCardIsFree(base)) - return -EIO; - - if ((status=inw(base+0x4))!=0) { - printk(KERN_WARNING "ISILoad:Card%d rejected load header:\nAddress:0x%x \nCount:0x%x \nStatus:0x%x \n", - card+1, frame.addr, frame.count, status); - return -EIO; - } - outsw(base, (void *) frame.bin_data, word_count); - - InterruptTheCard(base); - - for (i=0;i<=0x0f;i++); /* another wee bit of delay */ - - if (WaitTillCardIsFree(base)) - return -EIO; - - if ((status=inw(base+0x4))!=0) { - printk(KERN_ERR "ISILoad:Card%d got out of sync.Card Status:0x%x\n",card+1, status); - return -EIO; - } - return 0; - - case MIOCTL_READ_FIRMWARE: - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - - if (copy_from_user(&frame, argp, sizeof(bin_header))) - return -EFAULT; - - if (WaitTillCardIsFree(base)) - return -EIO; - - outw(0xf1,base); /* start download sequence */ - outw(0x00,base); - outw((frame.addr), base); /* lsb of adderess */ - - word_count=(frame.count >> 1) + frame.count % 2; - outw(word_count+1, base); - InterruptTheCard(base); - - for (i=0;i<=0xf;i++); /* a wee bit of delay */ - - if (WaitTillCardIsFree(base)) - return -EIO; - - if ((status=inw(base+0x4))!=0) { - printk(KERN_WARNING "ISILoad:Card%d rejected verify header:\nAddress:0x%x \nCount:0x%x \nStatus:0x%x \n", - card+1, frame.addr, frame.count, status); - return -EIO; - } - - inw(base); - insw(base, frame.bin_data, word_count); - InterruptTheCard(base); - - for (i=0;i<=0x0f;i++); /* another wee bit of delay */ - - if (WaitTillCardIsFree(base)) - return -EIO; - - if ((status=inw(base+0x4))!=0) { - printk(KERN_ERR "ISILoad:Card%d verify got out of sync.Card Status:0x%x\n",card+1, status); - return -EIO; - } - - if (copy_to_user(argp, &frame, sizeof(bin_frame))) - return -EFAULT; - return 0; - - case MIOCTL_XFER_CTRL: - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - if (WaitTillCardIsFree(base)) - return -EIO; - - outw(0xf2, base); - outw(0x800, base); - outw(0x0, base); - outw(0x0, base); - InterruptTheCard(base); - outw(0x0, base+0x4); /* for ISI4608 cards */ - - isi_card[card].status |= FIRMWARE_LOADED; - return 0; - - default: -#ifdef ISICOM_DEBUG - printk(KERN_DEBUG "ISILoad: Received Ioctl cmd 0x%x.\n", cmd); -#endif - return -ENOIOCTLCMD; - } -} - - /* * ISICOM Driver specific routines ... * @@ -1927,6 +1698,175 @@ end: return retval; } +static inline int WaitTillCardIsFree(u16 base) +{ + unsigned long count = 0; + + while (!(inw(base + 0xe) & 0x1) && count++ < 100) + msleep(5); + + return !(inw(base + 0xe) & 0x1); +} + +static int __devinit load_firmware(struct pci_dev *pdev, + const unsigned int index, const unsigned int signature) +{ + struct isi_board *board = pci_get_drvdata(pdev); + const struct firmware *fw; + unsigned long base = board->base; + unsigned int a; + u16 word_count, status; + int retval = -EIO; + char *name; + u8 *data; + + struct stframe { + u16 addr; + u16 count; + u8 data[0]; + } *frame; + + switch (signature) { + case 0xa5: + name = "isi608.bin"; + break; + case 0xbb: + name = "isi608em.bin"; + break; + case 0xcc: + name = "isi616em.bin"; + break; + case 0xdd: + name = "isi4608.bin"; + break; + case 0xee: + name = "isi4616.bin"; + break; + default: + dev_err(&pdev->dev, "Unknown signature.\n"); + goto end; + } + + retval = request_firmware(&fw, name, &pdev->dev); + if (retval) + goto end; + + for (frame = (struct stframe *)fw->data; + frame < (struct stframe *)(fw->data + fw->size); + frame++) { + if (WaitTillCardIsFree(base)) + goto errrelfw; + + outw(0xf0, base); /* start upload sequence */ + outw(0x00, base); + outw(frame->addr, base); /* lsb of address */ + + word_count = frame->count / 2 + frame->count % 2; + outw(word_count, base); + InterruptTheCard(base); + + udelay(100); /* 0x2f */ + + if (WaitTillCardIsFree(base)) + goto errrelfw; + + if ((status = inw(base + 0x4)) != 0) { + dev_warn(&pdev->dev, "Card%d rejected load header:\n" + "Address:0x%x\nCount:0x%x\nStatus:0x%x\n", + index + 1, frame->addr, frame->count, status); + goto errrelfw; + } + outsw(base, frame->data, word_count); + + InterruptTheCard(base); + + udelay(50); /* 0x0f */ + + if (WaitTillCardIsFree(base)) + goto errrelfw; + + if ((status = inw(base + 0x4)) != 0) { + dev_err(&pdev->dev, "Card%d got out of sync.Card " + "Status:0x%x\n", index + 1, status); + goto errrelfw; + } + } + + retval = -EIO; + + if (WaitTillCardIsFree(base)) + goto errrelfw; + + outw(0xf2, base); + outw(0x800, base); + outw(0x0, base); + outw(0x0, base); + InterruptTheCard(base); + outw(0x0, base + 0x4); /* for ISI4608 cards */ + +/* XXX: should we test it by reading it back and comparing with original like + * in load firmware package? */ + for (frame = (struct stframe*)fw->data; + frame < (struct stframe*)(fw->data + fw->size); + frame++) { + if (WaitTillCardIsFree(base)) + goto errrelfw; + + outw(0xf1, base); /* start download sequence */ + outw(0x00, base); + outw(frame->addr, base); /* lsb of address */ + + word_count = (frame->count >> 1) + frame->count % 2; + outw(word_count + 1, base); + InterruptTheCard(base); + + udelay(50); /* 0xf */ + + if (WaitTillCardIsFree(base)) + goto errrelfw; + + if ((status = inw(base + 0x4)) != 0) { + dev_warn(&pdev->dev, "Card%d rejected verify header:\n" + "Address:0x%x\nCount:0x%x\nStatus: 0x%x\n", + index + 1, frame->addr, frame->count, status); + goto errrelfw; + } + + data = kmalloc(word_count * 2, GFP_KERNEL); + inw(base); + insw(base, data, word_count); + InterruptTheCard(base); + + for (a = 0; a < frame->count; a++) + if (data[a] != frame->data[a]) { + kfree(data); + dev_err(&pdev->dev, "Card%d, firmware upload " + "failed\n", index + 1); + goto errrelfw; + } + kfree(data); + + udelay(50); /* 0xf */ + + if (WaitTillCardIsFree(base)) + goto errrelfw; + + if ((status = inw(base + 0x4)) != 0) { + dev_err(&pdev->dev, "Card%d verify got out of sync. " + "Card Status:0x%x\n", index + 1, status); + goto errrelfw; + } + } + + board->status |= FIRMWARE_LOADED; + retval = 0; + +errrelfw: + release_firmware(fw); +end: + return retval; +} + /* * Insmod can set static symbols so keep these static */ @@ -1976,6 +1916,10 @@ static int __devinit isicom_probe(struct pci_dev *pdev, if (retval < 0) goto errunri; + retval = load_firmware(pdev, index, signature); + if (retval < 0) + goto errunri; + return 0; errunri: @@ -2048,10 +1992,6 @@ static int __devinit isicom_setup(void) goto errtty; } - retval = misc_register(&isiloader_device); - if (retval < 0) - goto errpci; - init_timer(&tx); tx.expires = jiffies + 1; tx.data = 0; @@ -2060,8 +2000,6 @@ static int __devinit isicom_setup(void) add_timer(&tx); return 0; -errpci: - pci_unregister_driver(&isicom_driver); errtty: isicom_unregister_tty_driver(); error: |