Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / net / wireless / hostap / hostap_download.c
diff --git a/kernel/drivers/net/wireless/hostap/hostap_download.c b/kernel/drivers/net/wireless/hostap/hostap_download.c
new file mode 100644 (file)
index 0000000..705fe66
--- /dev/null
@@ -0,0 +1,812 @@
+static int prism2_enable_aux_port(struct net_device *dev, int enable)
+{
+       u16 val, reg;
+       int i, tries;
+       unsigned long flags;
+       struct hostap_interface *iface;
+       local_info_t *local;
+
+       iface = netdev_priv(dev);
+       local = iface->local;
+
+       if (local->no_pri) {
+               if (enable) {
+                       PDEBUG(DEBUG_EXTRA2, "%s: no PRI f/w - assuming Aux "
+                              "port is already enabled\n", dev->name);
+               }
+               return 0;
+       }
+
+       spin_lock_irqsave(&local->cmdlock, flags);
+
+       /* wait until busy bit is clear */
+       tries = HFA384X_CMD_BUSY_TIMEOUT;
+       while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) {
+               tries--;
+               udelay(1);
+       }
+       if (tries == 0) {
+               reg = HFA384X_INW(HFA384X_CMD_OFF);
+               spin_unlock_irqrestore(&local->cmdlock, flags);
+               printk("%s: prism2_enable_aux_port - timeout - reg=0x%04x\n",
+                      dev->name, reg);
+               return -ETIMEDOUT;
+       }
+
+       val = HFA384X_INW(HFA384X_CONTROL_OFF);
+
+       if (enable) {
+               HFA384X_OUTW(HFA384X_AUX_MAGIC0, HFA384X_PARAM0_OFF);
+               HFA384X_OUTW(HFA384X_AUX_MAGIC1, HFA384X_PARAM1_OFF);
+               HFA384X_OUTW(HFA384X_AUX_MAGIC2, HFA384X_PARAM2_OFF);
+
+               if ((val & HFA384X_AUX_PORT_MASK) != HFA384X_AUX_PORT_DISABLED)
+                       printk("prism2_enable_aux_port: was not disabled!?\n");
+               val &= ~HFA384X_AUX_PORT_MASK;
+               val |= HFA384X_AUX_PORT_ENABLE;
+       } else {
+               HFA384X_OUTW(0, HFA384X_PARAM0_OFF);
+               HFA384X_OUTW(0, HFA384X_PARAM1_OFF);
+               HFA384X_OUTW(0, HFA384X_PARAM2_OFF);
+
+               if ((val & HFA384X_AUX_PORT_MASK) != HFA384X_AUX_PORT_ENABLED)
+                       printk("prism2_enable_aux_port: was not enabled!?\n");
+               val &= ~HFA384X_AUX_PORT_MASK;
+               val |= HFA384X_AUX_PORT_DISABLE;
+       }
+       HFA384X_OUTW(val, HFA384X_CONTROL_OFF);
+
+       udelay(5);
+
+       i = 10000;
+       while (i > 0) {
+               val = HFA384X_INW(HFA384X_CONTROL_OFF);
+               val &= HFA384X_AUX_PORT_MASK;
+
+               if ((enable && val == HFA384X_AUX_PORT_ENABLED) ||
+                   (!enable && val == HFA384X_AUX_PORT_DISABLED))
+                       break;
+
+               udelay(10);
+               i--;
+       }
+
+       spin_unlock_irqrestore(&local->cmdlock, flags);
+
+       if (i == 0) {
+               printk("prism2_enable_aux_port(%d) timed out\n",
+                      enable);
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+
+static int hfa384x_from_aux(struct net_device *dev, unsigned int addr, int len,
+                           void *buf)
+{
+       u16 page, offset;
+       if (addr & 1 || len & 1)
+               return -1;
+
+       page = addr >> 7;
+       offset = addr & 0x7f;
+
+       HFA384X_OUTW(page, HFA384X_AUXPAGE_OFF);
+       HFA384X_OUTW(offset, HFA384X_AUXOFFSET_OFF);
+
+       udelay(5);
+
+#ifdef PRISM2_PCI
+       {
+               __le16 *pos = (__le16 *) buf;
+               while (len > 0) {
+                       *pos++ = HFA384X_INW_DATA(HFA384X_AUXDATA_OFF);
+                       len -= 2;
+               }
+       }
+#else /* PRISM2_PCI */
+       HFA384X_INSW(HFA384X_AUXDATA_OFF, buf, len / 2);
+#endif /* PRISM2_PCI */
+
+       return 0;
+}
+
+
+static int hfa384x_to_aux(struct net_device *dev, unsigned int addr, int len,
+                         void *buf)
+{
+       u16 page, offset;
+       if (addr & 1 || len & 1)
+               return -1;
+
+       page = addr >> 7;
+       offset = addr & 0x7f;
+
+       HFA384X_OUTW(page, HFA384X_AUXPAGE_OFF);
+       HFA384X_OUTW(offset, HFA384X_AUXOFFSET_OFF);
+
+       udelay(5);
+
+#ifdef PRISM2_PCI
+       {
+               __le16 *pos = (__le16 *) buf;
+               while (len > 0) {
+                       HFA384X_OUTW_DATA(*pos++, HFA384X_AUXDATA_OFF);
+                       len -= 2;
+               }
+       }
+#else /* PRISM2_PCI */
+       HFA384X_OUTSW(HFA384X_AUXDATA_OFF, buf, len / 2);
+#endif /* PRISM2_PCI */
+
+       return 0;
+}
+
+
+static int prism2_pda_ok(u8 *buf)
+{
+       __le16 *pda = (__le16 *) buf;
+       int pos;
+       u16 len, pdr;
+
+       if (buf[0] == 0xff && buf[1] == 0x00 && buf[2] == 0xff &&
+           buf[3] == 0x00)
+               return 0;
+
+       pos = 0;
+       while (pos + 1 < PRISM2_PDA_SIZE / 2) {
+               len = le16_to_cpu(pda[pos]);
+               pdr = le16_to_cpu(pda[pos + 1]);
+               if (len == 0 || pos + len > PRISM2_PDA_SIZE / 2)
+                       return 0;
+
+               if (pdr == 0x0000 && len == 2) {
+                       /* PDA end found */
+                       return 1;
+               }
+
+               pos += len + 1;
+       }
+
+       return 0;
+}
+
+
+#define prism2_download_aux_dump_npages 65536
+
+struct prism2_download_aux_dump {
+       local_info_t *local;
+       u16 page[0x80];
+};
+
+static int prism2_download_aux_dump_proc_show(struct seq_file *m, void *v)
+{
+       struct prism2_download_aux_dump *ctx = m->private;
+
+       hfa384x_from_aux(ctx->local->dev, (unsigned long)v - 1, 0x80, ctx->page);
+       seq_write(m, ctx->page, 0x80);
+       return 0;
+}
+
+static void *prism2_download_aux_dump_proc_start(struct seq_file *m, loff_t *_pos)
+{
+       struct prism2_download_aux_dump *ctx = m->private;
+       prism2_enable_aux_port(ctx->local->dev, 1);
+       if (*_pos >= prism2_download_aux_dump_npages)
+               return NULL;
+       return (void *)((unsigned long)*_pos + 1);
+}
+
+static void *prism2_download_aux_dump_proc_next(struct seq_file *m, void *v, loff_t *_pos)
+{
+       ++*_pos;
+       if (*_pos >= prism2_download_aux_dump_npages)
+               return NULL;
+       return (void *)((unsigned long)*_pos + 1);
+}
+
+static void prism2_download_aux_dump_proc_stop(struct seq_file *m, void *v)
+{
+       struct prism2_download_aux_dump *ctx = m->private;
+       prism2_enable_aux_port(ctx->local->dev, 0);
+}
+
+static const struct seq_operations prism2_download_aux_dump_proc_seqops = {
+       .start  = prism2_download_aux_dump_proc_start,
+       .next   = prism2_download_aux_dump_proc_next,
+       .stop   = prism2_download_aux_dump_proc_stop,
+       .show   = prism2_download_aux_dump_proc_show,
+};
+
+static int prism2_download_aux_dump_proc_open(struct inode *inode, struct file *file)
+{
+       int ret = seq_open_private(file, &prism2_download_aux_dump_proc_seqops,
+                                  sizeof(struct prism2_download_aux_dump));
+       if (ret == 0) {
+               struct seq_file *m = file->private_data;
+               m->private = PDE_DATA(inode);
+       }
+       return ret;
+}
+
+static const struct file_operations prism2_download_aux_dump_proc_fops = {
+       .open           = prism2_download_aux_dump_proc_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = seq_release_private,
+};
+
+
+static u8 * prism2_read_pda(struct net_device *dev)
+{
+       u8 *buf;
+       int res, i, found = 0;
+#define NUM_PDA_ADDRS 4
+       unsigned int pda_addr[NUM_PDA_ADDRS] = {
+               0x7f0000 /* others than HFA3841 */,
+               0x3f0000 /* HFA3841 */,
+               0x390000 /* apparently used in older cards */,
+               0x7f0002 /* Intel PRO/Wireless 2011B (PCI) */,
+       };
+
+       buf = kmalloc(PRISM2_PDA_SIZE, GFP_KERNEL);
+       if (buf == NULL)
+               return NULL;
+
+       /* Note: wlan card should be in initial state (just after init cmd)
+        * and no other operations should be performed concurrently. */
+
+       prism2_enable_aux_port(dev, 1);
+
+       for (i = 0; i < NUM_PDA_ADDRS; i++) {
+               PDEBUG(DEBUG_EXTRA2, "%s: trying to read PDA from 0x%08x",
+                      dev->name, pda_addr[i]);
+               res = hfa384x_from_aux(dev, pda_addr[i], PRISM2_PDA_SIZE, buf);
+               if (res)
+                       continue;
+               if (res == 0 && prism2_pda_ok(buf)) {
+                       PDEBUG2(DEBUG_EXTRA2, ": OK\n");
+                       found = 1;
+                       break;
+               } else {
+                       PDEBUG2(DEBUG_EXTRA2, ": failed\n");
+               }
+       }
+
+       prism2_enable_aux_port(dev, 0);
+
+       if (!found) {
+               printk(KERN_DEBUG "%s: valid PDA not found\n", dev->name);
+               kfree(buf);
+               buf = NULL;
+       }
+
+       return buf;
+}
+
+
+static int prism2_download_volatile(local_info_t *local,
+                                   struct prism2_download_data *param)
+{
+       struct net_device *dev = local->dev;
+       int ret = 0, i;
+       u16 param0, param1;
+
+       if (local->hw_downloading) {
+               printk(KERN_WARNING "%s: Already downloading - aborting new "
+                      "request\n", dev->name);
+               return -1;
+       }
+
+       local->hw_downloading = 1;
+       if (local->pri_only) {
+               hfa384x_disable_interrupts(dev);
+       } else {
+               prism2_hw_shutdown(dev, 0);
+
+               if (prism2_hw_init(dev, 0)) {
+                       printk(KERN_WARNING "%s: Could not initialize card for"
+                              " download\n", dev->name);
+                       ret = -1;
+                       goto out;
+               }
+       }
+
+       if (prism2_enable_aux_port(dev, 1)) {
+               printk(KERN_WARNING "%s: Could not enable AUX port\n",
+                      dev->name);
+               ret = -1;
+               goto out;
+       }
+
+       param0 = param->start_addr & 0xffff;
+       param1 = param->start_addr >> 16;
+
+       HFA384X_OUTW(0, HFA384X_PARAM2_OFF);
+       HFA384X_OUTW(param1, HFA384X_PARAM1_OFF);
+       if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD |
+                            (HFA384X_PROGMODE_ENABLE_VOLATILE << 8),
+                            param0)) {
+               printk(KERN_WARNING "%s: Download command execution failed\n",
+                      dev->name);
+               ret = -1;
+               goto out;
+       }
+
+       for (i = 0; i < param->num_areas; i++) {
+               PDEBUG(DEBUG_EXTRA2, "%s: Writing %d bytes at 0x%08x\n",
+                      dev->name, param->data[i].len, param->data[i].addr);
+               if (hfa384x_to_aux(dev, param->data[i].addr,
+                                  param->data[i].len, param->data[i].data)) {
+                       printk(KERN_WARNING "%s: RAM download at 0x%08x "
+                              "(len=%d) failed\n", dev->name,
+                              param->data[i].addr, param->data[i].len);
+                       ret = -1;
+                       goto out;
+               }
+       }
+
+       HFA384X_OUTW(param1, HFA384X_PARAM1_OFF);
+       HFA384X_OUTW(0, HFA384X_PARAM2_OFF);
+       if (hfa384x_cmd_no_wait(dev, HFA384X_CMDCODE_DOWNLOAD |
+                               (HFA384X_PROGMODE_DISABLE << 8), param0)) {
+               printk(KERN_WARNING "%s: Download command execution failed\n",
+                      dev->name);
+               ret = -1;
+               goto out;
+       }
+       /* ProgMode disable causes the hardware to restart itself from the
+        * given starting address. Give hw some time and ACK command just in
+        * case restart did not happen. */
+       mdelay(5);
+       HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF);
+
+       if (prism2_enable_aux_port(dev, 0)) {
+               printk(KERN_DEBUG "%s: Disabling AUX port failed\n",
+                      dev->name);
+               /* continue anyway.. restart should have taken care of this */
+       }
+
+       mdelay(5);
+       local->hw_downloading = 0;
+       if (prism2_hw_config(dev, 2)) {
+               printk(KERN_WARNING "%s: Card configuration after RAM "
+                      "download failed\n", dev->name);
+               ret = -1;
+               goto out;
+       }
+
+ out:
+       local->hw_downloading = 0;
+       return ret;
+}
+
+
+static int prism2_enable_genesis(local_info_t *local, int hcr)
+{
+       struct net_device *dev = local->dev;
+       u8 initseq[4] = { 0x00, 0xe1, 0xa1, 0xff };
+       u8 readbuf[4];
+
+       printk(KERN_DEBUG "%s: test Genesis mode with HCR 0x%02x\n",
+              dev->name, hcr);
+       local->func->cor_sreset(local);
+       hfa384x_to_aux(dev, 0x7e0038, sizeof(initseq), initseq);
+       local->func->genesis_reset(local, hcr);
+
+       /* Readback test */
+       hfa384x_from_aux(dev, 0x7e0038, sizeof(readbuf), readbuf);
+       hfa384x_to_aux(dev, 0x7e0038, sizeof(initseq), initseq);
+       hfa384x_from_aux(dev, 0x7e0038, sizeof(readbuf), readbuf);
+
+       if (memcmp(initseq, readbuf, sizeof(initseq)) == 0) {
+               printk(KERN_DEBUG "Readback test succeeded, HCR 0x%02x\n",
+                      hcr);
+               return 0;
+       } else {
+               printk(KERN_DEBUG "Readback test failed, HCR 0x%02x "
+                      "write %02x %02x %02x %02x read %02x %02x %02x %02x\n",
+                      hcr, initseq[0], initseq[1], initseq[2], initseq[3],
+                      readbuf[0], readbuf[1], readbuf[2], readbuf[3]);
+               return 1;
+       }
+}
+
+
+static int prism2_get_ram_size(local_info_t *local)
+{
+       int ret;
+
+       /* Try to enable genesis mode; 0x1F for x8 SRAM or 0x0F for x16 SRAM */
+       if (prism2_enable_genesis(local, 0x1f) == 0)
+               ret = 8;
+       else if (prism2_enable_genesis(local, 0x0f) == 0)
+               ret = 16;
+       else
+               ret = -1;
+
+       /* Disable genesis mode */
+       local->func->genesis_reset(local, ret == 16 ? 0x07 : 0x17);
+
+       return ret;
+}
+
+
+static int prism2_download_genesis(local_info_t *local,
+                                  struct prism2_download_data *param)
+{
+       struct net_device *dev = local->dev;
+       int ram16 = 0, i;
+       int ret = 0;
+
+       if (local->hw_downloading) {
+               printk(KERN_WARNING "%s: Already downloading - aborting new "
+                      "request\n", dev->name);
+               return -EBUSY;
+       }
+
+       if (!local->func->genesis_reset || !local->func->cor_sreset) {
+               printk(KERN_INFO "%s: Genesis mode downloading not supported "
+                      "with this hwmodel\n", dev->name);
+               return -EOPNOTSUPP;
+       }
+
+       local->hw_downloading = 1;
+
+       if (prism2_enable_aux_port(dev, 1)) {
+               printk(KERN_DEBUG "%s: failed to enable AUX port\n",
+                      dev->name);
+               ret = -EIO;
+               goto out;
+       }
+
+       if (local->sram_type == -1) {
+               /* 0x1F for x8 SRAM or 0x0F for x16 SRAM */
+               if (prism2_enable_genesis(local, 0x1f) == 0) {
+                       ram16 = 0;
+                       PDEBUG(DEBUG_EXTRA2, "%s: Genesis mode OK using x8 "
+                              "SRAM\n", dev->name);
+               } else if (prism2_enable_genesis(local, 0x0f) == 0) {
+                       ram16 = 1;
+                       PDEBUG(DEBUG_EXTRA2, "%s: Genesis mode OK using x16 "
+                              "SRAM\n", dev->name);
+               } else {
+                       printk(KERN_DEBUG "%s: Could not initiate genesis "
+                              "mode\n", dev->name);
+                       ret = -EIO;
+                       goto out;
+               }
+       } else {
+               if (prism2_enable_genesis(local, local->sram_type == 8 ?
+                                         0x1f : 0x0f)) {
+                       printk(KERN_DEBUG "%s: Failed to set Genesis "
+                              "mode (sram_type=%d)\n", dev->name,
+                              local->sram_type);
+                       ret = -EIO;
+                       goto out;
+               }
+               ram16 = local->sram_type != 8;
+       }
+
+       for (i = 0; i < param->num_areas; i++) {
+               PDEBUG(DEBUG_EXTRA2, "%s: Writing %d bytes at 0x%08x\n",
+                      dev->name, param->data[i].len, param->data[i].addr);
+               if (hfa384x_to_aux(dev, param->data[i].addr,
+                                  param->data[i].len, param->data[i].data)) {
+                       printk(KERN_WARNING "%s: RAM download at 0x%08x "
+                              "(len=%d) failed\n", dev->name,
+                              param->data[i].addr, param->data[i].len);
+                       ret = -EIO;
+                       goto out;
+               }
+       }
+
+       PDEBUG(DEBUG_EXTRA2, "Disable genesis mode\n");
+       local->func->genesis_reset(local, ram16 ? 0x07 : 0x17);
+       if (prism2_enable_aux_port(dev, 0)) {
+               printk(KERN_DEBUG "%s: Failed to disable AUX port\n",
+                      dev->name);
+       }
+
+       mdelay(5);
+       local->hw_downloading = 0;
+
+       PDEBUG(DEBUG_EXTRA2, "Trying to initialize card\n");
+       /*
+        * Make sure the INIT command does not generate a command completion
+        * event by disabling interrupts.
+        */
+       hfa384x_disable_interrupts(dev);
+       if (prism2_hw_init(dev, 1)) {
+               printk(KERN_DEBUG "%s: Initialization after genesis mode "
+                      "download failed\n", dev->name);
+               ret = -EIO;
+               goto out;
+       }
+
+       PDEBUG(DEBUG_EXTRA2, "Card initialized - running PRI only\n");
+       if (prism2_hw_init2(dev, 1)) {
+               printk(KERN_DEBUG "%s: Initialization(2) after genesis mode "
+                      "download failed\n", dev->name);
+               ret = -EIO;
+               goto out;
+       }
+
+ out:
+       local->hw_downloading = 0;
+       return ret;
+}
+
+
+#ifdef PRISM2_NON_VOLATILE_DOWNLOAD
+/* Note! Non-volatile downloading functionality has not yet been tested
+ * thoroughly and it may corrupt flash image and effectively kill the card that
+ * is being updated. You have been warned. */
+
+static inline int prism2_download_block(struct net_device *dev,
+                                       u32 addr, u8 *data,
+                                       u32 bufaddr, int rest_len)
+{
+       u16 param0, param1;
+       int block_len;
+
+       block_len = rest_len < 4096 ? rest_len : 4096;
+
+       param0 = addr & 0xffff;
+       param1 = addr >> 16;
+
+       HFA384X_OUTW(block_len, HFA384X_PARAM2_OFF);
+       HFA384X_OUTW(param1, HFA384X_PARAM1_OFF);
+
+       if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD |
+                            (HFA384X_PROGMODE_ENABLE_NON_VOLATILE << 8),
+                            param0)) {
+               printk(KERN_WARNING "%s: Flash download command execution "
+                      "failed\n", dev->name);
+               return -1;
+       }
+
+       if (hfa384x_to_aux(dev, bufaddr, block_len, data)) {
+               printk(KERN_WARNING "%s: flash download at 0x%08x "
+                      "(len=%d) failed\n", dev->name, addr, block_len);
+               return -1;
+       }
+
+       HFA384X_OUTW(0, HFA384X_PARAM2_OFF);
+       HFA384X_OUTW(0, HFA384X_PARAM1_OFF);
+       if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD |
+                            (HFA384X_PROGMODE_PROGRAM_NON_VOLATILE << 8),
+                            0)) {
+               printk(KERN_WARNING "%s: Flash write command execution "
+                      "failed\n", dev->name);
+               return -1;
+       }
+
+       return block_len;
+}
+
+
+static int prism2_download_nonvolatile(local_info_t *local,
+                                      struct prism2_download_data *dl)
+{
+       struct net_device *dev = local->dev;
+       int ret = 0, i;
+       struct {
+               __le16 page;
+               __le16 offset;
+               __le16 len;
+       } dlbuffer;
+       u32 bufaddr;
+
+       if (local->hw_downloading) {
+               printk(KERN_WARNING "%s: Already downloading - aborting new "
+                      "request\n", dev->name);
+               return -1;
+       }
+
+       ret = local->func->get_rid(dev, HFA384X_RID_DOWNLOADBUFFER,
+                                  &dlbuffer, 6, 0);
+
+       if (ret < 0) {
+               printk(KERN_WARNING "%s: Could not read download buffer "
+                      "parameters\n", dev->name);
+               goto out;
+       }
+
+       printk(KERN_DEBUG "Download buffer: %d bytes at 0x%04x:0x%04x\n",
+              le16_to_cpu(dlbuffer.len),
+              le16_to_cpu(dlbuffer.page),
+              le16_to_cpu(dlbuffer.offset));
+
+       bufaddr = (le16_to_cpu(dlbuffer.page) << 7) + le16_to_cpu(dlbuffer.offset);
+
+       local->hw_downloading = 1;
+
+       if (!local->pri_only) {
+               prism2_hw_shutdown(dev, 0);
+
+               if (prism2_hw_init(dev, 0)) {
+                       printk(KERN_WARNING "%s: Could not initialize card for"
+                              " download\n", dev->name);
+                       ret = -1;
+                       goto out;
+               }
+       }
+
+       hfa384x_disable_interrupts(dev);
+
+       if (prism2_enable_aux_port(dev, 1)) {
+               printk(KERN_WARNING "%s: Could not enable AUX port\n",
+                      dev->name);
+               ret = -1;
+               goto out;
+       }
+
+       printk(KERN_DEBUG "%s: starting flash download\n", dev->name);
+       for (i = 0; i < dl->num_areas; i++) {
+               int rest_len = dl->data[i].len;
+               int data_off = 0;
+
+               while (rest_len > 0) {
+                       int block_len;
+
+                       block_len = prism2_download_block(
+                               dev, dl->data[i].addr + data_off,
+                               dl->data[i].data + data_off, bufaddr,
+                               rest_len);
+
+                       if (block_len < 0) {
+                               ret = -1;
+                               goto out;
+                       }
+
+                       rest_len -= block_len;
+                       data_off += block_len;
+               }
+       }
+
+       HFA384X_OUTW(0, HFA384X_PARAM1_OFF);
+       HFA384X_OUTW(0, HFA384X_PARAM2_OFF);
+       if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD |
+                               (HFA384X_PROGMODE_DISABLE << 8), 0)) {
+               printk(KERN_WARNING "%s: Download command execution failed\n",
+                      dev->name);
+               ret = -1;
+               goto out;
+       }
+
+       if (prism2_enable_aux_port(dev, 0)) {
+               printk(KERN_DEBUG "%s: Disabling AUX port failed\n",
+                      dev->name);
+               /* continue anyway.. restart should have taken care of this */
+       }
+
+       mdelay(5);
+
+       local->func->hw_reset(dev);
+       local->hw_downloading = 0;
+       if (prism2_hw_config(dev, 2)) {
+               printk(KERN_WARNING "%s: Card configuration after flash "
+                      "download failed\n", dev->name);
+               ret = -1;
+       } else {
+               printk(KERN_INFO "%s: Card initialized successfully after "
+                      "flash download\n", dev->name);
+       }
+
+ out:
+       local->hw_downloading = 0;
+       return ret;
+}
+#endif /* PRISM2_NON_VOLATILE_DOWNLOAD */
+
+
+static void prism2_download_free_data(struct prism2_download_data *dl)
+{
+       int i;
+
+       if (dl == NULL)
+               return;
+
+       for (i = 0; i < dl->num_areas; i++)
+               kfree(dl->data[i].data);
+       kfree(dl);
+}
+
+
+static int prism2_download(local_info_t *local,
+                          struct prism2_download_param *param)
+{
+       int ret = 0;
+       int i;
+       u32 total_len = 0;
+       struct prism2_download_data *dl = NULL;
+
+       printk(KERN_DEBUG "prism2_download: dl_cmd=%d start_addr=0x%08x "
+              "num_areas=%d\n",
+              param->dl_cmd, param->start_addr, param->num_areas);
+
+       if (param->num_areas > 100) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       dl = kzalloc(sizeof(*dl) + param->num_areas *
+                    sizeof(struct prism2_download_data_area), GFP_KERNEL);
+       if (dl == NULL) {
+               ret = -ENOMEM;
+               goto out;
+       }
+       dl->dl_cmd = param->dl_cmd;
+       dl->start_addr = param->start_addr;
+       dl->num_areas = param->num_areas;
+       for (i = 0; i < param->num_areas; i++) {
+               PDEBUG(DEBUG_EXTRA2,
+                      "  area %d: addr=0x%08x len=%d ptr=0x%p\n",
+                      i, param->data[i].addr, param->data[i].len,
+                      param->data[i].ptr);
+
+               dl->data[i].addr = param->data[i].addr;
+               dl->data[i].len = param->data[i].len;
+
+               total_len += param->data[i].len;
+               if (param->data[i].len > PRISM2_MAX_DOWNLOAD_AREA_LEN ||
+                   total_len > PRISM2_MAX_DOWNLOAD_LEN) {
+                       ret = -E2BIG;
+                       goto out;
+               }
+
+               dl->data[i].data = kmalloc(dl->data[i].len, GFP_KERNEL);
+               if (dl->data[i].data == NULL) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+
+               if (copy_from_user(dl->data[i].data, param->data[i].ptr,
+                                  param->data[i].len)) {
+                       ret = -EFAULT;
+                       goto out;
+               }
+       }
+
+       switch (param->dl_cmd) {
+       case PRISM2_DOWNLOAD_VOLATILE:
+       case PRISM2_DOWNLOAD_VOLATILE_PERSISTENT:
+               ret = prism2_download_volatile(local, dl);
+               break;
+       case PRISM2_DOWNLOAD_VOLATILE_GENESIS:
+       case PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT:
+               ret = prism2_download_genesis(local, dl);
+               break;
+       case PRISM2_DOWNLOAD_NON_VOLATILE:
+#ifdef PRISM2_NON_VOLATILE_DOWNLOAD
+               ret = prism2_download_nonvolatile(local, dl);
+#else /* PRISM2_NON_VOLATILE_DOWNLOAD */
+               printk(KERN_INFO "%s: non-volatile downloading not enabled\n",
+                      local->dev->name);
+               ret = -EOPNOTSUPP;
+#endif /* PRISM2_NON_VOLATILE_DOWNLOAD */
+               break;
+       default:
+               printk(KERN_DEBUG "%s: unsupported download command %d\n",
+                      local->dev->name, param->dl_cmd);
+               ret = -EINVAL;
+               break;
+       }
+
+ out:
+       if (ret == 0 && dl &&
+           param->dl_cmd == PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT) {
+               prism2_download_free_data(local->dl_pri);
+               local->dl_pri = dl;
+       } else if (ret == 0 && dl &&
+                  param->dl_cmd == PRISM2_DOWNLOAD_VOLATILE_PERSISTENT) {
+               prism2_download_free_data(local->dl_sec);
+               local->dl_sec = dl;
+       } else
+               prism2_download_free_data(dl);
+
+       return ret;
+}