[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>
diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c
index 9ef8ab3..55a47b3 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 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 @@
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 @@
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 @@
if (retval < 0)
goto errunri;
+ retval = load_firmware(pdev, index, signature);
+ if (retval < 0)
+ goto errunri;
+
return 0;
errunri:
@@ -2048,10 +1992,6 @@
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 @@
add_timer(&tx);
return 0;
-errpci:
- pci_unregister_driver(&isicom_driver);
errtty:
isicom_unregister_tty_driver();
error: