Commit e65c1db1 authored by Jiri Slaby's avatar Jiri Slaby Committed by Linus Torvalds

[PATCH] char/isicom: Firmware loading

Firmware loading via hotplug added.
Cleanup firmware old-way fields in header file.
Signed-off-by: default avatarJiri Slaby <xslaby@fi.muni.cz>
Cc: Greg KH <greg@kroah.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 9ac0948b
...@@ -112,6 +112,7 @@ ...@@ -112,6 +112,7 @@
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/firmware.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/tty.h> #include <linux/tty.h>
#include <linux/tty_flip.h> #include <linux/tty_flip.h>
...@@ -120,7 +121,6 @@ ...@@ -120,7 +121,6 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/serial.h> #include <linux/serial.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/delay.h> #include <linux/delay.h>
...@@ -175,8 +175,6 @@ static struct tty_driver *isicom_normal; ...@@ -175,8 +175,6 @@ static struct tty_driver *isicom_normal;
static struct timer_list tx; static struct timer_list tx;
static char re_schedule = 1; 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_tx(unsigned long _data);
static void isicom_start(struct tty_struct *tty); static void isicom_start(struct tty_struct *tty);
...@@ -384,233 +382,6 @@ static inline void kill_queue(struct isi_port *port, short queue) ...@@ -384,233 +382,6 @@ static inline void kill_queue(struct isi_port *port, short queue)
unlock_card(card); 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 ... * ISICOM Driver specific routines ...
* *
...@@ -1927,6 +1698,175 @@ end: ...@@ -1927,6 +1698,175 @@ end:
return retval; 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 * Insmod can set static symbols so keep these static
*/ */
...@@ -1976,6 +1916,10 @@ static int __devinit isicom_probe(struct pci_dev *pdev, ...@@ -1976,6 +1916,10 @@ static int __devinit isicom_probe(struct pci_dev *pdev,
if (retval < 0) if (retval < 0)
goto errunri; goto errunri;
retval = load_firmware(pdev, index, signature);
if (retval < 0)
goto errunri;
return 0; return 0;
errunri: errunri:
...@@ -2048,10 +1992,6 @@ static int __devinit isicom_setup(void) ...@@ -2048,10 +1992,6 @@ static int __devinit isicom_setup(void)
goto errtty; goto errtty;
} }
retval = misc_register(&isiloader_device);
if (retval < 0)
goto errpci;
init_timer(&tx); init_timer(&tx);
tx.expires = jiffies + 1; tx.expires = jiffies + 1;
tx.data = 0; tx.data = 0;
...@@ -2060,8 +2000,6 @@ static int __devinit isicom_setup(void) ...@@ -2060,8 +2000,6 @@ static int __devinit isicom_setup(void)
add_timer(&tx); add_timer(&tx);
return 0; return 0;
errpci:
pci_unregister_driver(&isicom_driver);
errtty: errtty:
isicom_unregister_tty_driver(); isicom_unregister_tty_driver();
error: error:
......
...@@ -4,46 +4,11 @@ ...@@ -4,46 +4,11 @@
/*#define ISICOM_DEBUG*/ /*#define ISICOM_DEBUG*/
/*#define ISICOM_DEBUG_DTR_RTS*/ /*#define ISICOM_DEBUG_DTR_RTS*/
/*
* Firmware Loader definitions ...
*/
#define __MultiTech ('M'<<8)
#define MIOCTL_LOAD_FIRMWARE (__MultiTech | 0x01)
#define MIOCTL_READ_FIRMWARE (__MultiTech | 0x02)
#define MIOCTL_XFER_CTRL (__MultiTech | 0x03)
#define MIOCTL_RESET_CARD (__MultiTech | 0x04)
#define DATA_SIZE 16
typedef struct {
unsigned short exec_segment;
unsigned short exec_addr;
} exec_record;
typedef struct {
int board; /* Board to load */
unsigned short addr;
unsigned short count;
} bin_header;
typedef struct {
int board; /* Board to load */
unsigned short addr;
unsigned short count;
unsigned short segment;
unsigned char bin_data[DATA_SIZE];
} bin_frame;
#ifdef __KERNEL__ #ifdef __KERNEL__
#define YES 1 #define YES 1
#define NO 0 #define NO 0
#define ISILOAD_MISC_MINOR 155 /* /dev/isctl */
#define ISILOAD_NAME "ISILoad"
/* /*
* ISICOM Driver definitions ... * ISICOM Driver definitions ...
* *
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment