Commit | Line | Data |
---|---|---|
f7c1be0c MB |
1 | /*--------------------------------------------------------------------------- |
2 | FT1000 driver for Flarion Flash OFDM NIC Device | |
bf3146c8 | 3 | |
f7c1be0c | 4 | Copyright (C) 2002 Flarion Technologies, All rights reserved. |
bf3146c8 GKH |
5 | |
6 | This program is free software; you can redistribute it and/or modify it | |
f7c1be0c | 7 | under the terms of the GNU General Public License as published by the Free |
bf3146c8 GKH |
8 | Software Foundation; either version 2 of the License, or (at your option) any |
9 | later version. This program is distributed in the hope that it will be useful, | |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
11 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
12 | more details. You should have received a copy of the GNU General Public | |
13 | License along with this program; if not, write to the | |
14 | Free Software Foundation, Inc., 59 Temple Place - | |
15 | Suite 330, Boston, MA 02111-1307, USA. | |
f7c1be0c MB |
16 | -------------------------------------------------------------------------- |
17 | ||
bf3146c8 | 18 | Description: This module will handshake with the DSP bootloader to |
f7c1be0c | 19 | download the DSP runtime image. |
bf3146c8 | 20 | |
f7c1be0c MB |
21 | ---------------------------------------------------------------------------*/ |
22 | ||
23 | #define __KERNEL_SYSCALLS__ | |
24 | ||
25 | #include <linux/module.h> | |
26 | #include <linux/fs.h> | |
27 | #include <linux/mm.h> | |
28 | #include <linux/slab.h> | |
29 | #include <linux/unistd.h> | |
30 | #include <linux/netdevice.h> | |
31 | #include <linux/timer.h> | |
32 | #include <linux/delay.h> | |
f7c1be0c MB |
33 | #include <asm/io.h> |
34 | #include <asm/uaccess.h> | |
35 | #include <linux/vmalloc.h> | |
36 | ||
f7c1be0c MB |
37 | #include "ft1000.h" |
38 | #include "boot.h" | |
39 | ||
40 | #ifdef FT_DEBUG | |
41 | #define DEBUG(n, args...) printk(KERN_DEBUG args); | |
42 | #else | |
43 | #define DEBUG(n, args...) | |
44 | #endif | |
45 | ||
46 | #define MAX_DSP_WAIT_LOOPS 100 | |
47 | #define DSP_WAIT_SLEEP_TIME 1 /* 1 millisecond */ | |
48 | ||
49 | #define MAX_LENGTH 0x7f0 | |
50 | ||
51 | #define DWNLD_MAG_HANDSHAKE_LOC 0x00 | |
52 | #define DWNLD_MAG_TYPE_LOC 0x01 | |
53 | #define DWNLD_MAG_SIZE_LOC 0x02 | |
54 | #define DWNLD_MAG_PS_HDR_LOC 0x03 | |
55 | ||
56 | #define DWNLD_HANDSHAKE_LOC 0x02 | |
57 | #define DWNLD_TYPE_LOC 0x04 | |
58 | #define DWNLD_SIZE_MSW_LOC 0x06 | |
59 | #define DWNLD_SIZE_LSW_LOC 0x08 | |
60 | #define DWNLD_PS_HDR_LOC 0x0A | |
61 | ||
62 | #define HANDSHAKE_TIMEOUT_VALUE 0xF1F1 | |
63 | #define HANDSHAKE_RESET_VALUE 0xFEFE /* When DSP requests startover */ | |
64 | #define HANDSHAKE_DSP_BL_READY 0xFEFE /* At start DSP writes this when bootloader ready */ | |
65 | #define HANDSHAKE_DRIVER_READY 0xFFFF /* Driver writes after receiving 0xFEFE */ | |
66 | #define HANDSHAKE_SEND_DATA 0x0000 /* DSP writes this when ready for more data */ | |
67 | ||
68 | #define HANDSHAKE_REQUEST 0x0001 /* Request from DSP */ | |
69 | #define HANDSHAKE_RESPONSE 0x0000 /* Satisfied DSP request */ | |
70 | ||
71 | #define REQUEST_CODE_LENGTH 0x0000 | |
72 | #define REQUEST_RUN_ADDRESS 0x0001 | |
73 | #define REQUEST_CODE_SEGMENT 0x0002 /* In WORD count */ | |
74 | #define REQUEST_DONE_BL 0x0003 | |
75 | #define REQUEST_DONE_CL 0x0004 | |
76 | #define REQUEST_VERSION_INFO 0x0005 | |
77 | #define REQUEST_CODE_BY_VERSION 0x0006 | |
78 | #define REQUEST_MAILBOX_DATA 0x0007 | |
79 | #define REQUEST_FILE_CHECKSUM 0x0008 | |
80 | ||
81 | #define STATE_START_DWNLD 0x01 | |
82 | #define STATE_BOOT_DWNLD 0x02 | |
83 | #define STATE_CODE_DWNLD 0x03 | |
84 | #define STATE_DONE_DWNLD 0x04 | |
85 | #define STATE_SECTION_PROV 0x05 | |
86 | #define STATE_DONE_PROV 0x06 | |
87 | #define STATE_DONE_FILE 0x07 | |
88 | ||
ca145277 OZ |
89 | u16 get_handshake(struct net_device *dev, u16 expected_value); |
90 | void put_handshake(struct net_device *dev, u16 handshake_value); | |
91 | u16 get_request_type(struct net_device *dev); | |
f7c1be0c MB |
92 | long get_request_value(struct net_device *dev); |
93 | void put_request_value(struct net_device *dev, long lvalue); | |
ca145277 | 94 | u16 hdr_checksum(struct pseudo_hdr *pHdr); |
f7c1be0c | 95 | |
8e0fd2cf | 96 | struct dsp_file_hdr { |
ad139634 DN |
97 | u32 version_id; /* Version ID of this image format. */ |
98 | u32 package_id; /* Package ID of code release. */ | |
99 | u32 build_date; /* Date/time stamp when file was built. */ | |
100 | u32 commands_offset; /* Offset to attached commands in Pseudo Hdr format. */ | |
101 | u32 loader_offset; /* Offset to bootloader code. */ | |
102 | u32 loader_code_address; /* Start address of bootloader. */ | |
103 | u32 loader_code_end; /* Where bootloader code ends. */ | |
14e5d8ef | 104 | u32 loader_code_size; |
ad139634 DN |
105 | u32 version_data_offset; /* Offset were scrambled version data begins. */ |
106 | u32 version_data_size; /* Size, in words, of scrambled version data. */ | |
107 | u32 nDspImages; /* Number of DSP images in file. */ | |
8e0fd2cf | 108 | } __attribute__ ((packed)); |
f7c1be0c | 109 | |
8e0fd2cf | 110 | struct dsp_image_info { |
ad139634 DN |
111 | u32 coff_date; /* Date/time when DSP Coff image was built. */ |
112 | u32 begin_offset; /* Offset in file where image begins. */ | |
113 | u32 end_offset; /* Offset in file where image begins. */ | |
114 | u32 run_address; /* On chip Start address of DSP code. */ | |
115 | u32 image_size; /* Size of image. */ | |
116 | u32 version; /* Embedded version # of DSP code. */ | |
117 | unsigned short checksum; /* Dsp File checksum */ | |
f7c1be0c | 118 | unsigned short pad1; |
8e0fd2cf | 119 | } __attribute__ ((packed)); |
f7c1be0c MB |
120 | |
121 | void card_bootload(struct net_device *dev) | |
122 | { | |
d3706552 | 123 | struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev); |
f7c1be0c | 124 | unsigned long flags; |
ca145277 OZ |
125 | u32 *pdata; |
126 | u32 size; | |
127 | u32 i; | |
128 | u32 templong; | |
f7c1be0c MB |
129 | |
130 | DEBUG(0, "card_bootload is called\n"); | |
131 | ||
ca145277 | 132 | pdata = (u32 *) bootimage; |
f7c1be0c MB |
133 | size = sizeof(bootimage); |
134 | ||
135 | // check for odd word | |
136 | if (size & 0x0003) { | |
137 | size += 4; | |
138 | } | |
139 | // Provide mutual exclusive access while reading ASIC registers. | |
140 | spin_lock_irqsave(&info->dpram_lock, flags); | |
141 | ||
142 | // need to set i/o base address initially and hardware will autoincrement | |
143 | ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, FT1000_DPRAM_BASE); | |
144 | // write bytes | |
145 | for (i = 0; i < (size >> 2); i++) { | |
146 | templong = *pdata++; | |
147 | outl(templong, dev->base_addr + FT1000_REG_MAG_DPDATA); | |
148 | } | |
149 | ||
150 | spin_unlock_irqrestore(&info->dpram_lock, flags); | |
151 | } | |
152 | ||
ca145277 | 153 | u16 get_handshake(struct net_device *dev, u16 expected_value) |
f7c1be0c | 154 | { |
d3706552 | 155 | struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev); |
ca145277 OZ |
156 | u16 handshake; |
157 | u32 tempx; | |
f7c1be0c MB |
158 | int loopcnt; |
159 | ||
160 | loopcnt = 0; | |
161 | while (loopcnt < MAX_DSP_WAIT_LOOPS) { | |
162 | if (info->AsicID == ELECTRABUZZ_ID) { | |
163 | ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, | |
164 | DWNLD_HANDSHAKE_LOC); | |
165 | ||
166 | handshake = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA); | |
167 | } else { | |
168 | tempx = | |
169 | ntohl(ft1000_read_dpram_mag_32 | |
170 | (dev, DWNLD_MAG_HANDSHAKE_LOC)); | |
ca145277 | 171 | handshake = (u16) tempx; |
f7c1be0c MB |
172 | } |
173 | ||
174 | if ((handshake == expected_value) | |
175 | || (handshake == HANDSHAKE_RESET_VALUE)) { | |
176 | return handshake; | |
177 | } else { | |
178 | loopcnt++; | |
179 | mdelay(DSP_WAIT_SLEEP_TIME); | |
180 | } | |
181 | ||
182 | } | |
183 | ||
184 | return HANDSHAKE_TIMEOUT_VALUE; | |
185 | ||
186 | } | |
187 | ||
ca145277 | 188 | void put_handshake(struct net_device *dev, u16 handshake_value) |
f7c1be0c | 189 | { |
d3706552 | 190 | struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev); |
ca145277 | 191 | u32 tempx; |
f7c1be0c MB |
192 | |
193 | if (info->AsicID == ELECTRABUZZ_ID) { | |
194 | ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, | |
195 | DWNLD_HANDSHAKE_LOC); | |
196 | ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, handshake_value); /* Handshake */ | |
197 | } else { | |
ca145277 | 198 | tempx = (u32) handshake_value; |
f7c1be0c MB |
199 | tempx = ntohl(tempx); |
200 | ft1000_write_dpram_mag_32(dev, DWNLD_MAG_HANDSHAKE_LOC, tempx); /* Handshake */ | |
201 | } | |
202 | } | |
203 | ||
ca145277 | 204 | u16 get_request_type(struct net_device *dev) |
f7c1be0c | 205 | { |
d3706552 | 206 | struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev); |
ca145277 OZ |
207 | u16 request_type; |
208 | u32 tempx; | |
f7c1be0c MB |
209 | |
210 | if (info->AsicID == ELECTRABUZZ_ID) { | |
211 | ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, DWNLD_TYPE_LOC); | |
212 | request_type = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA); | |
213 | } else { | |
214 | tempx = ft1000_read_dpram_mag_32(dev, DWNLD_MAG_TYPE_LOC); | |
215 | tempx = ntohl(tempx); | |
ca145277 | 216 | request_type = (u16) tempx; |
f7c1be0c MB |
217 | } |
218 | ||
219 | return request_type; | |
220 | ||
221 | } | |
222 | ||
223 | long get_request_value(struct net_device *dev) | |
224 | { | |
d3706552 | 225 | struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev); |
f7c1be0c | 226 | long value; |
ca145277 | 227 | u16 w_val; |
f7c1be0c MB |
228 | |
229 | if (info->AsicID == ELECTRABUZZ_ID) { | |
230 | ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, | |
231 | DWNLD_SIZE_MSW_LOC); | |
232 | ||
233 | w_val = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA); | |
234 | ||
235 | value = (long)(w_val << 16); | |
236 | ||
237 | ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, | |
238 | DWNLD_SIZE_LSW_LOC); | |
239 | ||
240 | w_val = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA); | |
241 | ||
242 | value = (long)(value | w_val); | |
243 | } else { | |
244 | value = ft1000_read_dpram_mag_32(dev, DWNLD_MAG_SIZE_LOC); | |
245 | value = ntohl(value); | |
246 | } | |
247 | ||
248 | return value; | |
249 | ||
250 | } | |
251 | ||
252 | void put_request_value(struct net_device *dev, long lvalue) | |
253 | { | |
d3706552 | 254 | struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev); |
ca145277 OZ |
255 | u16 size; |
256 | u32 tempx; | |
f7c1be0c MB |
257 | |
258 | if (info->AsicID == ELECTRABUZZ_ID) { | |
ca145277 | 259 | size = (u16) (lvalue >> 16); |
f7c1be0c MB |
260 | |
261 | ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, | |
262 | DWNLD_SIZE_MSW_LOC); | |
263 | ||
264 | ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, size); | |
265 | ||
ca145277 | 266 | size = (u16) (lvalue); |
f7c1be0c MB |
267 | |
268 | ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, | |
269 | DWNLD_SIZE_LSW_LOC); | |
270 | ||
271 | ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, size); | |
272 | } else { | |
273 | tempx = ntohl(lvalue); | |
274 | ft1000_write_dpram_mag_32(dev, DWNLD_MAG_SIZE_LOC, tempx); /* Handshake */ | |
275 | } | |
276 | ||
277 | } | |
278 | ||
ca145277 | 279 | u16 hdr_checksum(struct pseudo_hdr *pHdr) |
f7c1be0c | 280 | { |
ca145277 OZ |
281 | u16 *usPtr = (u16 *) pHdr; |
282 | u16 chksum; | |
f7c1be0c MB |
283 | |
284 | chksum = ((((((usPtr[0] ^ usPtr[1]) ^ usPtr[2]) ^ usPtr[3]) ^ | |
285 | usPtr[4]) ^ usPtr[5]) ^ usPtr[6]); | |
286 | ||
287 | return chksum; | |
288 | } | |
289 | ||
eb93ca4e RD |
290 | int card_download(struct net_device *dev, const u8 *pFileStart, |
291 | size_t FileLength) | |
f7c1be0c | 292 | { |
d3706552 | 293 | struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev); |
f7c1be0c | 294 | int Status = SUCCESS; |
ca145277 OZ |
295 | u32 uiState; |
296 | u16 handshake; | |
2c9bf839 | 297 | struct pseudo_hdr *pHdr; |
ca145277 | 298 | u16 usHdrLength; |
f7c1be0c | 299 | long word_length; |
ca145277 OZ |
300 | u16 request; |
301 | u16 temp; | |
3aaf8073 | 302 | struct prov_record *pprov_record; |
ca145277 | 303 | u8 *pbuffer; |
e161a440 OZ |
304 | struct dsp_file_hdr *pFileHdr5; |
305 | struct dsp_image_info *pDspImageInfoV6 = NULL; | |
f7c1be0c | 306 | long requested_version; |
ca145277 | 307 | bool bGoodVersion = 0; |
8bc0d6fb | 308 | struct drv_msg *pMailBoxData; |
ca145277 OZ |
309 | u16 *pUsData = NULL; |
310 | u16 *pUsFile = NULL; | |
311 | u8 *pUcFile = NULL; | |
312 | u8 *pBootEnd = NULL; | |
313 | u8 *pCodeEnd = NULL; | |
f7c1be0c MB |
314 | int imageN; |
315 | long file_version; | |
316 | long loader_code_address = 0; | |
317 | long loader_code_size = 0; | |
318 | long run_address = 0; | |
319 | long run_size = 0; | |
320 | unsigned long flags; | |
321 | unsigned long templong; | |
322 | unsigned long image_chksum = 0; | |
323 | ||
f7c1be0c | 324 | file_version = *(long *)pFileStart; |
e161a440 OZ |
325 | if (file_version != 6) { |
326 | printk(KERN_ERR "ft1000: unsupported firmware version %ld\n", file_version); | |
327 | Status = FAILURE; | |
328 | } | |
f7c1be0c MB |
329 | |
330 | uiState = STATE_START_DWNLD; | |
331 | ||
e161a440 | 332 | pFileHdr5 = (struct dsp_file_hdr *) pFileStart; |
f7c1be0c | 333 | |
e161a440 OZ |
334 | pUsFile = (u16 *) ((long)pFileStart + pFileHdr5->loader_offset); |
335 | pUcFile = (u8 *) ((long)pFileStart + pFileHdr5->loader_offset); | |
336 | pBootEnd = (u8 *) ((long)pFileStart + pFileHdr5->loader_code_end); | |
337 | loader_code_address = pFileHdr5->loader_code_address; | |
338 | loader_code_size = pFileHdr5->loader_code_size; | |
339 | bGoodVersion = false; | |
f7c1be0c MB |
340 | |
341 | while ((Status == SUCCESS) && (uiState != STATE_DONE_FILE)) { | |
342 | ||
343 | switch (uiState) { | |
344 | case STATE_START_DWNLD: | |
345 | ||
346 | handshake = get_handshake(dev, HANDSHAKE_DSP_BL_READY); | |
347 | ||
348 | if (handshake == HANDSHAKE_DSP_BL_READY) { | |
349 | put_handshake(dev, HANDSHAKE_DRIVER_READY); | |
350 | } else { | |
351 | Status = FAILURE; | |
352 | } | |
353 | ||
354 | uiState = STATE_BOOT_DWNLD; | |
355 | ||
356 | break; | |
357 | ||
358 | case STATE_BOOT_DWNLD: | |
359 | handshake = get_handshake(dev, HANDSHAKE_REQUEST); | |
360 | if (handshake == HANDSHAKE_REQUEST) { | |
361 | /* | |
362 | * Get type associated with the request. | |
363 | */ | |
364 | request = get_request_type(dev); | |
365 | switch (request) { | |
366 | case REQUEST_RUN_ADDRESS: | |
367 | put_request_value(dev, | |
368 | loader_code_address); | |
369 | break; | |
370 | case REQUEST_CODE_LENGTH: | |
371 | put_request_value(dev, | |
372 | loader_code_size); | |
373 | break; | |
374 | case REQUEST_DONE_BL: | |
375 | /* Reposition ptrs to beginning of code section */ | |
ca145277 OZ |
376 | pUsFile = (u16 *) ((long)pBootEnd); |
377 | pUcFile = (u8 *) ((long)pBootEnd); | |
f7c1be0c MB |
378 | uiState = STATE_CODE_DWNLD; |
379 | break; | |
380 | case REQUEST_CODE_SEGMENT: | |
381 | word_length = get_request_value(dev); | |
382 | if (word_length > MAX_LENGTH) { | |
383 | Status = FAILURE; | |
384 | break; | |
385 | } | |
386 | if ((word_length * 2 + (long)pUcFile) > | |
387 | (long)pBootEnd) { | |
388 | /* | |
389 | * Error, beyond boot code range. | |
390 | */ | |
391 | Status = FAILURE; | |
392 | break; | |
393 | } | |
394 | // Provide mutual exclusive access while reading ASIC registers. | |
395 | spin_lock_irqsave(&info->dpram_lock, | |
396 | flags); | |
e161a440 OZ |
397 | /* |
398 | * Position ASIC DPRAM auto-increment pointer. | |
399 | */ | |
400 | outw(DWNLD_MAG_PS_HDR_LOC, | |
401 | dev->base_addr + | |
402 | FT1000_REG_DPRAM_ADDR); | |
403 | if (word_length & 0x01) | |
404 | word_length++; | |
405 | word_length = word_length / 2; | |
406 | ||
407 | for (; word_length > 0; word_length--) { /* In words */ | |
408 | templong = *pUsFile++; | |
409 | templong |= | |
410 | (*pUsFile++ << 16); | |
411 | pUcFile += 4; | |
412 | outl(templong, | |
f7c1be0c | 413 | dev->base_addr + |
e161a440 | 414 | FT1000_REG_MAG_DPDATAL); |
f7c1be0c MB |
415 | } |
416 | spin_unlock_irqrestore(&info-> | |
417 | dpram_lock, | |
418 | flags); | |
419 | break; | |
420 | default: | |
421 | Status = FAILURE; | |
422 | break; | |
423 | } | |
424 | put_handshake(dev, HANDSHAKE_RESPONSE); | |
425 | } else { | |
426 | Status = FAILURE; | |
427 | } | |
428 | ||
429 | break; | |
430 | ||
431 | case STATE_CODE_DWNLD: | |
432 | handshake = get_handshake(dev, HANDSHAKE_REQUEST); | |
433 | if (handshake == HANDSHAKE_REQUEST) { | |
434 | /* | |
435 | * Get type associated with the request. | |
436 | */ | |
437 | request = get_request_type(dev); | |
438 | switch (request) { | |
439 | case REQUEST_FILE_CHECKSUM: | |
440 | DEBUG(0, | |
441 | "ft1000_dnld: REQUEST_FOR_CHECKSUM\n"); | |
442 | put_request_value(dev, image_chksum); | |
443 | break; | |
444 | case REQUEST_RUN_ADDRESS: | |
445 | if (bGoodVersion) { | |
446 | put_request_value(dev, | |
447 | run_address); | |
448 | } else { | |
449 | Status = FAILURE; | |
450 | break; | |
451 | } | |
452 | break; | |
453 | case REQUEST_CODE_LENGTH: | |
454 | if (bGoodVersion) { | |
455 | put_request_value(dev, | |
456 | run_size); | |
457 | } else { | |
458 | Status = FAILURE; | |
459 | break; | |
460 | } | |
461 | break; | |
462 | case REQUEST_DONE_CL: | |
463 | /* Reposition ptrs to beginning of provisioning section */ | |
e161a440 OZ |
464 | pUsFile = (u16 *) ((long)pFileStart + pFileHdr5->commands_offset); |
465 | pUcFile = (u8 *) ((long)pFileStart + pFileHdr5->commands_offset); | |
f7c1be0c MB |
466 | uiState = STATE_DONE_DWNLD; |
467 | break; | |
468 | case REQUEST_CODE_SEGMENT: | |
469 | if (!bGoodVersion) { | |
470 | Status = FAILURE; | |
471 | break; | |
472 | } | |
473 | word_length = get_request_value(dev); | |
474 | if (word_length > MAX_LENGTH) { | |
475 | Status = FAILURE; | |
476 | break; | |
477 | } | |
478 | if ((word_length * 2 + (long)pUcFile) > | |
479 | (long)pCodeEnd) { | |
480 | /* | |
481 | * Error, beyond boot code range. | |
482 | */ | |
483 | Status = FAILURE; | |
484 | break; | |
485 | } | |
e161a440 OZ |
486 | /* |
487 | * Position ASIC DPRAM auto-increment pointer. | |
488 | */ | |
489 | outw(DWNLD_MAG_PS_HDR_LOC, | |
490 | dev->base_addr + | |
491 | FT1000_REG_DPRAM_ADDR); | |
492 | if (word_length & 0x01) | |
493 | word_length++; | |
494 | word_length = word_length / 2; | |
495 | ||
496 | for (; word_length > 0; word_length--) { /* In words */ | |
497 | templong = *pUsFile++; | |
498 | templong |= | |
499 | (*pUsFile++ << 16); | |
500 | pUcFile += 4; | |
501 | outl(templong, | |
f7c1be0c | 502 | dev->base_addr + |
e161a440 | 503 | FT1000_REG_MAG_DPDATAL); |
f7c1be0c MB |
504 | } |
505 | break; | |
506 | ||
507 | case REQUEST_MAILBOX_DATA: | |
508 | // Convert length from byte count to word count. Make sure we round up. | |
509 | word_length = | |
510 | (long)(info->DSPInfoBlklen + 1) / 2; | |
511 | put_request_value(dev, word_length); | |
512 | pMailBoxData = | |
8bc0d6fb | 513 | (struct drv_msg *) & info->DSPInfoBlk[0]; |
f7c1be0c | 514 | pUsData = |
ca145277 | 515 | (u16 *) & pMailBoxData->data[0]; |
f7c1be0c MB |
516 | // Provide mutual exclusive access while reading ASIC registers. |
517 | spin_lock_irqsave(&info->dpram_lock, | |
518 | flags); | |
519 | if (file_version == 5) { | |
520 | /* | |
521 | * Position ASIC DPRAM auto-increment pointer. | |
522 | */ | |
523 | ft1000_write_reg(dev, | |
524 | FT1000_REG_DPRAM_ADDR, | |
525 | DWNLD_PS_HDR_LOC); | |
526 | ||
527 | for (; word_length > 0; word_length--) { /* In words */ | |
528 | temp = ntohs(*pUsData); | |
529 | ft1000_write_reg(dev, | |
530 | FT1000_REG_DPRAM_DATA, | |
531 | temp); | |
532 | pUsData++; | |
533 | } | |
534 | } else { | |
535 | /* | |
536 | * Position ASIC DPRAM auto-increment pointer. | |
537 | */ | |
538 | outw(DWNLD_MAG_PS_HDR_LOC, | |
539 | dev->base_addr + | |
540 | FT1000_REG_DPRAM_ADDR); | |
541 | if (word_length & 0x01) { | |
542 | word_length++; | |
543 | } | |
544 | word_length = word_length / 2; | |
545 | ||
546 | for (; word_length > 0; word_length--) { /* In words */ | |
547 | templong = *pUsData++; | |
548 | templong |= | |
549 | (*pUsData++ << 16); | |
550 | outl(templong, | |
551 | dev->base_addr + | |
552 | FT1000_REG_MAG_DPDATAL); | |
553 | } | |
554 | } | |
555 | spin_unlock_irqrestore(&info-> | |
556 | dpram_lock, | |
557 | flags); | |
558 | break; | |
559 | ||
560 | case REQUEST_VERSION_INFO: | |
561 | word_length = | |
562 | pFileHdr5->version_data_size; | |
563 | put_request_value(dev, word_length); | |
564 | pUsFile = | |
ca145277 | 565 | (u16 *) ((long)pFileStart + |
f7c1be0c MB |
566 | pFileHdr5-> |
567 | version_data_offset); | |
568 | // Provide mutual exclusive access while reading ASIC registers. | |
569 | spin_lock_irqsave(&info->dpram_lock, | |
570 | flags); | |
e161a440 OZ |
571 | /* |
572 | * Position ASIC DPRAM auto-increment pointer. | |
573 | */ | |
574 | outw(DWNLD_MAG_PS_HDR_LOC, | |
575 | dev->base_addr + | |
576 | FT1000_REG_DPRAM_ADDR); | |
577 | if (word_length & 0x01) | |
578 | word_length++; | |
579 | word_length = word_length / 2; | |
580 | ||
581 | for (; word_length > 0; word_length--) { /* In words */ | |
582 | templong = | |
583 | ntohs(*pUsFile++); | |
584 | temp = | |
585 | ntohs(*pUsFile++); | |
586 | templong |= | |
587 | (temp << 16); | |
588 | outl(templong, | |
f7c1be0c | 589 | dev->base_addr + |
e161a440 | 590 | FT1000_REG_MAG_DPDATAL); |
f7c1be0c MB |
591 | } |
592 | spin_unlock_irqrestore(&info-> | |
593 | dpram_lock, | |
594 | flags); | |
595 | break; | |
596 | ||
597 | case REQUEST_CODE_BY_VERSION: | |
ca145277 | 598 | bGoodVersion = false; |
f7c1be0c MB |
599 | requested_version = |
600 | get_request_value(dev); | |
e161a440 OZ |
601 | pDspImageInfoV6 = |
602 | (struct dsp_image_info *) ((long) | |
603 | pFileStart | |
604 | + | |
605 | sizeof | |
606 | (struct dsp_file_hdr)); | |
607 | for (imageN = 0; | |
608 | imageN < | |
609 | pFileHdr5->nDspImages; | |
610 | imageN++) { | |
611 | temp = (u16) | |
612 | (pDspImageInfoV6-> | |
613 | version); | |
614 | templong = temp; | |
615 | temp = (u16) | |
616 | (pDspImageInfoV6-> | |
617 | version >> 16); | |
618 | templong |= | |
619 | (temp << 16); | |
620 | if (templong == | |
621 | requested_version) { | |
622 | bGoodVersion = | |
623 | true; | |
624 | pUsFile = | |
625 | (u16 | |
626 | *) ((long) | |
627 | pFileStart | |
628 | + | |
629 | pDspImageInfoV6-> | |
630 | begin_offset); | |
631 | pUcFile = | |
632 | (u8 | |
633 | *) ((long) | |
634 | pFileStart | |
635 | + | |
636 | pDspImageInfoV6-> | |
637 | begin_offset); | |
638 | pCodeEnd = | |
639 | (u8 | |
640 | *) ((long) | |
641 | pFileStart | |
642 | + | |
643 | pDspImageInfoV6-> | |
644 | end_offset); | |
645 | run_address = | |
646 | pDspImageInfoV6-> | |
647 | run_address; | |
648 | run_size = | |
649 | pDspImageInfoV6-> | |
650 | image_size; | |
651 | image_chksum = | |
652 | (u32) | |
653 | pDspImageInfoV6-> | |
654 | checksum; | |
655 | DEBUG(0, | |
656 | "ft1000_dnld: image_chksum = 0x%8x\n", | |
657 | (unsigned | |
658 | int) | |
659 | image_chksum); | |
660 | break; | |
f7c1be0c | 661 | } |
e161a440 | 662 | pDspImageInfoV6++; |
f7c1be0c MB |
663 | } |
664 | if (!bGoodVersion) { | |
665 | /* | |
666 | * Error, beyond boot code range. | |
667 | */ | |
668 | Status = FAILURE; | |
669 | break; | |
670 | } | |
671 | break; | |
672 | ||
673 | default: | |
674 | Status = FAILURE; | |
675 | break; | |
676 | } | |
677 | put_handshake(dev, HANDSHAKE_RESPONSE); | |
678 | } else { | |
679 | Status = FAILURE; | |
680 | } | |
681 | ||
682 | break; | |
683 | ||
684 | case STATE_DONE_DWNLD: | |
14e5d8ef | 685 | if (((unsigned long) (pUcFile) - (unsigned long) pFileStart) >= |
686 | (unsigned long) FileLength) { | |
f7c1be0c MB |
687 | uiState = STATE_DONE_FILE; |
688 | break; | |
689 | } | |
690 | ||
2c9bf839 | 691 | pHdr = (struct pseudo_hdr *) pUsFile; |
f7c1be0c MB |
692 | |
693 | if (pHdr->portdest == 0x80 /* DspOAM */ | |
694 | && (pHdr->portsrc == 0x00 /* Driver */ | |
695 | || pHdr->portsrc == 0x10 /* FMM */ )) { | |
696 | uiState = STATE_SECTION_PROV; | |
697 | } else { | |
698 | DEBUG(1, | |
699 | "FT1000:download:Download error: Bad Port IDs in Pseudo Record\n"); | |
700 | DEBUG(1, "\t Port Source = 0x%2.2x\n", | |
701 | pHdr->portsrc); | |
702 | DEBUG(1, "\t Port Destination = 0x%2.2x\n", | |
703 | pHdr->portdest); | |
704 | Status = FAILURE; | |
705 | } | |
706 | ||
707 | break; | |
708 | ||
709 | case STATE_SECTION_PROV: | |
710 | ||
2c9bf839 | 711 | pHdr = (struct pseudo_hdr *) pUcFile; |
f7c1be0c MB |
712 | |
713 | if (pHdr->checksum == hdr_checksum(pHdr)) { | |
714 | if (pHdr->portdest != 0x80 /* Dsp OAM */ ) { | |
715 | uiState = STATE_DONE_PROV; | |
716 | break; | |
717 | } | |
718 | usHdrLength = ntohs(pHdr->length); /* Byte length for PROV records */ | |
719 | ||
720 | // Get buffer for provisioning data | |
721 | pbuffer = | |
2c9bf839 | 722 | kmalloc((usHdrLength + sizeof(struct pseudo_hdr)), |
f7c1be0c MB |
723 | GFP_ATOMIC); |
724 | if (pbuffer) { | |
725 | memcpy(pbuffer, (void *)pUcFile, | |
ca145277 | 726 | (u32) (usHdrLength + |
2c9bf839 | 727 | sizeof(struct pseudo_hdr))); |
bf3146c8 | 728 | // link provisioning data |
f7c1be0c | 729 | pprov_record = |
3aaf8073 | 730 | kmalloc(sizeof(struct prov_record), |
f7c1be0c MB |
731 | GFP_ATOMIC); |
732 | if (pprov_record) { | |
733 | pprov_record->pprov_data = | |
734 | pbuffer; | |
735 | list_add_tail(&pprov_record-> | |
736 | list, | |
737 | &info->prov_list); | |
738 | // Move to next entry if available | |
739 | pUcFile = | |
ca145277 | 740 | (u8 *) ((unsigned long) pUcFile + |
2c9bf839 | 741 | (unsigned long) ((usHdrLength + 1) & 0xFFFFFFFE) + sizeof(struct pseudo_hdr)); |
14e5d8ef | 742 | if ((unsigned long) (pUcFile) - |
743 | (unsigned long) (pFileStart) >= | |
744 | (unsigned long) FileLength) { | |
f7c1be0c MB |
745 | uiState = |
746 | STATE_DONE_FILE; | |
747 | } | |
748 | } else { | |
749 | kfree(pbuffer); | |
750 | Status = FAILURE; | |
751 | } | |
752 | } else { | |
753 | Status = FAILURE; | |
754 | } | |
755 | } else { | |
756 | /* Checksum did not compute */ | |
757 | Status = FAILURE; | |
758 | } | |
759 | ||
760 | break; | |
761 | ||
762 | case STATE_DONE_PROV: | |
763 | uiState = STATE_DONE_FILE; | |
764 | break; | |
765 | ||
766 | default: | |
767 | Status = FAILURE; | |
768 | break; | |
769 | } /* End Switch */ | |
770 | ||
771 | } /* End while */ | |
772 | ||
773 | return Status; | |
774 | ||
775 | } |