18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_cistatic int prism2_enable_aux_port(struct net_device *dev, int enable) 38c2ecf20Sopenharmony_ci{ 48c2ecf20Sopenharmony_ci u16 val, reg; 58c2ecf20Sopenharmony_ci int i, tries; 68c2ecf20Sopenharmony_ci unsigned long flags; 78c2ecf20Sopenharmony_ci struct hostap_interface *iface; 88c2ecf20Sopenharmony_ci local_info_t *local; 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci iface = netdev_priv(dev); 118c2ecf20Sopenharmony_ci local = iface->local; 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci if (local->no_pri) { 148c2ecf20Sopenharmony_ci if (enable) { 158c2ecf20Sopenharmony_ci PDEBUG(DEBUG_EXTRA2, "%s: no PRI f/w - assuming Aux " 168c2ecf20Sopenharmony_ci "port is already enabled\n", dev->name); 178c2ecf20Sopenharmony_ci } 188c2ecf20Sopenharmony_ci return 0; 198c2ecf20Sopenharmony_ci } 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci spin_lock_irqsave(&local->cmdlock, flags); 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci /* wait until busy bit is clear */ 248c2ecf20Sopenharmony_ci tries = HFA384X_CMD_BUSY_TIMEOUT; 258c2ecf20Sopenharmony_ci while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) { 268c2ecf20Sopenharmony_ci tries--; 278c2ecf20Sopenharmony_ci udelay(1); 288c2ecf20Sopenharmony_ci } 298c2ecf20Sopenharmony_ci if (tries == 0) { 308c2ecf20Sopenharmony_ci reg = HFA384X_INW(HFA384X_CMD_OFF); 318c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&local->cmdlock, flags); 328c2ecf20Sopenharmony_ci printk("%s: prism2_enable_aux_port - timeout - reg=0x%04x\n", 338c2ecf20Sopenharmony_ci dev->name, reg); 348c2ecf20Sopenharmony_ci return -ETIMEDOUT; 358c2ecf20Sopenharmony_ci } 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci val = HFA384X_INW(HFA384X_CONTROL_OFF); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci if (enable) { 408c2ecf20Sopenharmony_ci HFA384X_OUTW(HFA384X_AUX_MAGIC0, HFA384X_PARAM0_OFF); 418c2ecf20Sopenharmony_ci HFA384X_OUTW(HFA384X_AUX_MAGIC1, HFA384X_PARAM1_OFF); 428c2ecf20Sopenharmony_ci HFA384X_OUTW(HFA384X_AUX_MAGIC2, HFA384X_PARAM2_OFF); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci if ((val & HFA384X_AUX_PORT_MASK) != HFA384X_AUX_PORT_DISABLED) 458c2ecf20Sopenharmony_ci printk("prism2_enable_aux_port: was not disabled!?\n"); 468c2ecf20Sopenharmony_ci val &= ~HFA384X_AUX_PORT_MASK; 478c2ecf20Sopenharmony_ci val |= HFA384X_AUX_PORT_ENABLE; 488c2ecf20Sopenharmony_ci } else { 498c2ecf20Sopenharmony_ci HFA384X_OUTW(0, HFA384X_PARAM0_OFF); 508c2ecf20Sopenharmony_ci HFA384X_OUTW(0, HFA384X_PARAM1_OFF); 518c2ecf20Sopenharmony_ci HFA384X_OUTW(0, HFA384X_PARAM2_OFF); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci if ((val & HFA384X_AUX_PORT_MASK) != HFA384X_AUX_PORT_ENABLED) 548c2ecf20Sopenharmony_ci printk("prism2_enable_aux_port: was not enabled!?\n"); 558c2ecf20Sopenharmony_ci val &= ~HFA384X_AUX_PORT_MASK; 568c2ecf20Sopenharmony_ci val |= HFA384X_AUX_PORT_DISABLE; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci HFA384X_OUTW(val, HFA384X_CONTROL_OFF); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci udelay(5); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci i = 10000; 638c2ecf20Sopenharmony_ci while (i > 0) { 648c2ecf20Sopenharmony_ci val = HFA384X_INW(HFA384X_CONTROL_OFF); 658c2ecf20Sopenharmony_ci val &= HFA384X_AUX_PORT_MASK; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if ((enable && val == HFA384X_AUX_PORT_ENABLED) || 688c2ecf20Sopenharmony_ci (!enable && val == HFA384X_AUX_PORT_DISABLED)) 698c2ecf20Sopenharmony_ci break; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci udelay(10); 728c2ecf20Sopenharmony_ci i--; 738c2ecf20Sopenharmony_ci } 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&local->cmdlock, flags); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (i == 0) { 788c2ecf20Sopenharmony_ci printk("prism2_enable_aux_port(%d) timed out\n", 798c2ecf20Sopenharmony_ci enable); 808c2ecf20Sopenharmony_ci return -ETIMEDOUT; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci return 0; 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic int hfa384x_from_aux(struct net_device *dev, unsigned int addr, int len, 888c2ecf20Sopenharmony_ci void *buf) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci u16 page, offset; 918c2ecf20Sopenharmony_ci if (addr & 1 || len & 1) 928c2ecf20Sopenharmony_ci return -1; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci page = addr >> 7; 958c2ecf20Sopenharmony_ci offset = addr & 0x7f; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci HFA384X_OUTW(page, HFA384X_AUXPAGE_OFF); 988c2ecf20Sopenharmony_ci HFA384X_OUTW(offset, HFA384X_AUXOFFSET_OFF); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci udelay(5); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci#ifdef PRISM2_PCI 1038c2ecf20Sopenharmony_ci { 1048c2ecf20Sopenharmony_ci __le16 *pos = (__le16 *) buf; 1058c2ecf20Sopenharmony_ci while (len > 0) { 1068c2ecf20Sopenharmony_ci *pos++ = HFA384X_INW_DATA(HFA384X_AUXDATA_OFF); 1078c2ecf20Sopenharmony_ci len -= 2; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci#else /* PRISM2_PCI */ 1118c2ecf20Sopenharmony_ci HFA384X_INSW(HFA384X_AUXDATA_OFF, buf, len / 2); 1128c2ecf20Sopenharmony_ci#endif /* PRISM2_PCI */ 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci return 0; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic int hfa384x_to_aux(struct net_device *dev, unsigned int addr, int len, 1198c2ecf20Sopenharmony_ci void *buf) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci u16 page, offset; 1228c2ecf20Sopenharmony_ci if (addr & 1 || len & 1) 1238c2ecf20Sopenharmony_ci return -1; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci page = addr >> 7; 1268c2ecf20Sopenharmony_ci offset = addr & 0x7f; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci HFA384X_OUTW(page, HFA384X_AUXPAGE_OFF); 1298c2ecf20Sopenharmony_ci HFA384X_OUTW(offset, HFA384X_AUXOFFSET_OFF); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci udelay(5); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci#ifdef PRISM2_PCI 1348c2ecf20Sopenharmony_ci { 1358c2ecf20Sopenharmony_ci __le16 *pos = (__le16 *) buf; 1368c2ecf20Sopenharmony_ci while (len > 0) { 1378c2ecf20Sopenharmony_ci HFA384X_OUTW_DATA(*pos++, HFA384X_AUXDATA_OFF); 1388c2ecf20Sopenharmony_ci len -= 2; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci#else /* PRISM2_PCI */ 1428c2ecf20Sopenharmony_ci HFA384X_OUTSW(HFA384X_AUXDATA_OFF, buf, len / 2); 1438c2ecf20Sopenharmony_ci#endif /* PRISM2_PCI */ 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci return 0; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic int prism2_pda_ok(u8 *buf) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci __le16 *pda = (__le16 *) buf; 1528c2ecf20Sopenharmony_ci int pos; 1538c2ecf20Sopenharmony_ci u16 len, pdr; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (buf[0] == 0xff && buf[1] == 0x00 && buf[2] == 0xff && 1568c2ecf20Sopenharmony_ci buf[3] == 0x00) 1578c2ecf20Sopenharmony_ci return 0; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci pos = 0; 1608c2ecf20Sopenharmony_ci while (pos + 1 < PRISM2_PDA_SIZE / 2) { 1618c2ecf20Sopenharmony_ci len = le16_to_cpu(pda[pos]); 1628c2ecf20Sopenharmony_ci pdr = le16_to_cpu(pda[pos + 1]); 1638c2ecf20Sopenharmony_ci if (len == 0 || pos + len > PRISM2_PDA_SIZE / 2) 1648c2ecf20Sopenharmony_ci return 0; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (pdr == 0x0000 && len == 2) { 1678c2ecf20Sopenharmony_ci /* PDA end found */ 1688c2ecf20Sopenharmony_ci return 1; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci pos += len + 1; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return 0; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci#define prism2_download_aux_dump_npages 65536 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistruct prism2_download_aux_dump { 1818c2ecf20Sopenharmony_ci local_info_t *local; 1828c2ecf20Sopenharmony_ci u16 page[0x80]; 1838c2ecf20Sopenharmony_ci}; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int prism2_download_aux_dump_proc_show(struct seq_file *m, void *v) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct prism2_download_aux_dump *ctx = m->private; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci hfa384x_from_aux(ctx->local->dev, (unsigned long)v - 1, 0x80, ctx->page); 1908c2ecf20Sopenharmony_ci seq_write(m, ctx->page, 0x80); 1918c2ecf20Sopenharmony_ci return 0; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic void *prism2_download_aux_dump_proc_start(struct seq_file *m, loff_t *_pos) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci struct prism2_download_aux_dump *ctx = m->private; 1978c2ecf20Sopenharmony_ci prism2_enable_aux_port(ctx->local->dev, 1); 1988c2ecf20Sopenharmony_ci if (*_pos >= prism2_download_aux_dump_npages) 1998c2ecf20Sopenharmony_ci return NULL; 2008c2ecf20Sopenharmony_ci return (void *)((unsigned long)*_pos + 1); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic void *prism2_download_aux_dump_proc_next(struct seq_file *m, void *v, loff_t *_pos) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci ++*_pos; 2068c2ecf20Sopenharmony_ci if (*_pos >= prism2_download_aux_dump_npages) 2078c2ecf20Sopenharmony_ci return NULL; 2088c2ecf20Sopenharmony_ci return (void *)((unsigned long)*_pos + 1); 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic void prism2_download_aux_dump_proc_stop(struct seq_file *m, void *v) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci struct prism2_download_aux_dump *ctx = m->private; 2148c2ecf20Sopenharmony_ci prism2_enable_aux_port(ctx->local->dev, 0); 2158c2ecf20Sopenharmony_ci} 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_cistatic const struct seq_operations prism2_download_aux_dump_proc_seqops = { 2188c2ecf20Sopenharmony_ci .start = prism2_download_aux_dump_proc_start, 2198c2ecf20Sopenharmony_ci .next = prism2_download_aux_dump_proc_next, 2208c2ecf20Sopenharmony_ci .stop = prism2_download_aux_dump_proc_stop, 2218c2ecf20Sopenharmony_ci .show = prism2_download_aux_dump_proc_show, 2228c2ecf20Sopenharmony_ci}; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic int prism2_download_aux_dump_proc_open(struct inode *inode, struct file *file) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci int ret = seq_open_private(file, &prism2_download_aux_dump_proc_seqops, 2278c2ecf20Sopenharmony_ci sizeof(struct prism2_download_aux_dump)); 2288c2ecf20Sopenharmony_ci if (ret == 0) { 2298c2ecf20Sopenharmony_ci struct seq_file *m = file->private_data; 2308c2ecf20Sopenharmony_ci m->private = PDE_DATA(inode); 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci return ret; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic const struct proc_ops prism2_download_aux_dump_proc_ops = { 2368c2ecf20Sopenharmony_ci .proc_open = prism2_download_aux_dump_proc_open, 2378c2ecf20Sopenharmony_ci .proc_read = seq_read, 2388c2ecf20Sopenharmony_ci .proc_lseek = seq_lseek, 2398c2ecf20Sopenharmony_ci .proc_release = seq_release_private, 2408c2ecf20Sopenharmony_ci}; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic u8 * prism2_read_pda(struct net_device *dev) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci u8 *buf; 2468c2ecf20Sopenharmony_ci int res, i, found = 0; 2478c2ecf20Sopenharmony_ci#define NUM_PDA_ADDRS 4 2488c2ecf20Sopenharmony_ci unsigned int pda_addr[NUM_PDA_ADDRS] = { 2498c2ecf20Sopenharmony_ci 0x7f0000 /* others than HFA3841 */, 2508c2ecf20Sopenharmony_ci 0x3f0000 /* HFA3841 */, 2518c2ecf20Sopenharmony_ci 0x390000 /* apparently used in older cards */, 2528c2ecf20Sopenharmony_ci 0x7f0002 /* Intel PRO/Wireless 2011B (PCI) */, 2538c2ecf20Sopenharmony_ci }; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci buf = kmalloc(PRISM2_PDA_SIZE, GFP_KERNEL); 2568c2ecf20Sopenharmony_ci if (buf == NULL) 2578c2ecf20Sopenharmony_ci return NULL; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci /* Note: wlan card should be in initial state (just after init cmd) 2608c2ecf20Sopenharmony_ci * and no other operations should be performed concurrently. */ 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci prism2_enable_aux_port(dev, 1); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci for (i = 0; i < NUM_PDA_ADDRS; i++) { 2658c2ecf20Sopenharmony_ci PDEBUG(DEBUG_EXTRA2, "%s: trying to read PDA from 0x%08x", 2668c2ecf20Sopenharmony_ci dev->name, pda_addr[i]); 2678c2ecf20Sopenharmony_ci res = hfa384x_from_aux(dev, pda_addr[i], PRISM2_PDA_SIZE, buf); 2688c2ecf20Sopenharmony_ci if (res) 2698c2ecf20Sopenharmony_ci continue; 2708c2ecf20Sopenharmony_ci if (res == 0 && prism2_pda_ok(buf)) { 2718c2ecf20Sopenharmony_ci PDEBUG2(DEBUG_EXTRA2, ": OK\n"); 2728c2ecf20Sopenharmony_ci found = 1; 2738c2ecf20Sopenharmony_ci break; 2748c2ecf20Sopenharmony_ci } else { 2758c2ecf20Sopenharmony_ci PDEBUG2(DEBUG_EXTRA2, ": failed\n"); 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci prism2_enable_aux_port(dev, 0); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (!found) { 2828c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: valid PDA not found\n", dev->name); 2838c2ecf20Sopenharmony_ci kfree(buf); 2848c2ecf20Sopenharmony_ci buf = NULL; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci return buf; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic int prism2_download_volatile(local_info_t *local, 2928c2ecf20Sopenharmony_ci struct prism2_download_data *param) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci struct net_device *dev = local->dev; 2958c2ecf20Sopenharmony_ci int ret = 0, i; 2968c2ecf20Sopenharmony_ci u16 param0, param1; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci if (local->hw_downloading) { 2998c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Already downloading - aborting new " 3008c2ecf20Sopenharmony_ci "request\n", dev->name); 3018c2ecf20Sopenharmony_ci return -1; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci local->hw_downloading = 1; 3058c2ecf20Sopenharmony_ci if (local->pri_only) { 3068c2ecf20Sopenharmony_ci hfa384x_disable_interrupts(dev); 3078c2ecf20Sopenharmony_ci } else { 3088c2ecf20Sopenharmony_ci prism2_hw_shutdown(dev, 0); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (prism2_hw_init(dev, 0)) { 3118c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Could not initialize card for" 3128c2ecf20Sopenharmony_ci " download\n", dev->name); 3138c2ecf20Sopenharmony_ci ret = -1; 3148c2ecf20Sopenharmony_ci goto out; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (prism2_enable_aux_port(dev, 1)) { 3198c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Could not enable AUX port\n", 3208c2ecf20Sopenharmony_ci dev->name); 3218c2ecf20Sopenharmony_ci ret = -1; 3228c2ecf20Sopenharmony_ci goto out; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci param0 = param->start_addr & 0xffff; 3268c2ecf20Sopenharmony_ci param1 = param->start_addr >> 16; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci HFA384X_OUTW(0, HFA384X_PARAM2_OFF); 3298c2ecf20Sopenharmony_ci HFA384X_OUTW(param1, HFA384X_PARAM1_OFF); 3308c2ecf20Sopenharmony_ci if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | 3318c2ecf20Sopenharmony_ci (HFA384X_PROGMODE_ENABLE_VOLATILE << 8), 3328c2ecf20Sopenharmony_ci param0)) { 3338c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Download command execution failed\n", 3348c2ecf20Sopenharmony_ci dev->name); 3358c2ecf20Sopenharmony_ci ret = -1; 3368c2ecf20Sopenharmony_ci goto out; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci for (i = 0; i < param->num_areas; i++) { 3408c2ecf20Sopenharmony_ci PDEBUG(DEBUG_EXTRA2, "%s: Writing %d bytes at 0x%08x\n", 3418c2ecf20Sopenharmony_ci dev->name, param->data[i].len, param->data[i].addr); 3428c2ecf20Sopenharmony_ci if (hfa384x_to_aux(dev, param->data[i].addr, 3438c2ecf20Sopenharmony_ci param->data[i].len, param->data[i].data)) { 3448c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: RAM download at 0x%08x " 3458c2ecf20Sopenharmony_ci "(len=%d) failed\n", dev->name, 3468c2ecf20Sopenharmony_ci param->data[i].addr, param->data[i].len); 3478c2ecf20Sopenharmony_ci ret = -1; 3488c2ecf20Sopenharmony_ci goto out; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci HFA384X_OUTW(param1, HFA384X_PARAM1_OFF); 3538c2ecf20Sopenharmony_ci HFA384X_OUTW(0, HFA384X_PARAM2_OFF); 3548c2ecf20Sopenharmony_ci if (hfa384x_cmd_no_wait(dev, HFA384X_CMDCODE_DOWNLOAD | 3558c2ecf20Sopenharmony_ci (HFA384X_PROGMODE_DISABLE << 8), param0)) { 3568c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Download command execution failed\n", 3578c2ecf20Sopenharmony_ci dev->name); 3588c2ecf20Sopenharmony_ci ret = -1; 3598c2ecf20Sopenharmony_ci goto out; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci /* ProgMode disable causes the hardware to restart itself from the 3628c2ecf20Sopenharmony_ci * given starting address. Give hw some time and ACK command just in 3638c2ecf20Sopenharmony_ci * case restart did not happen. */ 3648c2ecf20Sopenharmony_ci mdelay(5); 3658c2ecf20Sopenharmony_ci HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci if (prism2_enable_aux_port(dev, 0)) { 3688c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: Disabling AUX port failed\n", 3698c2ecf20Sopenharmony_ci dev->name); 3708c2ecf20Sopenharmony_ci /* continue anyway.. restart should have taken care of this */ 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci mdelay(5); 3748c2ecf20Sopenharmony_ci local->hw_downloading = 0; 3758c2ecf20Sopenharmony_ci if (prism2_hw_config(dev, 2)) { 3768c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Card configuration after RAM " 3778c2ecf20Sopenharmony_ci "download failed\n", dev->name); 3788c2ecf20Sopenharmony_ci ret = -1; 3798c2ecf20Sopenharmony_ci goto out; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci out: 3838c2ecf20Sopenharmony_ci local->hw_downloading = 0; 3848c2ecf20Sopenharmony_ci return ret; 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic int prism2_enable_genesis(local_info_t *local, int hcr) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci struct net_device *dev = local->dev; 3918c2ecf20Sopenharmony_ci u8 initseq[4] = { 0x00, 0xe1, 0xa1, 0xff }; 3928c2ecf20Sopenharmony_ci u8 readbuf[4]; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: test Genesis mode with HCR 0x%02x\n", 3958c2ecf20Sopenharmony_ci dev->name, hcr); 3968c2ecf20Sopenharmony_ci local->func->cor_sreset(local); 3978c2ecf20Sopenharmony_ci hfa384x_to_aux(dev, 0x7e0038, sizeof(initseq), initseq); 3988c2ecf20Sopenharmony_ci local->func->genesis_reset(local, hcr); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci /* Readback test */ 4018c2ecf20Sopenharmony_ci hfa384x_from_aux(dev, 0x7e0038, sizeof(readbuf), readbuf); 4028c2ecf20Sopenharmony_ci hfa384x_to_aux(dev, 0x7e0038, sizeof(initseq), initseq); 4038c2ecf20Sopenharmony_ci hfa384x_from_aux(dev, 0x7e0038, sizeof(readbuf), readbuf); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (memcmp(initseq, readbuf, sizeof(initseq)) == 0) { 4068c2ecf20Sopenharmony_ci printk(KERN_DEBUG "Readback test succeeded, HCR 0x%02x\n", 4078c2ecf20Sopenharmony_ci hcr); 4088c2ecf20Sopenharmony_ci return 0; 4098c2ecf20Sopenharmony_ci } else { 4108c2ecf20Sopenharmony_ci printk(KERN_DEBUG "Readback test failed, HCR 0x%02x write %4ph read %4ph\n", 4118c2ecf20Sopenharmony_ci hcr, initseq, readbuf); 4128c2ecf20Sopenharmony_ci return 1; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic int prism2_get_ram_size(local_info_t *local) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci int ret; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci /* Try to enable genesis mode; 0x1F for x8 SRAM or 0x0F for x16 SRAM */ 4228c2ecf20Sopenharmony_ci if (prism2_enable_genesis(local, 0x1f) == 0) 4238c2ecf20Sopenharmony_ci ret = 8; 4248c2ecf20Sopenharmony_ci else if (prism2_enable_genesis(local, 0x0f) == 0) 4258c2ecf20Sopenharmony_ci ret = 16; 4268c2ecf20Sopenharmony_ci else 4278c2ecf20Sopenharmony_ci ret = -1; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci /* Disable genesis mode */ 4308c2ecf20Sopenharmony_ci local->func->genesis_reset(local, ret == 16 ? 0x07 : 0x17); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci return ret; 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic int prism2_download_genesis(local_info_t *local, 4378c2ecf20Sopenharmony_ci struct prism2_download_data *param) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci struct net_device *dev = local->dev; 4408c2ecf20Sopenharmony_ci int ram16 = 0, i; 4418c2ecf20Sopenharmony_ci int ret = 0; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci if (local->hw_downloading) { 4448c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Already downloading - aborting new " 4458c2ecf20Sopenharmony_ci "request\n", dev->name); 4468c2ecf20Sopenharmony_ci return -EBUSY; 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci if (!local->func->genesis_reset || !local->func->cor_sreset) { 4508c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: Genesis mode downloading not supported " 4518c2ecf20Sopenharmony_ci "with this hwmodel\n", dev->name); 4528c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci local->hw_downloading = 1; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci if (prism2_enable_aux_port(dev, 1)) { 4588c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: failed to enable AUX port\n", 4598c2ecf20Sopenharmony_ci dev->name); 4608c2ecf20Sopenharmony_ci ret = -EIO; 4618c2ecf20Sopenharmony_ci goto out; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (local->sram_type == -1) { 4658c2ecf20Sopenharmony_ci /* 0x1F for x8 SRAM or 0x0F for x16 SRAM */ 4668c2ecf20Sopenharmony_ci if (prism2_enable_genesis(local, 0x1f) == 0) { 4678c2ecf20Sopenharmony_ci ram16 = 0; 4688c2ecf20Sopenharmony_ci PDEBUG(DEBUG_EXTRA2, "%s: Genesis mode OK using x8 " 4698c2ecf20Sopenharmony_ci "SRAM\n", dev->name); 4708c2ecf20Sopenharmony_ci } else if (prism2_enable_genesis(local, 0x0f) == 0) { 4718c2ecf20Sopenharmony_ci ram16 = 1; 4728c2ecf20Sopenharmony_ci PDEBUG(DEBUG_EXTRA2, "%s: Genesis mode OK using x16 " 4738c2ecf20Sopenharmony_ci "SRAM\n", dev->name); 4748c2ecf20Sopenharmony_ci } else { 4758c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: Could not initiate genesis " 4768c2ecf20Sopenharmony_ci "mode\n", dev->name); 4778c2ecf20Sopenharmony_ci ret = -EIO; 4788c2ecf20Sopenharmony_ci goto out; 4798c2ecf20Sopenharmony_ci } 4808c2ecf20Sopenharmony_ci } else { 4818c2ecf20Sopenharmony_ci if (prism2_enable_genesis(local, local->sram_type == 8 ? 4828c2ecf20Sopenharmony_ci 0x1f : 0x0f)) { 4838c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: Failed to set Genesis " 4848c2ecf20Sopenharmony_ci "mode (sram_type=%d)\n", dev->name, 4858c2ecf20Sopenharmony_ci local->sram_type); 4868c2ecf20Sopenharmony_ci ret = -EIO; 4878c2ecf20Sopenharmony_ci goto out; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci ram16 = local->sram_type != 8; 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci for (i = 0; i < param->num_areas; i++) { 4938c2ecf20Sopenharmony_ci PDEBUG(DEBUG_EXTRA2, "%s: Writing %d bytes at 0x%08x\n", 4948c2ecf20Sopenharmony_ci dev->name, param->data[i].len, param->data[i].addr); 4958c2ecf20Sopenharmony_ci if (hfa384x_to_aux(dev, param->data[i].addr, 4968c2ecf20Sopenharmony_ci param->data[i].len, param->data[i].data)) { 4978c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: RAM download at 0x%08x " 4988c2ecf20Sopenharmony_ci "(len=%d) failed\n", dev->name, 4998c2ecf20Sopenharmony_ci param->data[i].addr, param->data[i].len); 5008c2ecf20Sopenharmony_ci ret = -EIO; 5018c2ecf20Sopenharmony_ci goto out; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci PDEBUG(DEBUG_EXTRA2, "Disable genesis mode\n"); 5068c2ecf20Sopenharmony_ci local->func->genesis_reset(local, ram16 ? 0x07 : 0x17); 5078c2ecf20Sopenharmony_ci if (prism2_enable_aux_port(dev, 0)) { 5088c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: Failed to disable AUX port\n", 5098c2ecf20Sopenharmony_ci dev->name); 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci mdelay(5); 5138c2ecf20Sopenharmony_ci local->hw_downloading = 0; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci PDEBUG(DEBUG_EXTRA2, "Trying to initialize card\n"); 5168c2ecf20Sopenharmony_ci /* 5178c2ecf20Sopenharmony_ci * Make sure the INIT command does not generate a command completion 5188c2ecf20Sopenharmony_ci * event by disabling interrupts. 5198c2ecf20Sopenharmony_ci */ 5208c2ecf20Sopenharmony_ci hfa384x_disable_interrupts(dev); 5218c2ecf20Sopenharmony_ci if (prism2_hw_init(dev, 1)) { 5228c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: Initialization after genesis mode " 5238c2ecf20Sopenharmony_ci "download failed\n", dev->name); 5248c2ecf20Sopenharmony_ci ret = -EIO; 5258c2ecf20Sopenharmony_ci goto out; 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci PDEBUG(DEBUG_EXTRA2, "Card initialized - running PRI only\n"); 5298c2ecf20Sopenharmony_ci if (prism2_hw_init2(dev, 1)) { 5308c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: Initialization(2) after genesis mode " 5318c2ecf20Sopenharmony_ci "download failed\n", dev->name); 5328c2ecf20Sopenharmony_ci ret = -EIO; 5338c2ecf20Sopenharmony_ci goto out; 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci out: 5378c2ecf20Sopenharmony_ci local->hw_downloading = 0; 5388c2ecf20Sopenharmony_ci return ret; 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci#ifdef PRISM2_NON_VOLATILE_DOWNLOAD 5438c2ecf20Sopenharmony_ci/* Note! Non-volatile downloading functionality has not yet been tested 5448c2ecf20Sopenharmony_ci * thoroughly and it may corrupt flash image and effectively kill the card that 5458c2ecf20Sopenharmony_ci * is being updated. You have been warned. */ 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_cistatic inline int prism2_download_block(struct net_device *dev, 5488c2ecf20Sopenharmony_ci u32 addr, u8 *data, 5498c2ecf20Sopenharmony_ci u32 bufaddr, int rest_len) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci u16 param0, param1; 5528c2ecf20Sopenharmony_ci int block_len; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci block_len = rest_len < 4096 ? rest_len : 4096; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci param0 = addr & 0xffff; 5578c2ecf20Sopenharmony_ci param1 = addr >> 16; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci HFA384X_OUTW(block_len, HFA384X_PARAM2_OFF); 5608c2ecf20Sopenharmony_ci HFA384X_OUTW(param1, HFA384X_PARAM1_OFF); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | 5638c2ecf20Sopenharmony_ci (HFA384X_PROGMODE_ENABLE_NON_VOLATILE << 8), 5648c2ecf20Sopenharmony_ci param0)) { 5658c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Flash download command execution " 5668c2ecf20Sopenharmony_ci "failed\n", dev->name); 5678c2ecf20Sopenharmony_ci return -1; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci if (hfa384x_to_aux(dev, bufaddr, block_len, data)) { 5718c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: flash download at 0x%08x " 5728c2ecf20Sopenharmony_ci "(len=%d) failed\n", dev->name, addr, block_len); 5738c2ecf20Sopenharmony_ci return -1; 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci HFA384X_OUTW(0, HFA384X_PARAM2_OFF); 5778c2ecf20Sopenharmony_ci HFA384X_OUTW(0, HFA384X_PARAM1_OFF); 5788c2ecf20Sopenharmony_ci if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | 5798c2ecf20Sopenharmony_ci (HFA384X_PROGMODE_PROGRAM_NON_VOLATILE << 8), 5808c2ecf20Sopenharmony_ci 0)) { 5818c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Flash write command execution " 5828c2ecf20Sopenharmony_ci "failed\n", dev->name); 5838c2ecf20Sopenharmony_ci return -1; 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci return block_len; 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_cistatic int prism2_download_nonvolatile(local_info_t *local, 5918c2ecf20Sopenharmony_ci struct prism2_download_data *dl) 5928c2ecf20Sopenharmony_ci{ 5938c2ecf20Sopenharmony_ci struct net_device *dev = local->dev; 5948c2ecf20Sopenharmony_ci int ret = 0, i; 5958c2ecf20Sopenharmony_ci struct { 5968c2ecf20Sopenharmony_ci __le16 page; 5978c2ecf20Sopenharmony_ci __le16 offset; 5988c2ecf20Sopenharmony_ci __le16 len; 5998c2ecf20Sopenharmony_ci } dlbuffer; 6008c2ecf20Sopenharmony_ci u32 bufaddr; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci if (local->hw_downloading) { 6038c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Already downloading - aborting new " 6048c2ecf20Sopenharmony_ci "request\n", dev->name); 6058c2ecf20Sopenharmony_ci return -1; 6068c2ecf20Sopenharmony_ci } 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci ret = local->func->get_rid(dev, HFA384X_RID_DOWNLOADBUFFER, 6098c2ecf20Sopenharmony_ci &dlbuffer, 6, 0); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci if (ret < 0) { 6128c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Could not read download buffer " 6138c2ecf20Sopenharmony_ci "parameters\n", dev->name); 6148c2ecf20Sopenharmony_ci goto out; 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci printk(KERN_DEBUG "Download buffer: %d bytes at 0x%04x:0x%04x\n", 6188c2ecf20Sopenharmony_ci le16_to_cpu(dlbuffer.len), 6198c2ecf20Sopenharmony_ci le16_to_cpu(dlbuffer.page), 6208c2ecf20Sopenharmony_ci le16_to_cpu(dlbuffer.offset)); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci bufaddr = (le16_to_cpu(dlbuffer.page) << 7) + le16_to_cpu(dlbuffer.offset); 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci local->hw_downloading = 1; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci if (!local->pri_only) { 6278c2ecf20Sopenharmony_ci prism2_hw_shutdown(dev, 0); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci if (prism2_hw_init(dev, 0)) { 6308c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Could not initialize card for" 6318c2ecf20Sopenharmony_ci " download\n", dev->name); 6328c2ecf20Sopenharmony_ci ret = -1; 6338c2ecf20Sopenharmony_ci goto out; 6348c2ecf20Sopenharmony_ci } 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci hfa384x_disable_interrupts(dev); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci if (prism2_enable_aux_port(dev, 1)) { 6408c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Could not enable AUX port\n", 6418c2ecf20Sopenharmony_ci dev->name); 6428c2ecf20Sopenharmony_ci ret = -1; 6438c2ecf20Sopenharmony_ci goto out; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: starting flash download\n", dev->name); 6478c2ecf20Sopenharmony_ci for (i = 0; i < dl->num_areas; i++) { 6488c2ecf20Sopenharmony_ci int rest_len = dl->data[i].len; 6498c2ecf20Sopenharmony_ci int data_off = 0; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci while (rest_len > 0) { 6528c2ecf20Sopenharmony_ci int block_len; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci block_len = prism2_download_block( 6558c2ecf20Sopenharmony_ci dev, dl->data[i].addr + data_off, 6568c2ecf20Sopenharmony_ci dl->data[i].data + data_off, bufaddr, 6578c2ecf20Sopenharmony_ci rest_len); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci if (block_len < 0) { 6608c2ecf20Sopenharmony_ci ret = -1; 6618c2ecf20Sopenharmony_ci goto out; 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci rest_len -= block_len; 6658c2ecf20Sopenharmony_ci data_off += block_len; 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci HFA384X_OUTW(0, HFA384X_PARAM1_OFF); 6708c2ecf20Sopenharmony_ci HFA384X_OUTW(0, HFA384X_PARAM2_OFF); 6718c2ecf20Sopenharmony_ci if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | 6728c2ecf20Sopenharmony_ci (HFA384X_PROGMODE_DISABLE << 8), 0)) { 6738c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Download command execution failed\n", 6748c2ecf20Sopenharmony_ci dev->name); 6758c2ecf20Sopenharmony_ci ret = -1; 6768c2ecf20Sopenharmony_ci goto out; 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci if (prism2_enable_aux_port(dev, 0)) { 6808c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: Disabling AUX port failed\n", 6818c2ecf20Sopenharmony_ci dev->name); 6828c2ecf20Sopenharmony_ci /* continue anyway.. restart should have taken care of this */ 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci mdelay(5); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci local->func->hw_reset(dev); 6888c2ecf20Sopenharmony_ci local->hw_downloading = 0; 6898c2ecf20Sopenharmony_ci if (prism2_hw_config(dev, 2)) { 6908c2ecf20Sopenharmony_ci printk(KERN_WARNING "%s: Card configuration after flash " 6918c2ecf20Sopenharmony_ci "download failed\n", dev->name); 6928c2ecf20Sopenharmony_ci ret = -1; 6938c2ecf20Sopenharmony_ci } else { 6948c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: Card initialized successfully after " 6958c2ecf20Sopenharmony_ci "flash download\n", dev->name); 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci out: 6998c2ecf20Sopenharmony_ci local->hw_downloading = 0; 7008c2ecf20Sopenharmony_ci return ret; 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci#endif /* PRISM2_NON_VOLATILE_DOWNLOAD */ 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_cistatic void prism2_download_free_data(struct prism2_download_data *dl) 7068c2ecf20Sopenharmony_ci{ 7078c2ecf20Sopenharmony_ci int i; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci if (dl == NULL) 7108c2ecf20Sopenharmony_ci return; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci for (i = 0; i < dl->num_areas; i++) 7138c2ecf20Sopenharmony_ci kfree(dl->data[i].data); 7148c2ecf20Sopenharmony_ci kfree(dl); 7158c2ecf20Sopenharmony_ci} 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_cistatic int prism2_download(local_info_t *local, 7198c2ecf20Sopenharmony_ci struct prism2_download_param *param) 7208c2ecf20Sopenharmony_ci{ 7218c2ecf20Sopenharmony_ci int ret = 0; 7228c2ecf20Sopenharmony_ci int i; 7238c2ecf20Sopenharmony_ci u32 total_len = 0; 7248c2ecf20Sopenharmony_ci struct prism2_download_data *dl = NULL; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci printk(KERN_DEBUG "prism2_download: dl_cmd=%d start_addr=0x%08x " 7278c2ecf20Sopenharmony_ci "num_areas=%d\n", 7288c2ecf20Sopenharmony_ci param->dl_cmd, param->start_addr, param->num_areas); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci if (param->num_areas > 100) { 7318c2ecf20Sopenharmony_ci ret = -EINVAL; 7328c2ecf20Sopenharmony_ci goto out; 7338c2ecf20Sopenharmony_ci } 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci dl = kzalloc(sizeof(*dl) + param->num_areas * 7368c2ecf20Sopenharmony_ci sizeof(struct prism2_download_data_area), GFP_KERNEL); 7378c2ecf20Sopenharmony_ci if (dl == NULL) { 7388c2ecf20Sopenharmony_ci ret = -ENOMEM; 7398c2ecf20Sopenharmony_ci goto out; 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci dl->dl_cmd = param->dl_cmd; 7428c2ecf20Sopenharmony_ci dl->start_addr = param->start_addr; 7438c2ecf20Sopenharmony_ci dl->num_areas = param->num_areas; 7448c2ecf20Sopenharmony_ci for (i = 0; i < param->num_areas; i++) { 7458c2ecf20Sopenharmony_ci PDEBUG(DEBUG_EXTRA2, 7468c2ecf20Sopenharmony_ci " area %d: addr=0x%08x len=%d ptr=0x%p\n", 7478c2ecf20Sopenharmony_ci i, param->data[i].addr, param->data[i].len, 7488c2ecf20Sopenharmony_ci param->data[i].ptr); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci dl->data[i].addr = param->data[i].addr; 7518c2ecf20Sopenharmony_ci dl->data[i].len = param->data[i].len; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci total_len += param->data[i].len; 7548c2ecf20Sopenharmony_ci if (param->data[i].len > PRISM2_MAX_DOWNLOAD_AREA_LEN || 7558c2ecf20Sopenharmony_ci total_len > PRISM2_MAX_DOWNLOAD_LEN) { 7568c2ecf20Sopenharmony_ci ret = -E2BIG; 7578c2ecf20Sopenharmony_ci goto out; 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci dl->data[i].data = kmalloc(dl->data[i].len, GFP_KERNEL); 7618c2ecf20Sopenharmony_ci if (dl->data[i].data == NULL) { 7628c2ecf20Sopenharmony_ci ret = -ENOMEM; 7638c2ecf20Sopenharmony_ci goto out; 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci if (copy_from_user(dl->data[i].data, param->data[i].ptr, 7678c2ecf20Sopenharmony_ci param->data[i].len)) { 7688c2ecf20Sopenharmony_ci ret = -EFAULT; 7698c2ecf20Sopenharmony_ci goto out; 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci switch (param->dl_cmd) { 7748c2ecf20Sopenharmony_ci case PRISM2_DOWNLOAD_VOLATILE: 7758c2ecf20Sopenharmony_ci case PRISM2_DOWNLOAD_VOLATILE_PERSISTENT: 7768c2ecf20Sopenharmony_ci ret = prism2_download_volatile(local, dl); 7778c2ecf20Sopenharmony_ci break; 7788c2ecf20Sopenharmony_ci case PRISM2_DOWNLOAD_VOLATILE_GENESIS: 7798c2ecf20Sopenharmony_ci case PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT: 7808c2ecf20Sopenharmony_ci ret = prism2_download_genesis(local, dl); 7818c2ecf20Sopenharmony_ci break; 7828c2ecf20Sopenharmony_ci case PRISM2_DOWNLOAD_NON_VOLATILE: 7838c2ecf20Sopenharmony_ci#ifdef PRISM2_NON_VOLATILE_DOWNLOAD 7848c2ecf20Sopenharmony_ci ret = prism2_download_nonvolatile(local, dl); 7858c2ecf20Sopenharmony_ci#else /* PRISM2_NON_VOLATILE_DOWNLOAD */ 7868c2ecf20Sopenharmony_ci printk(KERN_INFO "%s: non-volatile downloading not enabled\n", 7878c2ecf20Sopenharmony_ci local->dev->name); 7888c2ecf20Sopenharmony_ci ret = -EOPNOTSUPP; 7898c2ecf20Sopenharmony_ci#endif /* PRISM2_NON_VOLATILE_DOWNLOAD */ 7908c2ecf20Sopenharmony_ci break; 7918c2ecf20Sopenharmony_ci default: 7928c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: unsupported download command %d\n", 7938c2ecf20Sopenharmony_ci local->dev->name, param->dl_cmd); 7948c2ecf20Sopenharmony_ci ret = -EINVAL; 7958c2ecf20Sopenharmony_ci break; 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci out: 7998c2ecf20Sopenharmony_ci if (ret == 0 && dl && 8008c2ecf20Sopenharmony_ci param->dl_cmd == PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT) { 8018c2ecf20Sopenharmony_ci prism2_download_free_data(local->dl_pri); 8028c2ecf20Sopenharmony_ci local->dl_pri = dl; 8038c2ecf20Sopenharmony_ci } else if (ret == 0 && dl && 8048c2ecf20Sopenharmony_ci param->dl_cmd == PRISM2_DOWNLOAD_VOLATILE_PERSISTENT) { 8058c2ecf20Sopenharmony_ci prism2_download_free_data(local->dl_sec); 8068c2ecf20Sopenharmony_ci local->dl_sec = dl; 8078c2ecf20Sopenharmony_ci } else 8088c2ecf20Sopenharmony_ci prism2_download_free_data(dl); 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci return ret; 8118c2ecf20Sopenharmony_ci} 812