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