diff options
author | Jiri Slaby <xslaby@fi.muni.cz> | 2006-01-09 23:54:25 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-01-10 11:02:01 -0500 |
commit | e65c1db19fe8177fa2da53e3e0bddffe585b2d47 (patch) | |
tree | 4d1c54f559812b71c9fde79383d00db908c1fd4f /drivers/char/isicom.c | |
parent | 9ac0948b20f76d9659add91f868c57383ea1e4e5 (diff) |
[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 @@ | |||
112 | */ | 112 | */ |
113 | 113 | ||
114 | #include <linux/module.h> | 114 | #include <linux/module.h> |
115 | #include <linux/firmware.h> | ||
115 | #include <linux/kernel.h> | 116 | #include <linux/kernel.h> |
116 | #include <linux/tty.h> | 117 | #include <linux/tty.h> |
117 | #include <linux/tty_flip.h> | 118 | #include <linux/tty_flip.h> |
@@ -120,7 +121,6 @@ | |||
120 | #include <linux/sched.h> | 121 | #include <linux/sched.h> |
121 | #include <linux/serial.h> | 122 | #include <linux/serial.h> |
122 | #include <linux/mm.h> | 123 | #include <linux/mm.h> |
123 | #include <linux/miscdevice.h> | ||
124 | #include <linux/interrupt.h> | 124 | #include <linux/interrupt.h> |
125 | #include <linux/timer.h> | 125 | #include <linux/timer.h> |
126 | #include <linux/delay.h> | 126 | #include <linux/delay.h> |
@@ -175,8 +175,6 @@ static struct tty_driver *isicom_normal; | |||
175 | static struct timer_list tx; | 175 | static struct timer_list tx; |
176 | static char re_schedule = 1; | 176 | static char re_schedule = 1; |
177 | 177 | ||
178 | static int ISILoad_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); | ||
179 | |||
180 | static void isicom_tx(unsigned long _data); | 178 | static void isicom_tx(unsigned long _data); |
181 | static void isicom_start(struct tty_struct *tty); | 179 | static void isicom_start(struct tty_struct *tty); |
182 | 180 | ||
@@ -384,233 +382,6 @@ static inline void kill_queue(struct isi_port *port, short queue) | |||
384 | unlock_card(card); | 382 | unlock_card(card); |
385 | } | 383 | } |
386 | 384 | ||
387 | |||
388 | /* | ||
389 | * Firmware loader driver specific routines. This needs to mostly die | ||
390 | * and be replaced with request_firmware. | ||
391 | */ | ||
392 | |||
393 | static struct file_operations ISILoad_fops = { | ||
394 | .owner = THIS_MODULE, | ||
395 | .ioctl = ISILoad_ioctl, | ||
396 | }; | ||
397 | |||
398 | static struct miscdevice isiloader_device = { | ||
399 | ISILOAD_MISC_MINOR, "isictl", &ISILoad_fops | ||
400 | }; | ||
401 | |||
402 | |||
403 | static inline int WaitTillCardIsFree(unsigned long base) | ||
404 | { | ||
405 | unsigned long count=0; | ||
406 | while( (!(inw(base+0xe) & 0x1)) && (count++ < 6000000)); | ||
407 | if (inw(base+0xe)&0x1) | ||
408 | return 0; | ||
409 | else | ||
410 | return 1; | ||
411 | } | ||
412 | |||
413 | static int ISILoad_ioctl(struct inode *inode, struct file *filp, | ||
414 | unsigned int cmd, unsigned long arg) | ||
415 | { | ||
416 | unsigned int card, i, j, signature, status, portcount = 0; | ||
417 | unsigned long t, base; | ||
418 | u16 word_count; | ||
419 | bin_frame frame; | ||
420 | void __user *argp = (void __user *)arg; | ||
421 | /* exec_record exec_rec; */ | ||
422 | |||
423 | if (get_user(card, (int __user *)argp)) | ||
424 | return -EFAULT; | ||
425 | |||
426 | if (card < 0 || card >= BOARD_COUNT) | ||
427 | return -ENXIO; | ||
428 | |||
429 | base=isi_card[card].base; | ||
430 | |||
431 | if (base==0) | ||
432 | return -ENXIO; /* disabled or not used */ | ||
433 | |||
434 | switch(cmd) { | ||
435 | case MIOCTL_RESET_CARD: | ||
436 | if (!capable(CAP_SYS_ADMIN)) | ||
437 | return -EPERM; | ||
438 | printk(KERN_DEBUG "ISILoad:Resetting Card%d at 0x%lx ",card+1,base); | ||
439 | |||
440 | inw(base+0x8); | ||
441 | |||
442 | for (t=jiffies+HZ/100;time_before(jiffies, t);); | ||
443 | |||
444 | outw(0,base+0x8); /* Reset */ | ||
445 | |||
446 | for (j=1;j<=3;j++) { | ||
447 | for (t=jiffies+HZ;time_before(jiffies, t);); | ||
448 | printk("."); | ||
449 | } | ||
450 | signature=(inw(base+0x4)) & 0xff; | ||
451 | if (isi_card[card].isa) { | ||
452 | |||
453 | if (!(inw(base+0xe) & 0x1) || (inw(base+0x2))) { | ||
454 | #ifdef ISICOM_DEBUG | ||
455 | printk("\nbase+0x2=0x%x , base+0xe=0x%x",inw(base+0x2),inw(base+0xe)); | ||
456 | #endif | ||
457 | printk("\nISILoad:ISA Card%d reset failure (Possible bad I/O Port Address 0x%lx).\n",card+1,base); | ||
458 | return -EIO; | ||
459 | } | ||
460 | } | ||
461 | else { | ||
462 | portcount = inw(base+0x2); | ||
463 | if (!(inw(base+0xe) & 0x1) || ((portcount!=0) && (portcount!=4) && (portcount!=8))) { | ||
464 | #ifdef ISICOM_DEBUG | ||
465 | printk("\nbase+0x2=0x%x , base+0xe=0x%x",inw(base+0x2),inw(base+0xe)); | ||
466 | #endif | ||
467 | printk("\nISILoad:PCI Card%d reset failure (Possible bad I/O Port Address 0x%lx).\n",card+1,base); | ||
468 | return -EIO; | ||
469 | } | ||
470 | } | ||
471 | switch(signature) { | ||
472 | case 0xa5: | ||
473 | case 0xbb: | ||
474 | case 0xdd: | ||
475 | if (isi_card[card].isa) | ||
476 | isi_card[card].port_count = 8; | ||
477 | else { | ||
478 | if (portcount == 4) | ||
479 | isi_card[card].port_count = 4; | ||
480 | else | ||
481 | isi_card[card].port_count = 8; | ||
482 | } | ||
483 | isi_card[card].shift_count = 12; | ||
484 | break; | ||
485 | |||
486 | case 0xcc: isi_card[card].port_count = 16; | ||
487 | isi_card[card].shift_count = 11; | ||
488 | break; | ||
489 | |||
490 | default: printk("ISILoad:Card%d reset failure (Possible bad I/O Port Address 0x%lx).\n",card+1,base); | ||
491 | #ifdef ISICOM_DEBUG | ||
492 | printk("Sig=0x%x\n",signature); | ||
493 | #endif | ||
494 | return -EIO; | ||
495 | } | ||
496 | printk("-Done\n"); | ||
497 | return put_user(signature,(unsigned __user *)argp); | ||
498 | |||
499 | case MIOCTL_LOAD_FIRMWARE: | ||
500 | if (!capable(CAP_SYS_ADMIN)) | ||
501 | return -EPERM; | ||
502 | |||
503 | if (copy_from_user(&frame, argp, sizeof(bin_frame))) | ||
504 | return -EFAULT; | ||
505 | |||
506 | if (WaitTillCardIsFree(base)) | ||
507 | return -EIO; | ||
508 | |||
509 | outw(0xf0,base); /* start upload sequence */ | ||
510 | outw(0x00,base); | ||
511 | outw((frame.addr), base); /* lsb of adderess */ | ||
512 | |||
513 | word_count=(frame.count >> 1) + frame.count % 2; | ||
514 | outw(word_count, base); | ||
515 | InterruptTheCard(base); | ||
516 | |||
517 | for (i=0;i<=0x2f;i++); /* a wee bit of delay */ | ||
518 | |||
519 | if (WaitTillCardIsFree(base)) | ||
520 | return -EIO; | ||
521 | |||
522 | if ((status=inw(base+0x4))!=0) { | ||
523 | printk(KERN_WARNING "ISILoad:Card%d rejected load header:\nAddress:0x%x \nCount:0x%x \nStatus:0x%x \n", | ||
524 | card+1, frame.addr, frame.count, status); | ||
525 | return -EIO; | ||
526 | } | ||
527 | outsw(base, (void *) frame.bin_data, word_count); | ||
528 | |||
529 | InterruptTheCard(base); | ||
530 | |||
531 | for (i=0;i<=0x0f;i++); /* another wee bit of delay */ | ||
532 | |||
533 | if (WaitTillCardIsFree(base)) | ||
534 | return -EIO; | ||
535 | |||
536 | if ((status=inw(base+0x4))!=0) { | ||
537 | printk(KERN_ERR "ISILoad:Card%d got out of sync.Card Status:0x%x\n",card+1, status); | ||
538 | return -EIO; | ||
539 | } | ||
540 | return 0; | ||
541 | |||
542 | case MIOCTL_READ_FIRMWARE: | ||
543 | if (!capable(CAP_SYS_ADMIN)) | ||
544 | return -EPERM; | ||
545 | |||
546 | if (copy_from_user(&frame, argp, sizeof(bin_header))) | ||
547 | return -EFAULT; | ||
548 | |||
549 | if (WaitTillCardIsFree(base)) | ||
550 | return -EIO; | ||
551 | |||
552 | outw(0xf1,base); /* start download sequence */ | ||
553 | outw(0x00,base); | ||
554 | outw((frame.addr), base); /* lsb of adderess */ | ||
555 | |||
556 | word_count=(frame.count >> 1) + frame.count % 2; | ||
557 | outw(word_count+1, base); | ||
558 | InterruptTheCard(base); | ||
559 | |||
560 | for (i=0;i<=0xf;i++); /* a wee bit of delay */ | ||
561 | |||
562 | if (WaitTillCardIsFree(base)) | ||
563 | return -EIO; | ||
564 | |||
565 | if ((status=inw(base+0x4))!=0) { | ||
566 | printk(KERN_WARNING "ISILoad:Card%d rejected verify header:\nAddress:0x%x \nCount:0x%x \nStatus:0x%x \n", | ||
567 | card+1, frame.addr, frame.count, status); | ||
568 | return -EIO; | ||
569 | } | ||
570 | |||
571 | inw(base); | ||
572 | insw(base, frame.bin_data, word_count); | ||
573 | InterruptTheCard(base); | ||
574 | |||
575 | for (i=0;i<=0x0f;i++); /* another wee bit of delay */ | ||
576 | |||
577 | if (WaitTillCardIsFree(base)) | ||
578 | return -EIO; | ||
579 | |||
580 | if ((status=inw(base+0x4))!=0) { | ||
581 | printk(KERN_ERR "ISILoad:Card%d verify got out of sync.Card Status:0x%x\n",card+1, status); | ||
582 | return -EIO; | ||
583 | } | ||
584 | |||
585 | if (copy_to_user(argp, &frame, sizeof(bin_frame))) | ||
586 | return -EFAULT; | ||
587 | return 0; | ||
588 | |||
589 | case MIOCTL_XFER_CTRL: | ||
590 | if (!capable(CAP_SYS_ADMIN)) | ||
591 | return -EPERM; | ||
592 | if (WaitTillCardIsFree(base)) | ||
593 | return -EIO; | ||
594 | |||
595 | outw(0xf2, base); | ||
596 | outw(0x800, base); | ||
597 | outw(0x0, base); | ||
598 | outw(0x0, base); | ||
599 | InterruptTheCard(base); | ||
600 | outw(0x0, base+0x4); /* for ISI4608 cards */ | ||
601 | |||
602 | isi_card[card].status |= FIRMWARE_LOADED; | ||
603 | return 0; | ||
604 | |||
605 | default: | ||
606 | #ifdef ISICOM_DEBUG | ||
607 | printk(KERN_DEBUG "ISILoad: Received Ioctl cmd 0x%x.\n", cmd); | ||
608 | #endif | ||
609 | return -ENOIOCTLCMD; | ||
610 | } | ||
611 | } | ||
612 | |||
613 | |||
614 | /* | 385 | /* |
615 | * ISICOM Driver specific routines ... | 386 | * ISICOM Driver specific routines ... |
616 | * | 387 | * |
@@ -1927,6 +1698,175 @@ end: | |||
1927 | return retval; | 1698 | return retval; |
1928 | } | 1699 | } |
1929 | 1700 | ||
1701 | static inline int WaitTillCardIsFree(u16 base) | ||
1702 | { | ||
1703 | unsigned long count = 0; | ||
1704 | |||
1705 | while (!(inw(base + 0xe) & 0x1) && count++ < 100) | ||
1706 | msleep(5); | ||
1707 | |||
1708 | return !(inw(base + 0xe) & 0x1); | ||
1709 | } | ||
1710 | |||
1711 | static int __devinit load_firmware(struct pci_dev *pdev, | ||
1712 | const unsigned int index, const unsigned int signature) | ||
1713 | { | ||
1714 | struct isi_board *board = pci_get_drvdata(pdev); | ||
1715 | const struct firmware *fw; | ||
1716 | unsigned long base = board->base; | ||
1717 | unsigned int a; | ||
1718 | u16 word_count, status; | ||
1719 | int retval = -EIO; | ||
1720 | char *name; | ||
1721 | u8 *data; | ||
1722 | |||
1723 | struct stframe { | ||
1724 | u16 addr; | ||
1725 | u16 count; | ||
1726 | u8 data[0]; | ||
1727 | } *frame; | ||
1728 | |||
1729 | switch (signature) { | ||
1730 | case 0xa5: | ||
1731 | name = "isi608.bin"; | ||
1732 | break; | ||
1733 | case 0xbb: | ||
1734 | name = "isi608em.bin"; | ||
1735 | break; | ||
1736 | case 0xcc: | ||
1737 | name = "isi616em.bin"; | ||
1738 | break; | ||
1739 | case 0xdd: | ||
1740 | name = "isi4608.bin"; | ||
1741 | break; | ||
1742 | case 0xee: | ||
1743 | name = "isi4616.bin"; | ||
1744 | break; | ||
1745 | default: | ||
1746 | dev_err(&pdev->dev, "Unknown signature.\n"); | ||
1747 | goto end; | ||
1748 | } | ||
1749 | |||
1750 | retval = request_firmware(&fw, name, &pdev->dev); | ||
1751 | if (retval) | ||
1752 | goto end; | ||
1753 | |||
1754 | for (frame = (struct stframe *)fw->data; | ||
1755 | frame < (struct stframe *)(fw->data + fw->size); | ||
1756 | frame++) { | ||
1757 | if (WaitTillCardIsFree(base)) | ||
1758 | goto errrelfw; | ||
1759 | |||
1760 | outw(0xf0, base); /* start upload sequence */ | ||
1761 | outw(0x00, base); | ||
1762 | outw(frame->addr, base); /* lsb of address */ | ||
1763 | |||
1764 | word_count = frame->count / 2 + frame->count % 2; | ||
1765 | outw(word_count, base); | ||
1766 | InterruptTheCard(base); | ||
1767 | |||
1768 | udelay(100); /* 0x2f */ | ||
1769 | |||
1770 | if (WaitTillCardIsFree(base)) | ||
1771 | goto errrelfw; | ||
1772 | |||
1773 | if ((status = inw(base + 0x4)) != 0) { | ||
1774 | dev_warn(&pdev->dev, "Card%d rejected load header:\n" | ||
1775 | "Address:0x%x\nCount:0x%x\nStatus:0x%x\n", | ||
1776 | index + 1, frame->addr, frame->count, status); | ||
1777 | goto errrelfw; | ||
1778 | } | ||
1779 | outsw(base, frame->data, word_count); | ||
1780 | |||
1781 | InterruptTheCard(base); | ||
1782 | |||
1783 | udelay(50); /* 0x0f */ | ||
1784 | |||
1785 | if (WaitTillCardIsFree(base)) | ||
1786 | goto errrelfw; | ||
1787 | |||
1788 | if ((status = inw(base + 0x4)) != 0) { | ||
1789 | dev_err(&pdev->dev, "Card%d got out of sync.Card " | ||
1790 | "Status:0x%x\n", index + 1, status); | ||
1791 | goto errrelfw; | ||
1792 | } | ||
1793 | } | ||
1794 | |||
1795 | retval = -EIO; | ||
1796 | |||
1797 | if (WaitTillCardIsFree(base)) | ||
1798 | goto errrelfw; | ||
1799 | |||
1800 | outw(0xf2, base); | ||
1801 | outw(0x800, base); | ||
1802 | outw(0x0, base); | ||
1803 | outw(0x0, base); | ||
1804 | InterruptTheCard(base); | ||
1805 | outw(0x0, base + 0x4); /* for ISI4608 cards */ | ||
1806 | |||
1807 | /* XXX: should we test it by reading it back and comparing with original like | ||
1808 | * in load firmware package? */ | ||
1809 | for (frame = (struct stframe*)fw->data; | ||
1810 | frame < (struct stframe*)(fw->data + fw->size); | ||
1811 | frame++) { | ||
1812 | if (WaitTillCardIsFree(base)) | ||
1813 | goto errrelfw; | ||
1814 | |||
1815 | outw(0xf1, base); /* start download sequence */ | ||
1816 | outw(0x00, base); | ||
1817 | outw(frame->addr, base); /* lsb of address */ | ||
1818 | |||
1819 | word_count = (frame->count >> 1) + frame->count % 2; | ||
1820 | outw(word_count + 1, base); | ||
1821 | InterruptTheCard(base); | ||
1822 | |||
1823 | udelay(50); /* 0xf */ | ||
1824 | |||
1825 | if (WaitTillCardIsFree(base)) | ||
1826 | goto errrelfw; | ||
1827 | |||
1828 | if ((status = inw(base + 0x4)) != 0) { | ||
1829 | dev_warn(&pdev->dev, "Card%d rejected verify header:\n" | ||
1830 | "Address:0x%x\nCount:0x%x\nStatus: 0x%x\n", | ||
1831 | index + 1, frame->addr, frame->count, status); | ||
1832 | goto errrelfw; | ||
1833 | } | ||
1834 | |||
1835 | data = kmalloc(word_count * 2, GFP_KERNEL); | ||
1836 | inw(base); | ||
1837 | insw(base, data, word_count); | ||
1838 | InterruptTheCard(base); | ||
1839 | |||
1840 | for (a = 0; a < frame->count; a++) | ||
1841 | if (data[a] != frame->data[a]) { | ||
1842 | kfree(data); | ||
1843 | dev_err(&pdev->dev, "Card%d, firmware upload " | ||
1844 | "failed\n", index + 1); | ||
1845 | goto errrelfw; | ||
1846 | } | ||
1847 | kfree(data); | ||
1848 | |||
1849 | udelay(50); /* 0xf */ | ||
1850 | |||
1851 | if (WaitTillCardIsFree(base)) | ||
1852 | goto errrelfw; | ||
1853 | |||
1854 | if ((status = inw(base + 0x4)) != 0) { | ||
1855 | dev_err(&pdev->dev, "Card%d verify got out of sync. " | ||
1856 | "Card Status:0x%x\n", index + 1, status); | ||
1857 | goto errrelfw; | ||
1858 | } | ||
1859 | } | ||
1860 | |||
1861 | board->status |= FIRMWARE_LOADED; | ||
1862 | retval = 0; | ||
1863 | |||
1864 | errrelfw: | ||
1865 | release_firmware(fw); | ||
1866 | end: | ||
1867 | return retval; | ||
1868 | } | ||
1869 | |||
1930 | /* | 1870 | /* |
1931 | * Insmod can set static symbols so keep these static | 1871 | * Insmod can set static symbols so keep these static |
1932 | */ | 1872 | */ |
@@ -1976,6 +1916,10 @@ static int __devinit isicom_probe(struct pci_dev *pdev, | |||
1976 | if (retval < 0) | 1916 | if (retval < 0) |
1977 | goto errunri; | 1917 | goto errunri; |
1978 | 1918 | ||
1919 | retval = load_firmware(pdev, index, signature); | ||
1920 | if (retval < 0) | ||
1921 | goto errunri; | ||
1922 | |||
1979 | return 0; | 1923 | return 0; |
1980 | 1924 | ||
1981 | errunri: | 1925 | errunri: |
@@ -2048,10 +1992,6 @@ static int __devinit isicom_setup(void) | |||
2048 | goto errtty; | 1992 | goto errtty; |
2049 | } | 1993 | } |
2050 | 1994 | ||
2051 | retval = misc_register(&isiloader_device); | ||
2052 | if (retval < 0) | ||
2053 | goto errpci; | ||
2054 | |||
2055 | init_timer(&tx); | 1995 | init_timer(&tx); |
2056 | tx.expires = jiffies + 1; | 1996 | tx.expires = jiffies + 1; |
2057 | tx.data = 0; | 1997 | tx.data = 0; |
@@ -2060,8 +2000,6 @@ static int __devinit isicom_setup(void) | |||
2060 | add_timer(&tx); | 2000 | add_timer(&tx); |
2061 | 2001 | ||
2062 | return 0; | 2002 | return 0; |
2063 | errpci: | ||
2064 | pci_unregister_driver(&isicom_driver); | ||
2065 | errtty: | 2003 | errtty: |
2066 | isicom_unregister_tty_driver(); | 2004 | isicom_unregister_tty_driver(); |
2067 | error: | 2005 | error: |