1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
prism2_enable_aux_port(struct net_device * dev,int enable)2eb4f98d5SKalle Valo static int prism2_enable_aux_port(struct net_device *dev, int enable)
3eb4f98d5SKalle Valo {
4eb4f98d5SKalle Valo u16 val, reg;
5eb4f98d5SKalle Valo int i, tries;
6eb4f98d5SKalle Valo unsigned long flags;
7eb4f98d5SKalle Valo struct hostap_interface *iface;
8eb4f98d5SKalle Valo local_info_t *local;
9eb4f98d5SKalle Valo
10eb4f98d5SKalle Valo iface = netdev_priv(dev);
11eb4f98d5SKalle Valo local = iface->local;
12eb4f98d5SKalle Valo
13eb4f98d5SKalle Valo if (local->no_pri) {
14eb4f98d5SKalle Valo if (enable) {
15eb4f98d5SKalle Valo PDEBUG(DEBUG_EXTRA2, "%s: no PRI f/w - assuming Aux "
16eb4f98d5SKalle Valo "port is already enabled\n", dev->name);
17eb4f98d5SKalle Valo }
18eb4f98d5SKalle Valo return 0;
19eb4f98d5SKalle Valo }
20eb4f98d5SKalle Valo
21eb4f98d5SKalle Valo spin_lock_irqsave(&local->cmdlock, flags);
22eb4f98d5SKalle Valo
23eb4f98d5SKalle Valo /* wait until busy bit is clear */
24eb4f98d5SKalle Valo tries = HFA384X_CMD_BUSY_TIMEOUT;
25eb4f98d5SKalle Valo while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) {
26eb4f98d5SKalle Valo tries--;
27eb4f98d5SKalle Valo udelay(1);
28eb4f98d5SKalle Valo }
29eb4f98d5SKalle Valo if (tries == 0) {
30eb4f98d5SKalle Valo reg = HFA384X_INW(HFA384X_CMD_OFF);
31eb4f98d5SKalle Valo spin_unlock_irqrestore(&local->cmdlock, flags);
32eb4f98d5SKalle Valo printk("%s: prism2_enable_aux_port - timeout - reg=0x%04x\n",
33eb4f98d5SKalle Valo dev->name, reg);
34eb4f98d5SKalle Valo return -ETIMEDOUT;
35eb4f98d5SKalle Valo }
36eb4f98d5SKalle Valo
37eb4f98d5SKalle Valo val = HFA384X_INW(HFA384X_CONTROL_OFF);
38eb4f98d5SKalle Valo
39eb4f98d5SKalle Valo if (enable) {
40eb4f98d5SKalle Valo HFA384X_OUTW(HFA384X_AUX_MAGIC0, HFA384X_PARAM0_OFF);
41eb4f98d5SKalle Valo HFA384X_OUTW(HFA384X_AUX_MAGIC1, HFA384X_PARAM1_OFF);
42eb4f98d5SKalle Valo HFA384X_OUTW(HFA384X_AUX_MAGIC2, HFA384X_PARAM2_OFF);
43eb4f98d5SKalle Valo
44eb4f98d5SKalle Valo if ((val & HFA384X_AUX_PORT_MASK) != HFA384X_AUX_PORT_DISABLED)
45eb4f98d5SKalle Valo printk("prism2_enable_aux_port: was not disabled!?\n");
46eb4f98d5SKalle Valo val &= ~HFA384X_AUX_PORT_MASK;
47eb4f98d5SKalle Valo val |= HFA384X_AUX_PORT_ENABLE;
48eb4f98d5SKalle Valo } else {
49eb4f98d5SKalle Valo HFA384X_OUTW(0, HFA384X_PARAM0_OFF);
50eb4f98d5SKalle Valo HFA384X_OUTW(0, HFA384X_PARAM1_OFF);
51eb4f98d5SKalle Valo HFA384X_OUTW(0, HFA384X_PARAM2_OFF);
52eb4f98d5SKalle Valo
53eb4f98d5SKalle Valo if ((val & HFA384X_AUX_PORT_MASK) != HFA384X_AUX_PORT_ENABLED)
54eb4f98d5SKalle Valo printk("prism2_enable_aux_port: was not enabled!?\n");
55eb4f98d5SKalle Valo val &= ~HFA384X_AUX_PORT_MASK;
56eb4f98d5SKalle Valo val |= HFA384X_AUX_PORT_DISABLE;
57eb4f98d5SKalle Valo }
58eb4f98d5SKalle Valo HFA384X_OUTW(val, HFA384X_CONTROL_OFF);
59eb4f98d5SKalle Valo
60eb4f98d5SKalle Valo udelay(5);
61eb4f98d5SKalle Valo
62eb4f98d5SKalle Valo i = 10000;
63eb4f98d5SKalle Valo while (i > 0) {
64eb4f98d5SKalle Valo val = HFA384X_INW(HFA384X_CONTROL_OFF);
65eb4f98d5SKalle Valo val &= HFA384X_AUX_PORT_MASK;
66eb4f98d5SKalle Valo
67eb4f98d5SKalle Valo if ((enable && val == HFA384X_AUX_PORT_ENABLED) ||
68eb4f98d5SKalle Valo (!enable && val == HFA384X_AUX_PORT_DISABLED))
69eb4f98d5SKalle Valo break;
70eb4f98d5SKalle Valo
71eb4f98d5SKalle Valo udelay(10);
72eb4f98d5SKalle Valo i--;
73eb4f98d5SKalle Valo }
74eb4f98d5SKalle Valo
75eb4f98d5SKalle Valo spin_unlock_irqrestore(&local->cmdlock, flags);
76eb4f98d5SKalle Valo
77eb4f98d5SKalle Valo if (i == 0) {
78eb4f98d5SKalle Valo printk("prism2_enable_aux_port(%d) timed out\n",
79eb4f98d5SKalle Valo enable);
80eb4f98d5SKalle Valo return -ETIMEDOUT;
81eb4f98d5SKalle Valo }
82eb4f98d5SKalle Valo
83eb4f98d5SKalle Valo return 0;
84eb4f98d5SKalle Valo }
85eb4f98d5SKalle Valo
86eb4f98d5SKalle Valo
hfa384x_from_aux(struct net_device * dev,unsigned int addr,int len,void * buf)87eb4f98d5SKalle Valo static int hfa384x_from_aux(struct net_device *dev, unsigned int addr, int len,
88eb4f98d5SKalle Valo void *buf)
89eb4f98d5SKalle Valo {
90eb4f98d5SKalle Valo u16 page, offset;
91eb4f98d5SKalle Valo if (addr & 1 || len & 1)
92eb4f98d5SKalle Valo return -1;
93eb4f98d5SKalle Valo
94eb4f98d5SKalle Valo page = addr >> 7;
95eb4f98d5SKalle Valo offset = addr & 0x7f;
96eb4f98d5SKalle Valo
97eb4f98d5SKalle Valo HFA384X_OUTW(page, HFA384X_AUXPAGE_OFF);
98eb4f98d5SKalle Valo HFA384X_OUTW(offset, HFA384X_AUXOFFSET_OFF);
99eb4f98d5SKalle Valo
100eb4f98d5SKalle Valo udelay(5);
101eb4f98d5SKalle Valo
102eb4f98d5SKalle Valo #ifdef PRISM2_PCI
103eb4f98d5SKalle Valo {
104eb4f98d5SKalle Valo __le16 *pos = (__le16 *) buf;
105eb4f98d5SKalle Valo while (len > 0) {
106eb4f98d5SKalle Valo *pos++ = HFA384X_INW_DATA(HFA384X_AUXDATA_OFF);
107eb4f98d5SKalle Valo len -= 2;
108eb4f98d5SKalle Valo }
109eb4f98d5SKalle Valo }
110eb4f98d5SKalle Valo #else /* PRISM2_PCI */
111eb4f98d5SKalle Valo HFA384X_INSW(HFA384X_AUXDATA_OFF, buf, len / 2);
112eb4f98d5SKalle Valo #endif /* PRISM2_PCI */
113eb4f98d5SKalle Valo
114eb4f98d5SKalle Valo return 0;
115eb4f98d5SKalle Valo }
116eb4f98d5SKalle Valo
117eb4f98d5SKalle Valo
hfa384x_to_aux(struct net_device * dev,unsigned int addr,int len,void * buf)118eb4f98d5SKalle Valo static int hfa384x_to_aux(struct net_device *dev, unsigned int addr, int len,
119eb4f98d5SKalle Valo void *buf)
120eb4f98d5SKalle Valo {
121eb4f98d5SKalle Valo u16 page, offset;
122eb4f98d5SKalle Valo if (addr & 1 || len & 1)
123eb4f98d5SKalle Valo return -1;
124eb4f98d5SKalle Valo
125eb4f98d5SKalle Valo page = addr >> 7;
126eb4f98d5SKalle Valo offset = addr & 0x7f;
127eb4f98d5SKalle Valo
128eb4f98d5SKalle Valo HFA384X_OUTW(page, HFA384X_AUXPAGE_OFF);
129eb4f98d5SKalle Valo HFA384X_OUTW(offset, HFA384X_AUXOFFSET_OFF);
130eb4f98d5SKalle Valo
131eb4f98d5SKalle Valo udelay(5);
132eb4f98d5SKalle Valo
133eb4f98d5SKalle Valo #ifdef PRISM2_PCI
134eb4f98d5SKalle Valo {
135eb4f98d5SKalle Valo __le16 *pos = (__le16 *) buf;
136eb4f98d5SKalle Valo while (len > 0) {
137eb4f98d5SKalle Valo HFA384X_OUTW_DATA(*pos++, HFA384X_AUXDATA_OFF);
138eb4f98d5SKalle Valo len -= 2;
139eb4f98d5SKalle Valo }
140eb4f98d5SKalle Valo }
141eb4f98d5SKalle Valo #else /* PRISM2_PCI */
142eb4f98d5SKalle Valo HFA384X_OUTSW(HFA384X_AUXDATA_OFF, buf, len / 2);
143eb4f98d5SKalle Valo #endif /* PRISM2_PCI */
144eb4f98d5SKalle Valo
145eb4f98d5SKalle Valo return 0;
146eb4f98d5SKalle Valo }
147eb4f98d5SKalle Valo
148eb4f98d5SKalle Valo
prism2_pda_ok(u8 * buf)149eb4f98d5SKalle Valo static int prism2_pda_ok(u8 *buf)
150eb4f98d5SKalle Valo {
151eb4f98d5SKalle Valo __le16 *pda = (__le16 *) buf;
152eb4f98d5SKalle Valo int pos;
153eb4f98d5SKalle Valo u16 len, pdr;
154eb4f98d5SKalle Valo
155eb4f98d5SKalle Valo if (buf[0] == 0xff && buf[1] == 0x00 && buf[2] == 0xff &&
156eb4f98d5SKalle Valo buf[3] == 0x00)
157eb4f98d5SKalle Valo return 0;
158eb4f98d5SKalle Valo
159eb4f98d5SKalle Valo pos = 0;
160eb4f98d5SKalle Valo while (pos + 1 < PRISM2_PDA_SIZE / 2) {
161eb4f98d5SKalle Valo len = le16_to_cpu(pda[pos]);
162eb4f98d5SKalle Valo pdr = le16_to_cpu(pda[pos + 1]);
163eb4f98d5SKalle Valo if (len == 0 || pos + len > PRISM2_PDA_SIZE / 2)
164eb4f98d5SKalle Valo return 0;
165eb4f98d5SKalle Valo
166eb4f98d5SKalle Valo if (pdr == 0x0000 && len == 2) {
167eb4f98d5SKalle Valo /* PDA end found */
168eb4f98d5SKalle Valo return 1;
169eb4f98d5SKalle Valo }
170eb4f98d5SKalle Valo
171eb4f98d5SKalle Valo pos += len + 1;
172eb4f98d5SKalle Valo }
173eb4f98d5SKalle Valo
174eb4f98d5SKalle Valo return 0;
175eb4f98d5SKalle Valo }
176eb4f98d5SKalle Valo
177eb4f98d5SKalle Valo
178eb4f98d5SKalle Valo #define prism2_download_aux_dump_npages 65536
179eb4f98d5SKalle Valo
180eb4f98d5SKalle Valo struct prism2_download_aux_dump {
181eb4f98d5SKalle Valo local_info_t *local;
182eb4f98d5SKalle Valo u16 page[0x80];
183eb4f98d5SKalle Valo };
184eb4f98d5SKalle Valo
prism2_download_aux_dump_proc_show(struct seq_file * m,void * v)185eb4f98d5SKalle Valo static int prism2_download_aux_dump_proc_show(struct seq_file *m, void *v)
186eb4f98d5SKalle Valo {
187eb4f98d5SKalle Valo struct prism2_download_aux_dump *ctx = m->private;
188eb4f98d5SKalle Valo
189eb4f98d5SKalle Valo hfa384x_from_aux(ctx->local->dev, (unsigned long)v - 1, 0x80, ctx->page);
190eb4f98d5SKalle Valo seq_write(m, ctx->page, 0x80);
191eb4f98d5SKalle Valo return 0;
192eb4f98d5SKalle Valo }
193eb4f98d5SKalle Valo
prism2_download_aux_dump_proc_start(struct seq_file * m,loff_t * _pos)194eb4f98d5SKalle Valo static void *prism2_download_aux_dump_proc_start(struct seq_file *m, loff_t *_pos)
195eb4f98d5SKalle Valo {
196eb4f98d5SKalle Valo struct prism2_download_aux_dump *ctx = m->private;
197eb4f98d5SKalle Valo prism2_enable_aux_port(ctx->local->dev, 1);
198eb4f98d5SKalle Valo if (*_pos >= prism2_download_aux_dump_npages)
199eb4f98d5SKalle Valo return NULL;
200eb4f98d5SKalle Valo return (void *)((unsigned long)*_pos + 1);
201eb4f98d5SKalle Valo }
202eb4f98d5SKalle Valo
prism2_download_aux_dump_proc_next(struct seq_file * m,void * v,loff_t * _pos)203eb4f98d5SKalle Valo static void *prism2_download_aux_dump_proc_next(struct seq_file *m, void *v, loff_t *_pos)
204eb4f98d5SKalle Valo {
205eb4f98d5SKalle Valo ++*_pos;
206eb4f98d5SKalle Valo if (*_pos >= prism2_download_aux_dump_npages)
207eb4f98d5SKalle Valo return NULL;
208eb4f98d5SKalle Valo return (void *)((unsigned long)*_pos + 1);
209eb4f98d5SKalle Valo }
210eb4f98d5SKalle Valo
prism2_download_aux_dump_proc_stop(struct seq_file * m,void * v)211eb4f98d5SKalle Valo static void prism2_download_aux_dump_proc_stop(struct seq_file *m, void *v)
212eb4f98d5SKalle Valo {
213eb4f98d5SKalle Valo struct prism2_download_aux_dump *ctx = m->private;
214eb4f98d5SKalle Valo prism2_enable_aux_port(ctx->local->dev, 0);
215eb4f98d5SKalle Valo }
216eb4f98d5SKalle Valo
217eb4f98d5SKalle Valo static const struct seq_operations prism2_download_aux_dump_proc_seqops = {
218eb4f98d5SKalle Valo .start = prism2_download_aux_dump_proc_start,
219eb4f98d5SKalle Valo .next = prism2_download_aux_dump_proc_next,
220eb4f98d5SKalle Valo .stop = prism2_download_aux_dump_proc_stop,
221eb4f98d5SKalle Valo .show = prism2_download_aux_dump_proc_show,
222eb4f98d5SKalle Valo };
223eb4f98d5SKalle Valo
prism2_download_aux_dump_proc_open(struct inode * inode,struct file * file)224eb4f98d5SKalle Valo static int prism2_download_aux_dump_proc_open(struct inode *inode, struct file *file)
225eb4f98d5SKalle Valo {
226eb4f98d5SKalle Valo int ret = seq_open_private(file, &prism2_download_aux_dump_proc_seqops,
227eb4f98d5SKalle Valo sizeof(struct prism2_download_aux_dump));
228eb4f98d5SKalle Valo if (ret == 0) {
229eb4f98d5SKalle Valo struct seq_file *m = file->private_data;
230*359745d7SMuchun Song m->private = pde_data(inode);
231eb4f98d5SKalle Valo }
232eb4f98d5SKalle Valo return ret;
233eb4f98d5SKalle Valo }
234eb4f98d5SKalle Valo
2353af4da16SYueHaibing static const struct proc_ops prism2_download_aux_dump_proc_ops = {
2363af4da16SYueHaibing .proc_open = prism2_download_aux_dump_proc_open,
2373af4da16SYueHaibing .proc_read = seq_read,
2383af4da16SYueHaibing .proc_lseek = seq_lseek,
2393af4da16SYueHaibing .proc_release = seq_release_private,
240eb4f98d5SKalle Valo };
241eb4f98d5SKalle Valo
242eb4f98d5SKalle Valo
prism2_read_pda(struct net_device * dev)243eb4f98d5SKalle Valo static u8 * prism2_read_pda(struct net_device *dev)
244eb4f98d5SKalle Valo {
245eb4f98d5SKalle Valo u8 *buf;
246eb4f98d5SKalle Valo int res, i, found = 0;
247eb4f98d5SKalle Valo #define NUM_PDA_ADDRS 4
248eb4f98d5SKalle Valo unsigned int pda_addr[NUM_PDA_ADDRS] = {
249eb4f98d5SKalle Valo 0x7f0000 /* others than HFA3841 */,
250eb4f98d5SKalle Valo 0x3f0000 /* HFA3841 */,
251eb4f98d5SKalle Valo 0x390000 /* apparently used in older cards */,
252eb4f98d5SKalle Valo 0x7f0002 /* Intel PRO/Wireless 2011B (PCI) */,
253eb4f98d5SKalle Valo };
254eb4f98d5SKalle Valo
255eb4f98d5SKalle Valo buf = kmalloc(PRISM2_PDA_SIZE, GFP_KERNEL);
256eb4f98d5SKalle Valo if (buf == NULL)
257eb4f98d5SKalle Valo return NULL;
258eb4f98d5SKalle Valo
259eb4f98d5SKalle Valo /* Note: wlan card should be in initial state (just after init cmd)
260eb4f98d5SKalle Valo * and no other operations should be performed concurrently. */
261eb4f98d5SKalle Valo
262eb4f98d5SKalle Valo prism2_enable_aux_port(dev, 1);
263eb4f98d5SKalle Valo
264eb4f98d5SKalle Valo for (i = 0; i < NUM_PDA_ADDRS; i++) {
265eb4f98d5SKalle Valo PDEBUG(DEBUG_EXTRA2, "%s: trying to read PDA from 0x%08x",
266eb4f98d5SKalle Valo dev->name, pda_addr[i]);
267eb4f98d5SKalle Valo res = hfa384x_from_aux(dev, pda_addr[i], PRISM2_PDA_SIZE, buf);
268eb4f98d5SKalle Valo if (res)
269eb4f98d5SKalle Valo continue;
270eb4f98d5SKalle Valo if (res == 0 && prism2_pda_ok(buf)) {
271eb4f98d5SKalle Valo PDEBUG2(DEBUG_EXTRA2, ": OK\n");
272eb4f98d5SKalle Valo found = 1;
273eb4f98d5SKalle Valo break;
274eb4f98d5SKalle Valo } else {
275eb4f98d5SKalle Valo PDEBUG2(DEBUG_EXTRA2, ": failed\n");
276eb4f98d5SKalle Valo }
277eb4f98d5SKalle Valo }
278eb4f98d5SKalle Valo
279eb4f98d5SKalle Valo prism2_enable_aux_port(dev, 0);
280eb4f98d5SKalle Valo
281eb4f98d5SKalle Valo if (!found) {
282eb4f98d5SKalle Valo printk(KERN_DEBUG "%s: valid PDA not found\n", dev->name);
283eb4f98d5SKalle Valo kfree(buf);
284eb4f98d5SKalle Valo buf = NULL;
285eb4f98d5SKalle Valo }
286eb4f98d5SKalle Valo
287eb4f98d5SKalle Valo return buf;
288eb4f98d5SKalle Valo }
289eb4f98d5SKalle Valo
290eb4f98d5SKalle Valo
prism2_download_volatile(local_info_t * local,struct prism2_download_data * param)291eb4f98d5SKalle Valo static int prism2_download_volatile(local_info_t *local,
292eb4f98d5SKalle Valo struct prism2_download_data *param)
293eb4f98d5SKalle Valo {
294eb4f98d5SKalle Valo struct net_device *dev = local->dev;
295eb4f98d5SKalle Valo int ret = 0, i;
296eb4f98d5SKalle Valo u16 param0, param1;
297eb4f98d5SKalle Valo
298eb4f98d5SKalle Valo if (local->hw_downloading) {
299eb4f98d5SKalle Valo printk(KERN_WARNING "%s: Already downloading - aborting new "
300eb4f98d5SKalle Valo "request\n", dev->name);
301eb4f98d5SKalle Valo return -1;
302eb4f98d5SKalle Valo }
303eb4f98d5SKalle Valo
304eb4f98d5SKalle Valo local->hw_downloading = 1;
305eb4f98d5SKalle Valo if (local->pri_only) {
306eb4f98d5SKalle Valo hfa384x_disable_interrupts(dev);
307eb4f98d5SKalle Valo } else {
308eb4f98d5SKalle Valo prism2_hw_shutdown(dev, 0);
309eb4f98d5SKalle Valo
310eb4f98d5SKalle Valo if (prism2_hw_init(dev, 0)) {
311eb4f98d5SKalle Valo printk(KERN_WARNING "%s: Could not initialize card for"
312eb4f98d5SKalle Valo " download\n", dev->name);
313eb4f98d5SKalle Valo ret = -1;
314eb4f98d5SKalle Valo goto out;
315eb4f98d5SKalle Valo }
316eb4f98d5SKalle Valo }
317eb4f98d5SKalle Valo
318eb4f98d5SKalle Valo if (prism2_enable_aux_port(dev, 1)) {
319eb4f98d5SKalle Valo printk(KERN_WARNING "%s: Could not enable AUX port\n",
320eb4f98d5SKalle Valo dev->name);
321eb4f98d5SKalle Valo ret = -1;
322eb4f98d5SKalle Valo goto out;
323eb4f98d5SKalle Valo }
324eb4f98d5SKalle Valo
325eb4f98d5SKalle Valo param0 = param->start_addr & 0xffff;
326eb4f98d5SKalle Valo param1 = param->start_addr >> 16;
327eb4f98d5SKalle Valo
328eb4f98d5SKalle Valo HFA384X_OUTW(0, HFA384X_PARAM2_OFF);
329eb4f98d5SKalle Valo HFA384X_OUTW(param1, HFA384X_PARAM1_OFF);
330eb4f98d5SKalle Valo if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD |
331eb4f98d5SKalle Valo (HFA384X_PROGMODE_ENABLE_VOLATILE << 8),
332eb4f98d5SKalle Valo param0)) {
333eb4f98d5SKalle Valo printk(KERN_WARNING "%s: Download command execution failed\n",
334eb4f98d5SKalle Valo dev->name);
335eb4f98d5SKalle Valo ret = -1;
336eb4f98d5SKalle Valo goto out;
337eb4f98d5SKalle Valo }
338eb4f98d5SKalle Valo
339eb4f98d5SKalle Valo for (i = 0; i < param->num_areas; i++) {
340eb4f98d5SKalle Valo PDEBUG(DEBUG_EXTRA2, "%s: Writing %d bytes at 0x%08x\n",
341eb4f98d5SKalle Valo dev->name, param->data[i].len, param->data[i].addr);
342eb4f98d5SKalle Valo if (hfa384x_to_aux(dev, param->data[i].addr,
343eb4f98d5SKalle Valo param->data[i].len, param->data[i].data)) {
344eb4f98d5SKalle Valo printk(KERN_WARNING "%s: RAM download at 0x%08x "
345eb4f98d5SKalle Valo "(len=%d) failed\n", dev->name,
346eb4f98d5SKalle Valo param->data[i].addr, param->data[i].len);
347eb4f98d5SKalle Valo ret = -1;
348eb4f98d5SKalle Valo goto out;
349eb4f98d5SKalle Valo }
350eb4f98d5SKalle Valo }
351eb4f98d5SKalle Valo
352eb4f98d5SKalle Valo HFA384X_OUTW(param1, HFA384X_PARAM1_OFF);
353eb4f98d5SKalle Valo HFA384X_OUTW(0, HFA384X_PARAM2_OFF);
354eb4f98d5SKalle Valo if (hfa384x_cmd_no_wait(dev, HFA384X_CMDCODE_DOWNLOAD |
355eb4f98d5SKalle Valo (HFA384X_PROGMODE_DISABLE << 8), param0)) {
356eb4f98d5SKalle Valo printk(KERN_WARNING "%s: Download command execution failed\n",
357eb4f98d5SKalle Valo dev->name);
358eb4f98d5SKalle Valo ret = -1;
359eb4f98d5SKalle Valo goto out;
360eb4f98d5SKalle Valo }
361eb4f98d5SKalle Valo /* ProgMode disable causes the hardware to restart itself from the
362eb4f98d5SKalle Valo * given starting address. Give hw some time and ACK command just in
363eb4f98d5SKalle Valo * case restart did not happen. */
364eb4f98d5SKalle Valo mdelay(5);
365eb4f98d5SKalle Valo HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF);
366eb4f98d5SKalle Valo
367eb4f98d5SKalle Valo if (prism2_enable_aux_port(dev, 0)) {
368eb4f98d5SKalle Valo printk(KERN_DEBUG "%s: Disabling AUX port failed\n",
369eb4f98d5SKalle Valo dev->name);
370eb4f98d5SKalle Valo /* continue anyway.. restart should have taken care of this */
371eb4f98d5SKalle Valo }
372eb4f98d5SKalle Valo
373eb4f98d5SKalle Valo mdelay(5);
374eb4f98d5SKalle Valo local->hw_downloading = 0;
375eb4f98d5SKalle Valo if (prism2_hw_config(dev, 2)) {
376eb4f98d5SKalle Valo printk(KERN_WARNING "%s: Card configuration after RAM "
377eb4f98d5SKalle Valo "download failed\n", dev->name);
378eb4f98d5SKalle Valo ret = -1;
379eb4f98d5SKalle Valo goto out;
380eb4f98d5SKalle Valo }
381eb4f98d5SKalle Valo
382eb4f98d5SKalle Valo out:
383eb4f98d5SKalle Valo local->hw_downloading = 0;
384eb4f98d5SKalle Valo return ret;
385eb4f98d5SKalle Valo }
386eb4f98d5SKalle Valo
387eb4f98d5SKalle Valo
prism2_enable_genesis(local_info_t * local,int hcr)388eb4f98d5SKalle Valo static int prism2_enable_genesis(local_info_t *local, int hcr)
389eb4f98d5SKalle Valo {
390eb4f98d5SKalle Valo struct net_device *dev = local->dev;
391eb4f98d5SKalle Valo u8 initseq[4] = { 0x00, 0xe1, 0xa1, 0xff };
392eb4f98d5SKalle Valo u8 readbuf[4];
393eb4f98d5SKalle Valo
394eb4f98d5SKalle Valo printk(KERN_DEBUG "%s: test Genesis mode with HCR 0x%02x\n",
395eb4f98d5SKalle Valo dev->name, hcr);
396eb4f98d5SKalle Valo local->func->cor_sreset(local);
397eb4f98d5SKalle Valo hfa384x_to_aux(dev, 0x7e0038, sizeof(initseq), initseq);
398eb4f98d5SKalle Valo local->func->genesis_reset(local, hcr);
399eb4f98d5SKalle Valo
400eb4f98d5SKalle Valo /* Readback test */
401eb4f98d5SKalle Valo hfa384x_from_aux(dev, 0x7e0038, sizeof(readbuf), readbuf);
402eb4f98d5SKalle Valo hfa384x_to_aux(dev, 0x7e0038, sizeof(initseq), initseq);
403eb4f98d5SKalle Valo hfa384x_from_aux(dev, 0x7e0038, sizeof(readbuf), readbuf);
404eb4f98d5SKalle Valo
405eb4f98d5SKalle Valo if (memcmp(initseq, readbuf, sizeof(initseq)) == 0) {
406eb4f98d5SKalle Valo printk(KERN_DEBUG "Readback test succeeded, HCR 0x%02x\n",
407eb4f98d5SKalle Valo hcr);
408eb4f98d5SKalle Valo return 0;
409eb4f98d5SKalle Valo } else {
410290890dfSAndy Shevchenko printk(KERN_DEBUG "Readback test failed, HCR 0x%02x write %4ph read %4ph\n",
411290890dfSAndy Shevchenko hcr, initseq, readbuf);
412eb4f98d5SKalle Valo return 1;
413eb4f98d5SKalle Valo }
414eb4f98d5SKalle Valo }
415eb4f98d5SKalle Valo
416eb4f98d5SKalle Valo
prism2_get_ram_size(local_info_t * local)417eb4f98d5SKalle Valo static int prism2_get_ram_size(local_info_t *local)
418eb4f98d5SKalle Valo {
419eb4f98d5SKalle Valo int ret;
420eb4f98d5SKalle Valo
421eb4f98d5SKalle Valo /* Try to enable genesis mode; 0x1F for x8 SRAM or 0x0F for x16 SRAM */
422eb4f98d5SKalle Valo if (prism2_enable_genesis(local, 0x1f) == 0)
423eb4f98d5SKalle Valo ret = 8;
424eb4f98d5SKalle Valo else if (prism2_enable_genesis(local, 0x0f) == 0)
425eb4f98d5SKalle Valo ret = 16;
426eb4f98d5SKalle Valo else
427eb4f98d5SKalle Valo ret = -1;
428eb4f98d5SKalle Valo
429eb4f98d5SKalle Valo /* Disable genesis mode */
430eb4f98d5SKalle Valo local->func->genesis_reset(local, ret == 16 ? 0x07 : 0x17);
431eb4f98d5SKalle Valo
432eb4f98d5SKalle Valo return ret;
433eb4f98d5SKalle Valo }
434eb4f98d5SKalle Valo
435eb4f98d5SKalle Valo
prism2_download_genesis(local_info_t * local,struct prism2_download_data * param)436eb4f98d5SKalle Valo static int prism2_download_genesis(local_info_t *local,
437eb4f98d5SKalle Valo struct prism2_download_data *param)
438eb4f98d5SKalle Valo {
439eb4f98d5SKalle Valo struct net_device *dev = local->dev;
440eb4f98d5SKalle Valo int ram16 = 0, i;
441eb4f98d5SKalle Valo int ret = 0;
442eb4f98d5SKalle Valo
443eb4f98d5SKalle Valo if (local->hw_downloading) {
444eb4f98d5SKalle Valo printk(KERN_WARNING "%s: Already downloading - aborting new "
445eb4f98d5SKalle Valo "request\n", dev->name);
446eb4f98d5SKalle Valo return -EBUSY;
447eb4f98d5SKalle Valo }
448eb4f98d5SKalle Valo
449eb4f98d5SKalle Valo if (!local->func->genesis_reset || !local->func->cor_sreset) {
450eb4f98d5SKalle Valo printk(KERN_INFO "%s: Genesis mode downloading not supported "
451eb4f98d5SKalle Valo "with this hwmodel\n", dev->name);
452eb4f98d5SKalle Valo return -EOPNOTSUPP;
453eb4f98d5SKalle Valo }
454eb4f98d5SKalle Valo
455eb4f98d5SKalle Valo local->hw_downloading = 1;
456eb4f98d5SKalle Valo
457eb4f98d5SKalle Valo if (prism2_enable_aux_port(dev, 1)) {
458eb4f98d5SKalle Valo printk(KERN_DEBUG "%s: failed to enable AUX port\n",
459eb4f98d5SKalle Valo dev->name);
460eb4f98d5SKalle Valo ret = -EIO;
461eb4f98d5SKalle Valo goto out;
462eb4f98d5SKalle Valo }
463eb4f98d5SKalle Valo
464eb4f98d5SKalle Valo if (local->sram_type == -1) {
465eb4f98d5SKalle Valo /* 0x1F for x8 SRAM or 0x0F for x16 SRAM */
466eb4f98d5SKalle Valo if (prism2_enable_genesis(local, 0x1f) == 0) {
467eb4f98d5SKalle Valo ram16 = 0;
468eb4f98d5SKalle Valo PDEBUG(DEBUG_EXTRA2, "%s: Genesis mode OK using x8 "
469eb4f98d5SKalle Valo "SRAM\n", dev->name);
470eb4f98d5SKalle Valo } else if (prism2_enable_genesis(local, 0x0f) == 0) {
471eb4f98d5SKalle Valo ram16 = 1;
472eb4f98d5SKalle Valo PDEBUG(DEBUG_EXTRA2, "%s: Genesis mode OK using x16 "
473eb4f98d5SKalle Valo "SRAM\n", dev->name);
474eb4f98d5SKalle Valo } else {
475eb4f98d5SKalle Valo printk(KERN_DEBUG "%s: Could not initiate genesis "
476eb4f98d5SKalle Valo "mode\n", dev->name);
477eb4f98d5SKalle Valo ret = -EIO;
478eb4f98d5SKalle Valo goto out;
479eb4f98d5SKalle Valo }
480eb4f98d5SKalle Valo } else {
481eb4f98d5SKalle Valo if (prism2_enable_genesis(local, local->sram_type == 8 ?
482eb4f98d5SKalle Valo 0x1f : 0x0f)) {
483eb4f98d5SKalle Valo printk(KERN_DEBUG "%s: Failed to set Genesis "
484eb4f98d5SKalle Valo "mode (sram_type=%d)\n", dev->name,
485eb4f98d5SKalle Valo local->sram_type);
486eb4f98d5SKalle Valo ret = -EIO;
487eb4f98d5SKalle Valo goto out;
488eb4f98d5SKalle Valo }
489eb4f98d5SKalle Valo ram16 = local->sram_type != 8;
490eb4f98d5SKalle Valo }
491eb4f98d5SKalle Valo
492eb4f98d5SKalle Valo for (i = 0; i < param->num_areas; i++) {
493eb4f98d5SKalle Valo PDEBUG(DEBUG_EXTRA2, "%s: Writing %d bytes at 0x%08x\n",
494eb4f98d5SKalle Valo dev->name, param->data[i].len, param->data[i].addr);
495eb4f98d5SKalle Valo if (hfa384x_to_aux(dev, param->data[i].addr,
496eb4f98d5SKalle Valo param->data[i].len, param->data[i].data)) {
497eb4f98d5SKalle Valo printk(KERN_WARNING "%s: RAM download at 0x%08x "
498eb4f98d5SKalle Valo "(len=%d) failed\n", dev->name,
499eb4f98d5SKalle Valo param->data[i].addr, param->data[i].len);
500eb4f98d5SKalle Valo ret = -EIO;
501eb4f98d5SKalle Valo goto out;
502eb4f98d5SKalle Valo }
503eb4f98d5SKalle Valo }
504eb4f98d5SKalle Valo
505eb4f98d5SKalle Valo PDEBUG(DEBUG_EXTRA2, "Disable genesis mode\n");
506eb4f98d5SKalle Valo local->func->genesis_reset(local, ram16 ? 0x07 : 0x17);
507eb4f98d5SKalle Valo if (prism2_enable_aux_port(dev, 0)) {
508eb4f98d5SKalle Valo printk(KERN_DEBUG "%s: Failed to disable AUX port\n",
509eb4f98d5SKalle Valo dev->name);
510eb4f98d5SKalle Valo }
511eb4f98d5SKalle Valo
512eb4f98d5SKalle Valo mdelay(5);
513eb4f98d5SKalle Valo local->hw_downloading = 0;
514eb4f98d5SKalle Valo
515eb4f98d5SKalle Valo PDEBUG(DEBUG_EXTRA2, "Trying to initialize card\n");
516eb4f98d5SKalle Valo /*
517eb4f98d5SKalle Valo * Make sure the INIT command does not generate a command completion
518eb4f98d5SKalle Valo * event by disabling interrupts.
519eb4f98d5SKalle Valo */
520eb4f98d5SKalle Valo hfa384x_disable_interrupts(dev);
521eb4f98d5SKalle Valo if (prism2_hw_init(dev, 1)) {
522eb4f98d5SKalle Valo printk(KERN_DEBUG "%s: Initialization after genesis mode "
523eb4f98d5SKalle Valo "download failed\n", dev->name);
524eb4f98d5SKalle Valo ret = -EIO;
525eb4f98d5SKalle Valo goto out;
526eb4f98d5SKalle Valo }
527eb4f98d5SKalle Valo
528eb4f98d5SKalle Valo PDEBUG(DEBUG_EXTRA2, "Card initialized - running PRI only\n");
529eb4f98d5SKalle Valo if (prism2_hw_init2(dev, 1)) {
530eb4f98d5SKalle Valo printk(KERN_DEBUG "%s: Initialization(2) after genesis mode "
531eb4f98d5SKalle Valo "download failed\n", dev->name);
532eb4f98d5SKalle Valo ret = -EIO;
533eb4f98d5SKalle Valo goto out;
534eb4f98d5SKalle Valo }
535eb4f98d5SKalle Valo
536eb4f98d5SKalle Valo out:
537eb4f98d5SKalle Valo local->hw_downloading = 0;
538eb4f98d5SKalle Valo return ret;
539eb4f98d5SKalle Valo }
540eb4f98d5SKalle Valo
541eb4f98d5SKalle Valo
542eb4f98d5SKalle Valo #ifdef PRISM2_NON_VOLATILE_DOWNLOAD
543eb4f98d5SKalle Valo /* Note! Non-volatile downloading functionality has not yet been tested
544eb4f98d5SKalle Valo * thoroughly and it may corrupt flash image and effectively kill the card that
545eb4f98d5SKalle Valo * is being updated. You have been warned. */
546eb4f98d5SKalle Valo
prism2_download_block(struct net_device * dev,u32 addr,u8 * data,u32 bufaddr,int rest_len)547eb4f98d5SKalle Valo static inline int prism2_download_block(struct net_device *dev,
548eb4f98d5SKalle Valo u32 addr, u8 *data,
549eb4f98d5SKalle Valo u32 bufaddr, int rest_len)
550eb4f98d5SKalle Valo {
551eb4f98d5SKalle Valo u16 param0, param1;
552eb4f98d5SKalle Valo int block_len;
553eb4f98d5SKalle Valo
554eb4f98d5SKalle Valo block_len = rest_len < 4096 ? rest_len : 4096;
555eb4f98d5SKalle Valo
556eb4f98d5SKalle Valo param0 = addr & 0xffff;
557eb4f98d5SKalle Valo param1 = addr >> 16;
558eb4f98d5SKalle Valo
559eb4f98d5SKalle Valo HFA384X_OUTW(block_len, HFA384X_PARAM2_OFF);
560eb4f98d5SKalle Valo HFA384X_OUTW(param1, HFA384X_PARAM1_OFF);
561eb4f98d5SKalle Valo
562eb4f98d5SKalle Valo if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD |
563eb4f98d5SKalle Valo (HFA384X_PROGMODE_ENABLE_NON_VOLATILE << 8),
564eb4f98d5SKalle Valo param0)) {
565eb4f98d5SKalle Valo printk(KERN_WARNING "%s: Flash download command execution "
566eb4f98d5SKalle Valo "failed\n", dev->name);
567eb4f98d5SKalle Valo return -1;
568eb4f98d5SKalle Valo }
569eb4f98d5SKalle Valo
570eb4f98d5SKalle Valo if (hfa384x_to_aux(dev, bufaddr, block_len, data)) {
571eb4f98d5SKalle Valo printk(KERN_WARNING "%s: flash download at 0x%08x "
572eb4f98d5SKalle Valo "(len=%d) failed\n", dev->name, addr, block_len);
573eb4f98d5SKalle Valo return -1;
574eb4f98d5SKalle Valo }
575eb4f98d5SKalle Valo
576eb4f98d5SKalle Valo HFA384X_OUTW(0, HFA384X_PARAM2_OFF);
577eb4f98d5SKalle Valo HFA384X_OUTW(0, HFA384X_PARAM1_OFF);
578eb4f98d5SKalle Valo if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD |
579eb4f98d5SKalle Valo (HFA384X_PROGMODE_PROGRAM_NON_VOLATILE << 8),
580eb4f98d5SKalle Valo 0)) {
581eb4f98d5SKalle Valo printk(KERN_WARNING "%s: Flash write command execution "
582eb4f98d5SKalle Valo "failed\n", dev->name);
583eb4f98d5SKalle Valo return -1;
584eb4f98d5SKalle Valo }
585eb4f98d5SKalle Valo
586eb4f98d5SKalle Valo return block_len;
587eb4f98d5SKalle Valo }
588eb4f98d5SKalle Valo
589eb4f98d5SKalle Valo
prism2_download_nonvolatile(local_info_t * local,struct prism2_download_data * dl)590eb4f98d5SKalle Valo static int prism2_download_nonvolatile(local_info_t *local,
591eb4f98d5SKalle Valo struct prism2_download_data *dl)
592eb4f98d5SKalle Valo {
593eb4f98d5SKalle Valo struct net_device *dev = local->dev;
594eb4f98d5SKalle Valo int ret = 0, i;
595eb4f98d5SKalle Valo struct {
596eb4f98d5SKalle Valo __le16 page;
597eb4f98d5SKalle Valo __le16 offset;
598eb4f98d5SKalle Valo __le16 len;
599eb4f98d5SKalle Valo } dlbuffer;
600eb4f98d5SKalle Valo u32 bufaddr;
601eb4f98d5SKalle Valo
602eb4f98d5SKalle Valo if (local->hw_downloading) {
603eb4f98d5SKalle Valo printk(KERN_WARNING "%s: Already downloading - aborting new "
604eb4f98d5SKalle Valo "request\n", dev->name);
605eb4f98d5SKalle Valo return -1;
606eb4f98d5SKalle Valo }
607eb4f98d5SKalle Valo
608eb4f98d5SKalle Valo ret = local->func->get_rid(dev, HFA384X_RID_DOWNLOADBUFFER,
609eb4f98d5SKalle Valo &dlbuffer, 6, 0);
610eb4f98d5SKalle Valo
611eb4f98d5SKalle Valo if (ret < 0) {
612eb4f98d5SKalle Valo printk(KERN_WARNING "%s: Could not read download buffer "
613eb4f98d5SKalle Valo "parameters\n", dev->name);
614eb4f98d5SKalle Valo goto out;
615eb4f98d5SKalle Valo }
616eb4f98d5SKalle Valo
617eb4f98d5SKalle Valo printk(KERN_DEBUG "Download buffer: %d bytes at 0x%04x:0x%04x\n",
618eb4f98d5SKalle Valo le16_to_cpu(dlbuffer.len),
619eb4f98d5SKalle Valo le16_to_cpu(dlbuffer.page),
620eb4f98d5SKalle Valo le16_to_cpu(dlbuffer.offset));
621eb4f98d5SKalle Valo
622eb4f98d5SKalle Valo bufaddr = (le16_to_cpu(dlbuffer.page) << 7) + le16_to_cpu(dlbuffer.offset);
623eb4f98d5SKalle Valo
624eb4f98d5SKalle Valo local->hw_downloading = 1;
625eb4f98d5SKalle Valo
626eb4f98d5SKalle Valo if (!local->pri_only) {
627eb4f98d5SKalle Valo prism2_hw_shutdown(dev, 0);
628eb4f98d5SKalle Valo
629eb4f98d5SKalle Valo if (prism2_hw_init(dev, 0)) {
630eb4f98d5SKalle Valo printk(KERN_WARNING "%s: Could not initialize card for"
631eb4f98d5SKalle Valo " download\n", dev->name);
632eb4f98d5SKalle Valo ret = -1;
633eb4f98d5SKalle Valo goto out;
634eb4f98d5SKalle Valo }
635eb4f98d5SKalle Valo }
636eb4f98d5SKalle Valo
637eb4f98d5SKalle Valo hfa384x_disable_interrupts(dev);
638eb4f98d5SKalle Valo
639eb4f98d5SKalle Valo if (prism2_enable_aux_port(dev, 1)) {
640eb4f98d5SKalle Valo printk(KERN_WARNING "%s: Could not enable AUX port\n",
641eb4f98d5SKalle Valo dev->name);
642eb4f98d5SKalle Valo ret = -1;
643eb4f98d5SKalle Valo goto out;
644eb4f98d5SKalle Valo }
645eb4f98d5SKalle Valo
646eb4f98d5SKalle Valo printk(KERN_DEBUG "%s: starting flash download\n", dev->name);
647eb4f98d5SKalle Valo for (i = 0; i < dl->num_areas; i++) {
648eb4f98d5SKalle Valo int rest_len = dl->data[i].len;
649eb4f98d5SKalle Valo int data_off = 0;
650eb4f98d5SKalle Valo
651eb4f98d5SKalle Valo while (rest_len > 0) {
652eb4f98d5SKalle Valo int block_len;
653eb4f98d5SKalle Valo
654eb4f98d5SKalle Valo block_len = prism2_download_block(
655eb4f98d5SKalle Valo dev, dl->data[i].addr + data_off,
656eb4f98d5SKalle Valo dl->data[i].data + data_off, bufaddr,
657eb4f98d5SKalle Valo rest_len);
658eb4f98d5SKalle Valo
659eb4f98d5SKalle Valo if (block_len < 0) {
660eb4f98d5SKalle Valo ret = -1;
661eb4f98d5SKalle Valo goto out;
662eb4f98d5SKalle Valo }
663eb4f98d5SKalle Valo
664eb4f98d5SKalle Valo rest_len -= block_len;
665eb4f98d5SKalle Valo data_off += block_len;
666eb4f98d5SKalle Valo }
667eb4f98d5SKalle Valo }
668eb4f98d5SKalle Valo
669eb4f98d5SKalle Valo HFA384X_OUTW(0, HFA384X_PARAM1_OFF);
670eb4f98d5SKalle Valo HFA384X_OUTW(0, HFA384X_PARAM2_OFF);
671eb4f98d5SKalle Valo if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD |
672eb4f98d5SKalle Valo (HFA384X_PROGMODE_DISABLE << 8), 0)) {
673eb4f98d5SKalle Valo printk(KERN_WARNING "%s: Download command execution failed\n",
674eb4f98d5SKalle Valo dev->name);
675eb4f98d5SKalle Valo ret = -1;
676eb4f98d5SKalle Valo goto out;
677eb4f98d5SKalle Valo }
678eb4f98d5SKalle Valo
679eb4f98d5SKalle Valo if (prism2_enable_aux_port(dev, 0)) {
680eb4f98d5SKalle Valo printk(KERN_DEBUG "%s: Disabling AUX port failed\n",
681eb4f98d5SKalle Valo dev->name);
682eb4f98d5SKalle Valo /* continue anyway.. restart should have taken care of this */
683eb4f98d5SKalle Valo }
684eb4f98d5SKalle Valo
685eb4f98d5SKalle Valo mdelay(5);
686eb4f98d5SKalle Valo
687eb4f98d5SKalle Valo local->func->hw_reset(dev);
688eb4f98d5SKalle Valo local->hw_downloading = 0;
689eb4f98d5SKalle Valo if (prism2_hw_config(dev, 2)) {
690eb4f98d5SKalle Valo printk(KERN_WARNING "%s: Card configuration after flash "
691eb4f98d5SKalle Valo "download failed\n", dev->name);
692eb4f98d5SKalle Valo ret = -1;
693eb4f98d5SKalle Valo } else {
694eb4f98d5SKalle Valo printk(KERN_INFO "%s: Card initialized successfully after "
695eb4f98d5SKalle Valo "flash download\n", dev->name);
696eb4f98d5SKalle Valo }
697eb4f98d5SKalle Valo
698eb4f98d5SKalle Valo out:
699eb4f98d5SKalle Valo local->hw_downloading = 0;
700eb4f98d5SKalle Valo return ret;
701eb4f98d5SKalle Valo }
702eb4f98d5SKalle Valo #endif /* PRISM2_NON_VOLATILE_DOWNLOAD */
703eb4f98d5SKalle Valo
704eb4f98d5SKalle Valo
prism2_download_free_data(struct prism2_download_data * dl)705eb4f98d5SKalle Valo static void prism2_download_free_data(struct prism2_download_data *dl)
706eb4f98d5SKalle Valo {
707eb4f98d5SKalle Valo int i;
708eb4f98d5SKalle Valo
709eb4f98d5SKalle Valo if (dl == NULL)
710eb4f98d5SKalle Valo return;
711eb4f98d5SKalle Valo
712eb4f98d5SKalle Valo for (i = 0; i < dl->num_areas; i++)
713eb4f98d5SKalle Valo kfree(dl->data[i].data);
714eb4f98d5SKalle Valo kfree(dl);
715eb4f98d5SKalle Valo }
716eb4f98d5SKalle Valo
717eb4f98d5SKalle Valo
prism2_download(local_info_t * local,struct prism2_download_param * param)718eb4f98d5SKalle Valo static int prism2_download(local_info_t *local,
719eb4f98d5SKalle Valo struct prism2_download_param *param)
720eb4f98d5SKalle Valo {
721eb4f98d5SKalle Valo int ret = 0;
722eb4f98d5SKalle Valo int i;
723eb4f98d5SKalle Valo u32 total_len = 0;
724eb4f98d5SKalle Valo struct prism2_download_data *dl = NULL;
725eb4f98d5SKalle Valo
726eb4f98d5SKalle Valo printk(KERN_DEBUG "prism2_download: dl_cmd=%d start_addr=0x%08x "
727eb4f98d5SKalle Valo "num_areas=%d\n",
728eb4f98d5SKalle Valo param->dl_cmd, param->start_addr, param->num_areas);
729eb4f98d5SKalle Valo
730eb4f98d5SKalle Valo if (param->num_areas > 100) {
731eb4f98d5SKalle Valo ret = -EINVAL;
732eb4f98d5SKalle Valo goto out;
733eb4f98d5SKalle Valo }
734eb4f98d5SKalle Valo
735eb4f98d5SKalle Valo dl = kzalloc(sizeof(*dl) + param->num_areas *
736eb4f98d5SKalle Valo sizeof(struct prism2_download_data_area), GFP_KERNEL);
737eb4f98d5SKalle Valo if (dl == NULL) {
738eb4f98d5SKalle Valo ret = -ENOMEM;
739eb4f98d5SKalle Valo goto out;
740eb4f98d5SKalle Valo }
741eb4f98d5SKalle Valo dl->dl_cmd = param->dl_cmd;
742eb4f98d5SKalle Valo dl->start_addr = param->start_addr;
743eb4f98d5SKalle Valo dl->num_areas = param->num_areas;
744eb4f98d5SKalle Valo for (i = 0; i < param->num_areas; i++) {
745eb4f98d5SKalle Valo PDEBUG(DEBUG_EXTRA2,
746eb4f98d5SKalle Valo " area %d: addr=0x%08x len=%d ptr=0x%p\n",
747eb4f98d5SKalle Valo i, param->data[i].addr, param->data[i].len,
748eb4f98d5SKalle Valo param->data[i].ptr);
749eb4f98d5SKalle Valo
750eb4f98d5SKalle Valo dl->data[i].addr = param->data[i].addr;
751eb4f98d5SKalle Valo dl->data[i].len = param->data[i].len;
752eb4f98d5SKalle Valo
753eb4f98d5SKalle Valo total_len += param->data[i].len;
754eb4f98d5SKalle Valo if (param->data[i].len > PRISM2_MAX_DOWNLOAD_AREA_LEN ||
755eb4f98d5SKalle Valo total_len > PRISM2_MAX_DOWNLOAD_LEN) {
756eb4f98d5SKalle Valo ret = -E2BIG;
757eb4f98d5SKalle Valo goto out;
758eb4f98d5SKalle Valo }
759eb4f98d5SKalle Valo
760eb4f98d5SKalle Valo dl->data[i].data = kmalloc(dl->data[i].len, GFP_KERNEL);
761eb4f98d5SKalle Valo if (dl->data[i].data == NULL) {
762eb4f98d5SKalle Valo ret = -ENOMEM;
763eb4f98d5SKalle Valo goto out;
764eb4f98d5SKalle Valo }
765eb4f98d5SKalle Valo
766eb4f98d5SKalle Valo if (copy_from_user(dl->data[i].data, param->data[i].ptr,
767eb4f98d5SKalle Valo param->data[i].len)) {
768eb4f98d5SKalle Valo ret = -EFAULT;
769eb4f98d5SKalle Valo goto out;
770eb4f98d5SKalle Valo }
771eb4f98d5SKalle Valo }
772eb4f98d5SKalle Valo
773eb4f98d5SKalle Valo switch (param->dl_cmd) {
774eb4f98d5SKalle Valo case PRISM2_DOWNLOAD_VOLATILE:
775eb4f98d5SKalle Valo case PRISM2_DOWNLOAD_VOLATILE_PERSISTENT:
776eb4f98d5SKalle Valo ret = prism2_download_volatile(local, dl);
777eb4f98d5SKalle Valo break;
778eb4f98d5SKalle Valo case PRISM2_DOWNLOAD_VOLATILE_GENESIS:
779eb4f98d5SKalle Valo case PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT:
780eb4f98d5SKalle Valo ret = prism2_download_genesis(local, dl);
781eb4f98d5SKalle Valo break;
782eb4f98d5SKalle Valo case PRISM2_DOWNLOAD_NON_VOLATILE:
783eb4f98d5SKalle Valo #ifdef PRISM2_NON_VOLATILE_DOWNLOAD
784eb4f98d5SKalle Valo ret = prism2_download_nonvolatile(local, dl);
785eb4f98d5SKalle Valo #else /* PRISM2_NON_VOLATILE_DOWNLOAD */
786eb4f98d5SKalle Valo printk(KERN_INFO "%s: non-volatile downloading not enabled\n",
787eb4f98d5SKalle Valo local->dev->name);
788eb4f98d5SKalle Valo ret = -EOPNOTSUPP;
789eb4f98d5SKalle Valo #endif /* PRISM2_NON_VOLATILE_DOWNLOAD */
790eb4f98d5SKalle Valo break;
791eb4f98d5SKalle Valo default:
792eb4f98d5SKalle Valo printk(KERN_DEBUG "%s: unsupported download command %d\n",
793eb4f98d5SKalle Valo local->dev->name, param->dl_cmd);
794eb4f98d5SKalle Valo ret = -EINVAL;
795eb4f98d5SKalle Valo break;
796eb4f98d5SKalle Valo }
797eb4f98d5SKalle Valo
798eb4f98d5SKalle Valo out:
799eb4f98d5SKalle Valo if (ret == 0 && dl &&
800eb4f98d5SKalle Valo param->dl_cmd == PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT) {
801eb4f98d5SKalle Valo prism2_download_free_data(local->dl_pri);
802eb4f98d5SKalle Valo local->dl_pri = dl;
803eb4f98d5SKalle Valo } else if (ret == 0 && dl &&
804eb4f98d5SKalle Valo param->dl_cmd == PRISM2_DOWNLOAD_VOLATILE_PERSISTENT) {
805eb4f98d5SKalle Valo prism2_download_free_data(local->dl_sec);
806eb4f98d5SKalle Valo local->dl_sec = dl;
807eb4f98d5SKalle Valo } else
808eb4f98d5SKalle Valo prism2_download_free_data(dl);
809eb4f98d5SKalle Valo
810eb4f98d5SKalle Valo return ret;
811eb4f98d5SKalle Valo }
812