diff options
author | Jiri Slaby <jirislaby@gmail.com> | 2007-07-17 07:05:16 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-17 13:23:10 -0400 |
commit | 054f5b0aaa58dfc841635e52b6c1cc2b14ec37fc (patch) | |
tree | f2356ddebc9e11d1eb5c178d4443dcf3e9612088 /drivers | |
parent | cf3a386c083c0cc4eb9f01a8818b9499ae56e73f (diff) |
Char: cyclades, add firmware loading
cyclades, add firmware loading
Signed-off-by: Jiri Slaby <jirislaby@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/char/cyclades.c | 351 |
1 files changed, 328 insertions, 23 deletions
diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index e04005b5f8a6..b9bd4f67efb4 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c | |||
@@ -646,6 +646,7 @@ | |||
646 | #include <linux/delay.h> | 646 | #include <linux/delay.h> |
647 | #include <linux/spinlock.h> | 647 | #include <linux/spinlock.h> |
648 | #include <linux/bitops.h> | 648 | #include <linux/bitops.h> |
649 | #include <linux/firmware.h> | ||
649 | 650 | ||
650 | #include <asm/system.h> | 651 | #include <asm/system.h> |
651 | #include <asm/io.h> | 652 | #include <asm/io.h> |
@@ -680,6 +681,44 @@ static void cy_send_xchar(struct tty_struct *tty, char ch); | |||
680 | 681 | ||
681 | #define STD_COM_FLAGS (0) | 682 | #define STD_COM_FLAGS (0) |
682 | 683 | ||
684 | /* firmware stuff */ | ||
685 | #define ZL_MAX_BLOCKS 16 | ||
686 | #define DRIVER_VERSION 0x02010203 | ||
687 | #define RAM_SIZE 0x80000 | ||
688 | |||
689 | #define Z_FPGA_LOADED(X) ((readl(&(X)->init_ctrl) & (1<<17)) != 0) | ||
690 | |||
691 | enum zblock_type { | ||
692 | ZBLOCK_PRG = 0, | ||
693 | ZBLOCK_FPGA = 1 | ||
694 | }; | ||
695 | |||
696 | struct zfile_header { | ||
697 | char name[64]; | ||
698 | char date[32]; | ||
699 | char aux[32]; | ||
700 | u32 n_config; | ||
701 | u32 config_offset; | ||
702 | u32 n_blocks; | ||
703 | u32 block_offset; | ||
704 | u32 reserved[9]; | ||
705 | } __attribute__ ((packed)); | ||
706 | |||
707 | struct zfile_config { | ||
708 | char name[64]; | ||
709 | u32 mailbox; | ||
710 | u32 function; | ||
711 | u32 n_blocks; | ||
712 | u32 block_list[ZL_MAX_BLOCKS]; | ||
713 | } __attribute__ ((packed)); | ||
714 | |||
715 | struct zfile_block { | ||
716 | u32 type; | ||
717 | u32 file_offset; | ||
718 | u32 ram_offset; | ||
719 | u32 size; | ||
720 | } __attribute__ ((packed)); | ||
721 | |||
683 | static struct tty_driver *cy_serial_driver; | 722 | static struct tty_driver *cy_serial_driver; |
684 | 723 | ||
685 | #ifdef CONFIG_ISA | 724 | #ifdef CONFIG_ISA |
@@ -4735,17 +4774,295 @@ static int __init cy_detect_isa(void) | |||
4735 | } /* cy_detect_isa */ | 4774 | } /* cy_detect_isa */ |
4736 | 4775 | ||
4737 | #ifdef CONFIG_PCI | 4776 | #ifdef CONFIG_PCI |
4738 | static void __devinit plx_init(void __iomem * addr, __u32 initctl) | 4777 | static inline int __devinit cyc_isfwstr(const char *str, unsigned int size) |
4778 | { | ||
4779 | unsigned int a; | ||
4780 | |||
4781 | for (a = 0; a < size && *str; a++, str++) | ||
4782 | if (*str & 0x80) | ||
4783 | return -EINVAL; | ||
4784 | |||
4785 | for (; a < size; a++, str++) | ||
4786 | if (*str) | ||
4787 | return -EINVAL; | ||
4788 | |||
4789 | return 0; | ||
4790 | } | ||
4791 | |||
4792 | static inline void __devinit cyz_fpga_copy(void __iomem *fpga, u8 *data, | ||
4793 | unsigned int size) | ||
4794 | { | ||
4795 | for (; size > 0; size--) { | ||
4796 | cy_writel(fpga, *data++); | ||
4797 | udelay(10); | ||
4798 | } | ||
4799 | } | ||
4800 | |||
4801 | static void __devinit plx_init(struct pci_dev *pdev, int irq, | ||
4802 | struct RUNTIME_9060 __iomem *addr) | ||
4739 | { | 4803 | { |
4740 | /* Reset PLX */ | 4804 | /* Reset PLX */ |
4741 | cy_writel(addr + initctl, readl(addr + initctl) | 0x40000000); | 4805 | cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) | 0x40000000); |
4742 | udelay(100L); | 4806 | udelay(100L); |
4743 | cy_writel(addr + initctl, readl(addr + initctl) & ~0x40000000); | 4807 | cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) & ~0x40000000); |
4744 | 4808 | ||
4745 | /* Reload Config. Registers from EEPROM */ | 4809 | /* Reload Config. Registers from EEPROM */ |
4746 | cy_writel(addr + initctl, readl(addr + initctl) | 0x20000000); | 4810 | cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) | 0x20000000); |
4747 | udelay(100L); | 4811 | udelay(100L); |
4748 | cy_writel(addr + initctl, readl(addr + initctl) & ~0x20000000); | 4812 | cy_writel(&addr->init_ctrl, readl(&addr->init_ctrl) & ~0x20000000); |
4813 | |||
4814 | /* For some yet unknown reason, once the PLX9060 reloads the EEPROM, | ||
4815 | * the IRQ is lost and, thus, we have to re-write it to the PCI config. | ||
4816 | * registers. This will remain here until we find a permanent fix. | ||
4817 | */ | ||
4818 | pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, irq); | ||
4819 | } | ||
4820 | |||
4821 | static int __devinit __cyz_load_fw(const struct firmware *fw, | ||
4822 | const char *name, const u32 mailbox, void __iomem *base, | ||
4823 | void __iomem *fpga) | ||
4824 | { | ||
4825 | void *ptr = fw->data; | ||
4826 | struct zfile_header *h = ptr; | ||
4827 | struct zfile_config *c, *cs; | ||
4828 | struct zfile_block *b, *bs; | ||
4829 | unsigned int a, tmp, len = fw->size; | ||
4830 | #define BAD_FW KERN_ERR "Bad firmware: " | ||
4831 | if (len < sizeof(*h)) { | ||
4832 | printk(BAD_FW "too short: %u<%zu\n", len, sizeof(*h)); | ||
4833 | return -EINVAL; | ||
4834 | } | ||
4835 | |||
4836 | cs = ptr + h->config_offset; | ||
4837 | bs = ptr + h->block_offset; | ||
4838 | |||
4839 | if ((void *)(cs + h->n_config) > ptr + len || | ||
4840 | (void *)(bs + h->n_blocks) > ptr + len) { | ||
4841 | printk(BAD_FW "too short"); | ||
4842 | return -EINVAL; | ||
4843 | } | ||
4844 | |||
4845 | if (cyc_isfwstr(h->name, sizeof(h->name)) || | ||
4846 | cyc_isfwstr(h->date, sizeof(h->date))) { | ||
4847 | printk(BAD_FW "bad formatted header string\n"); | ||
4848 | return -EINVAL; | ||
4849 | } | ||
4850 | |||
4851 | if (strncmp(name, h->name, sizeof(h->name))) { | ||
4852 | printk(BAD_FW "bad name '%s' (expected '%s')\n", h->name, name); | ||
4853 | return -EINVAL; | ||
4854 | } | ||
4855 | |||
4856 | tmp = 0; | ||
4857 | for (c = cs; c < cs + h->n_config; c++) { | ||
4858 | for (a = 0; a < c->n_blocks; a++) | ||
4859 | if (c->block_list[a] > h->n_blocks) { | ||
4860 | printk(BAD_FW "bad block ref number in cfgs\n"); | ||
4861 | return -EINVAL; | ||
4862 | } | ||
4863 | if (c->mailbox == mailbox && c->function == 0) /* 0 is normal */ | ||
4864 | tmp++; | ||
4865 | } | ||
4866 | if (!tmp) { | ||
4867 | printk(BAD_FW "nothing appropriate\n"); | ||
4868 | return -EINVAL; | ||
4869 | } | ||
4870 | |||
4871 | for (b = bs; b < bs + h->n_blocks; b++) | ||
4872 | if (b->file_offset + b->size > len) { | ||
4873 | printk(BAD_FW "bad block data offset\n"); | ||
4874 | return -EINVAL; | ||
4875 | } | ||
4876 | |||
4877 | /* everything is OK, let's seek'n'load it */ | ||
4878 | for (c = cs; c < cs + h->n_config; c++) | ||
4879 | if (c->mailbox == mailbox && c->function == 0) | ||
4880 | break; | ||
4881 | |||
4882 | for (a = 0; a < c->n_blocks; a++) { | ||
4883 | b = &bs[c->block_list[a]]; | ||
4884 | if (b->type == ZBLOCK_FPGA) { | ||
4885 | if (fpga != NULL) | ||
4886 | cyz_fpga_copy(fpga, ptr + b->file_offset, | ||
4887 | b->size); | ||
4888 | } else { | ||
4889 | if (base != NULL) | ||
4890 | memcpy_toio(base + b->ram_offset, | ||
4891 | ptr + b->file_offset, b->size); | ||
4892 | } | ||
4893 | } | ||
4894 | #undef BAD_FW | ||
4895 | return 0; | ||
4896 | } | ||
4897 | |||
4898 | static int __devinit cyz_load_fw(struct pci_dev *pdev, void __iomem *base_addr, | ||
4899 | struct RUNTIME_9060 __iomem *ctl_addr, int irq) | ||
4900 | { | ||
4901 | const struct firmware *fw; | ||
4902 | struct FIRM_ID __iomem *fid = base_addr + ID_ADDRESS; | ||
4903 | struct CUSTOM_REG __iomem *cust = base_addr; | ||
4904 | struct ZFW_CTRL __iomem *pt_zfwctrl; | ||
4905 | u8 *tmp; | ||
4906 | u32 mailbox, status; | ||
4907 | unsigned int i; | ||
4908 | int retval; | ||
4909 | |||
4910 | retval = request_firmware(&fw, "cyzfirm.bin", &pdev->dev); | ||
4911 | if (retval) { | ||
4912 | dev_err(&pdev->dev, "can't get firmware\n"); | ||
4913 | goto err; | ||
4914 | } | ||
4915 | |||
4916 | /* Check whether the firmware is already loaded and running. If | ||
4917 | positive, skip this board */ | ||
4918 | if (Z_FPGA_LOADED(ctl_addr) && readl(&fid->signature) == ZFIRM_ID) { | ||
4919 | u32 cntval = readl(base_addr + 0x190); | ||
4920 | |||
4921 | udelay(100); | ||
4922 | if (cntval != readl(base_addr + 0x190)) { | ||
4923 | /* FW counter is working, FW is running */ | ||
4924 | dev_dbg(&pdev->dev, "Cyclades-Z FW already loaded. " | ||
4925 | "Skipping board.\n"); | ||
4926 | retval = 0; | ||
4927 | goto err_rel; | ||
4928 | } | ||
4929 | } | ||
4930 | |||
4931 | /* start boot */ | ||
4932 | cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) & | ||
4933 | ~0x00030800UL); | ||
4934 | |||
4935 | mailbox = readl(&ctl_addr->mail_box_0); | ||
4936 | |||
4937 | if (mailbox == 0 || Z_FPGA_LOADED(ctl_addr)) { | ||
4938 | /* stops CPU and set window to beginning of RAM */ | ||
4939 | cy_writel(&ctl_addr->loc_addr_base, WIN_CREG); | ||
4940 | cy_writel(&cust->cpu_stop, 0); | ||
4941 | cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); | ||
4942 | udelay(100); | ||
4943 | } | ||
4944 | |||
4945 | plx_init(pdev, irq, ctl_addr); | ||
4946 | |||
4947 | if (mailbox != 0) { | ||
4948 | /* load FPGA */ | ||
4949 | retval = __cyz_load_fw(fw, "Cyclom-Z", mailbox, NULL, | ||
4950 | base_addr); | ||
4951 | if (retval) | ||
4952 | goto err_rel; | ||
4953 | if (!Z_FPGA_LOADED(ctl_addr)) { | ||
4954 | dev_err(&pdev->dev, "fw upload successful, but fw is " | ||
4955 | "not loaded\n"); | ||
4956 | goto err_rel; | ||
4957 | } | ||
4958 | } | ||
4959 | |||
4960 | /* stops CPU and set window to beginning of RAM */ | ||
4961 | cy_writel(&ctl_addr->loc_addr_base, WIN_CREG); | ||
4962 | cy_writel(&cust->cpu_stop, 0); | ||
4963 | cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); | ||
4964 | udelay(100); | ||
4965 | |||
4966 | /* clear memory */ | ||
4967 | for (tmp = base_addr; (void *)tmp < base_addr + RAM_SIZE; tmp++) | ||
4968 | cy_writeb(tmp, 255); | ||
4969 | if (mailbox != 0) { | ||
4970 | /* set window to last 512K of RAM */ | ||
4971 | cy_writel(&ctl_addr->loc_addr_base, WIN_RAM + RAM_SIZE); | ||
4972 | //sleep(1); | ||
4973 | for (tmp = base_addr; (void *)tmp < base_addr + RAM_SIZE; tmp++) | ||
4974 | cy_writeb(tmp, 255); | ||
4975 | /* set window to beginning of RAM */ | ||
4976 | cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); | ||
4977 | //sleep(1); | ||
4978 | } | ||
4979 | |||
4980 | retval = __cyz_load_fw(fw, "Cyclom-Z", mailbox, base_addr, NULL); | ||
4981 | release_firmware(fw); | ||
4982 | if (retval) | ||
4983 | goto err; | ||
4984 | |||
4985 | /* finish boot and start boards */ | ||
4986 | cy_writel(&ctl_addr->loc_addr_base, WIN_CREG); | ||
4987 | cy_writel(&cust->cpu_start, 0); | ||
4988 | cy_writel(&ctl_addr->loc_addr_base, WIN_RAM); | ||
4989 | i = 0; | ||
4990 | while ((status = readl(&fid->signature)) != ZFIRM_ID && i++ < 40) | ||
4991 | msleep(100); | ||
4992 | if (status != ZFIRM_ID) { | ||
4993 | if (status == ZFIRM_HLT) { | ||
4994 | dev_err(&pdev->dev, "you need an external power supply " | ||
4995 | "for this number of ports. Firmware halted and " | ||
4996 | "board reset.\n"); | ||
4997 | retval = -EIO; | ||
4998 | goto err; | ||
4999 | } | ||
5000 | dev_warn(&pdev->dev, "fid->signature = 0x%x... Waiting " | ||
5001 | "some more time\n", status); | ||
5002 | while ((status = readl(&fid->signature)) != ZFIRM_ID && | ||
5003 | i++ < 200) | ||
5004 | msleep(100); | ||
5005 | if (status != ZFIRM_ID) { | ||
5006 | dev_err(&pdev->dev, "Board not started in 20 seconds! " | ||
5007 | "Giving up. (fid->signature = 0x%x)\n", | ||
5008 | status); | ||
5009 | dev_info(&pdev->dev, "*** Warning ***: if you are " | ||
5010 | "upgrading the FW, please power cycle the " | ||
5011 | "system before loading the new FW to the " | ||
5012 | "Cyclades-Z.\n"); | ||
5013 | |||
5014 | if (Z_FPGA_LOADED(ctl_addr)) | ||
5015 | plx_init(pdev, irq, ctl_addr); | ||
5016 | |||
5017 | retval = -EIO; | ||
5018 | goto err; | ||
5019 | } | ||
5020 | dev_dbg(&pdev->dev, "Firmware started after %d seconds.\n", | ||
5021 | i / 10); | ||
5022 | } | ||
5023 | pt_zfwctrl = base_addr + readl(&fid->zfwctrl_addr); | ||
5024 | |||
5025 | dev_dbg(&pdev->dev, "fid=> %p, zfwctrl_addr=> %x, npt_zfwctrl=> %p\n", | ||
5026 | base_addr + ID_ADDRESS, readl(&fid->zfwctrl_addr), | ||
5027 | base_addr + readl(&fid->zfwctrl_addr)); | ||
5028 | |||
5029 | dev_info(&pdev->dev, "Cyclades-Z FW loaded: version = %x, ports = %u\n", | ||
5030 | readl(&pt_zfwctrl->board_ctrl.fw_version), | ||
5031 | readl(&pt_zfwctrl->board_ctrl.n_channel)); | ||
5032 | |||
5033 | if (readl(&pt_zfwctrl->board_ctrl.n_channel) == 0) { | ||
5034 | dev_warn(&pdev->dev, "no Cyclades-Z ports were found. Please " | ||
5035 | "check the connection between the Z host card and the " | ||
5036 | "serial expanders.\n"); | ||
5037 | |||
5038 | if (Z_FPGA_LOADED(ctl_addr)) | ||
5039 | plx_init(pdev, irq, ctl_addr); | ||
5040 | |||
5041 | dev_info(&pdev->dev, "Null number of ports detected. Board " | ||
5042 | "reset.\n"); | ||
5043 | retval = 0; | ||
5044 | goto err; | ||
5045 | } | ||
5046 | |||
5047 | cy_writel(&pt_zfwctrl->board_ctrl.op_system, C_OS_LINUX); | ||
5048 | cy_writel(&pt_zfwctrl->board_ctrl.dr_version, DRIVER_VERSION); | ||
5049 | |||
5050 | /* | ||
5051 | Early firmware failed to start looking for commands. | ||
5052 | This enables firmware interrupts for those commands. | ||
5053 | */ | ||
5054 | cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) | | ||
5055 | (1 << 17)); | ||
5056 | cy_writel(&ctl_addr->intr_ctrl_stat, readl(&ctl_addr->intr_ctrl_stat) | | ||
5057 | 0x00030800UL); | ||
5058 | |||
5059 | plx_init(pdev, irq, ctl_addr); | ||
5060 | |||
5061 | return 0; | ||
5062 | err_rel: | ||
5063 | release_firmware(fw); | ||
5064 | err: | ||
5065 | return retval; | ||
4749 | } | 5066 | } |
4750 | 5067 | ||
4751 | static int __devinit cy_pci_probe(struct pci_dev *pdev, | 5068 | static int __devinit cy_pci_probe(struct pci_dev *pdev, |
@@ -4827,16 +5144,9 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev, | |||
4827 | } | 5144 | } |
4828 | 5145 | ||
4829 | /* Disable interrupts on the PLX before resetting it */ | 5146 | /* Disable interrupts on the PLX before resetting it */ |
4830 | cy_writew(addr0 + 0x68, | 5147 | cy_writew(addr0 + 0x68, readw(addr0 + 0x68) & ~0x0900); |
4831 | readw(addr0 + 0x68) & ~0x0900); | ||
4832 | 5148 | ||
4833 | plx_init(addr0, 0x6c); | 5149 | plx_init(pdev, irq, addr0); |
4834 | /* For some yet unknown reason, once the PLX9060 reloads | ||
4835 | the EEPROM, the IRQ is lost and, thus, we have to | ||
4836 | re-write it to the PCI config. registers. | ||
4837 | This will remain here until we find a permanent | ||
4838 | fix. */ | ||
4839 | pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, irq); | ||
4840 | 5150 | ||
4841 | mailbox = (u32)readl(&ctl_addr->mail_box_0); | 5151 | mailbox = (u32)readl(&ctl_addr->mail_box_0); |
4842 | 5152 | ||
@@ -4877,6 +5187,9 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev, | |||
4877 | if ((mailbox == ZO_V1) || (mailbox == ZO_V2)) | 5187 | if ((mailbox == ZO_V1) || (mailbox == ZO_V2)) |
4878 | cy_writel(addr2 + ID_ADDRESS, 0L); | 5188 | cy_writel(addr2 + ID_ADDRESS, 0L); |
4879 | 5189 | ||
5190 | retval = cyz_load_fw(pdev, addr2, addr0, irq); | ||
5191 | if (retval) | ||
5192 | goto err_unmap; | ||
4880 | /* This must be a Cyclades-8Zo/PCI. The extendable | 5193 | /* This must be a Cyclades-8Zo/PCI. The extendable |
4881 | version will have a different device_id and will | 5194 | version will have a different device_id and will |
4882 | be allocated its maximum number of ports. */ | 5195 | be allocated its maximum number of ports. */ |
@@ -4953,15 +5266,7 @@ static int __devinit cy_pci_probe(struct pci_dev *pdev, | |||
4953 | case PLX_9060: | 5266 | case PLX_9060: |
4954 | case PLX_9080: | 5267 | case PLX_9080: |
4955 | default: /* Old boards, use PLX_9060 */ | 5268 | default: /* Old boards, use PLX_9060 */ |
4956 | 5269 | plx_init(pdev, irq, addr0); | |
4957 | plx_init(addr0, 0x6c); | ||
4958 | /* For some yet unknown reason, once the PLX9060 reloads | ||
4959 | the EEPROM, the IRQ is lost and, thus, we have to | ||
4960 | re-write it to the PCI config. registers. | ||
4961 | This will remain here until we find a permanent | ||
4962 | fix. */ | ||
4963 | pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, irq); | ||
4964 | |||
4965 | cy_writew(addr0 + 0x68, readw(addr0 + 0x68) | 0x0900); | 5270 | cy_writew(addr0 + 0x68, readw(addr0 + 0x68) | 0x0900); |
4966 | break; | 5271 | break; |
4967 | } | 5272 | } |