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 { |
14e5d8ef | 97 | u32 build_date; |
98 | u32 dsp_coff_date; | |
99 | u32 loader_code_address; | |
100 | u32 loader_code_size; | |
101 | u32 loader_code_end; | |
102 | u32 dsp_code_address; | |
103 | u32 dsp_code_size; | |
104 | u32 dsp_code_end; | |
105 | u32 reserved[8]; | |
8e0fd2cf | 106 | } __attribute__ ((packed)); |
f7c1be0c | 107 | |
8e0fd2cf | 108 | struct dsp_file_hdr_5 { |
14e5d8ef | 109 | u32 version_id; // Version ID of this image format. |
110 | u32 package_id; // Package ID of code release. | |
111 | u32 build_date; // Date/time stamp when file was built. | |
112 | u32 commands_offset; // Offset to attached commands in Pseudo Hdr format. | |
113 | u32 loader_offset; // Offset to bootloader code. | |
114 | u32 loader_code_address; // Start address of bootloader. | |
115 | u32 loader_code_end; // Where bootloader code ends. | |
116 | u32 loader_code_size; | |
117 | u32 version_data_offset; // Offset were scrambled version data begins. | |
118 | u32 version_data_size; // Size, in words, of scrambled version data. | |
119 | u32 nDspImages; // Number of DSP images in file. | |
8e0fd2cf | 120 | } __attribute__ ((packed)); |
f7c1be0c | 121 | |
8e0fd2cf | 122 | struct dsp_image_info { |
14e5d8ef | 123 | u32 coff_date; // Date/time when DSP Coff image was built. |
124 | u32 begin_offset; // Offset in file where image begins. | |
125 | u32 end_offset; // Offset in file where image begins. | |
126 | u32 run_address; // On chip Start address of DSP code. | |
127 | u32 image_size; // Size of image. | |
128 | u32 version; // Embedded version # of DSP code. | |
8e0fd2cf | 129 | } __attribute__ ((packed)); |
f7c1be0c | 130 | |
8e0fd2cf | 131 | struct dsp_image_info_v6 { |
14e5d8ef | 132 | u32 coff_date; // Date/time when DSP Coff image was built. |
133 | u32 begin_offset; // Offset in file where image begins. | |
134 | u32 end_offset; // Offset in file where image begins. | |
135 | u32 run_address; // On chip Start address of DSP code. | |
136 | u32 image_size; // Size of image. | |
137 | u32 version; // Embedded version # of DSP code. | |
f7c1be0c MB |
138 | unsigned short checksum; // Dsp File checksum |
139 | unsigned short pad1; | |
8e0fd2cf | 140 | } __attribute__ ((packed)); |
f7c1be0c MB |
141 | |
142 | void card_bootload(struct net_device *dev) | |
143 | { | |
d3706552 | 144 | struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev); |
f7c1be0c | 145 | unsigned long flags; |
ca145277 OZ |
146 | u32 *pdata; |
147 | u32 size; | |
148 | u32 i; | |
149 | u32 templong; | |
f7c1be0c MB |
150 | |
151 | DEBUG(0, "card_bootload is called\n"); | |
152 | ||
ca145277 | 153 | pdata = (u32 *) bootimage; |
f7c1be0c MB |
154 | size = sizeof(bootimage); |
155 | ||
156 | // check for odd word | |
157 | if (size & 0x0003) { | |
158 | size += 4; | |
159 | } | |
160 | // Provide mutual exclusive access while reading ASIC registers. | |
161 | spin_lock_irqsave(&info->dpram_lock, flags); | |
162 | ||
163 | // need to set i/o base address initially and hardware will autoincrement | |
164 | ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, FT1000_DPRAM_BASE); | |
165 | // write bytes | |
166 | for (i = 0; i < (size >> 2); i++) { | |
167 | templong = *pdata++; | |
168 | outl(templong, dev->base_addr + FT1000_REG_MAG_DPDATA); | |
169 | } | |
170 | ||
171 | spin_unlock_irqrestore(&info->dpram_lock, flags); | |
172 | } | |
173 | ||
ca145277 | 174 | u16 get_handshake(struct net_device *dev, u16 expected_value) |
f7c1be0c | 175 | { |
d3706552 | 176 | struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev); |
ca145277 OZ |
177 | u16 handshake; |
178 | u32 tempx; | |
f7c1be0c MB |
179 | int loopcnt; |
180 | ||
181 | loopcnt = 0; | |
182 | while (loopcnt < MAX_DSP_WAIT_LOOPS) { | |
183 | if (info->AsicID == ELECTRABUZZ_ID) { | |
184 | ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, | |
185 | DWNLD_HANDSHAKE_LOC); | |
186 | ||
187 | handshake = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA); | |
188 | } else { | |
189 | tempx = | |
190 | ntohl(ft1000_read_dpram_mag_32 | |
191 | (dev, DWNLD_MAG_HANDSHAKE_LOC)); | |
ca145277 | 192 | handshake = (u16) tempx; |
f7c1be0c MB |
193 | } |
194 | ||
195 | if ((handshake == expected_value) | |
196 | || (handshake == HANDSHAKE_RESET_VALUE)) { | |
197 | return handshake; | |
198 | } else { | |
199 | loopcnt++; | |
200 | mdelay(DSP_WAIT_SLEEP_TIME); | |
201 | } | |
202 | ||
203 | } | |
204 | ||
205 | return HANDSHAKE_TIMEOUT_VALUE; | |
206 | ||
207 | } | |
208 | ||
ca145277 | 209 | void put_handshake(struct net_device *dev, u16 handshake_value) |
f7c1be0c | 210 | { |
d3706552 | 211 | struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev); |
ca145277 | 212 | u32 tempx; |
f7c1be0c MB |
213 | |
214 | if (info->AsicID == ELECTRABUZZ_ID) { | |
215 | ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, | |
216 | DWNLD_HANDSHAKE_LOC); | |
217 | ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, handshake_value); /* Handshake */ | |
218 | } else { | |
ca145277 | 219 | tempx = (u32) handshake_value; |
f7c1be0c MB |
220 | tempx = ntohl(tempx); |
221 | ft1000_write_dpram_mag_32(dev, DWNLD_MAG_HANDSHAKE_LOC, tempx); /* Handshake */ | |
222 | } | |
223 | } | |
224 | ||
ca145277 | 225 | u16 get_request_type(struct net_device *dev) |
f7c1be0c | 226 | { |
d3706552 | 227 | struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev); |
ca145277 OZ |
228 | u16 request_type; |
229 | u32 tempx; | |
f7c1be0c MB |
230 | |
231 | if (info->AsicID == ELECTRABUZZ_ID) { | |
232 | ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, DWNLD_TYPE_LOC); | |
233 | request_type = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA); | |
234 | } else { | |
235 | tempx = ft1000_read_dpram_mag_32(dev, DWNLD_MAG_TYPE_LOC); | |
236 | tempx = ntohl(tempx); | |
ca145277 | 237 | request_type = (u16) tempx; |
f7c1be0c MB |
238 | } |
239 | ||
240 | return request_type; | |
241 | ||
242 | } | |
243 | ||
244 | long get_request_value(struct net_device *dev) | |
245 | { | |
d3706552 | 246 | struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev); |
f7c1be0c | 247 | long value; |
ca145277 | 248 | u16 w_val; |
f7c1be0c MB |
249 | |
250 | if (info->AsicID == ELECTRABUZZ_ID) { | |
251 | ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, | |
252 | DWNLD_SIZE_MSW_LOC); | |
253 | ||
254 | w_val = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA); | |
255 | ||
256 | value = (long)(w_val << 16); | |
257 | ||
258 | ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, | |
259 | DWNLD_SIZE_LSW_LOC); | |
260 | ||
261 | w_val = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA); | |
262 | ||
263 | value = (long)(value | w_val); | |
264 | } else { | |
265 | value = ft1000_read_dpram_mag_32(dev, DWNLD_MAG_SIZE_LOC); | |
266 | value = ntohl(value); | |
267 | } | |
268 | ||
269 | return value; | |
270 | ||
271 | } | |
272 | ||
273 | void put_request_value(struct net_device *dev, long lvalue) | |
274 | { | |
d3706552 | 275 | struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev); |
ca145277 OZ |
276 | u16 size; |
277 | u32 tempx; | |
f7c1be0c MB |
278 | |
279 | if (info->AsicID == ELECTRABUZZ_ID) { | |
ca145277 | 280 | size = (u16) (lvalue >> 16); |
f7c1be0c MB |
281 | |
282 | ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, | |
283 | DWNLD_SIZE_MSW_LOC); | |
284 | ||
285 | ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, size); | |
286 | ||
ca145277 | 287 | size = (u16) (lvalue); |
f7c1be0c MB |
288 | |
289 | ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, | |
290 | DWNLD_SIZE_LSW_LOC); | |
291 | ||
292 | ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, size); | |
293 | } else { | |
294 | tempx = ntohl(lvalue); | |
295 | ft1000_write_dpram_mag_32(dev, DWNLD_MAG_SIZE_LOC, tempx); /* Handshake */ | |
296 | } | |
297 | ||
298 | } | |
299 | ||
ca145277 | 300 | u16 hdr_checksum(struct pseudo_hdr *pHdr) |
f7c1be0c | 301 | { |
ca145277 OZ |
302 | u16 *usPtr = (u16 *) pHdr; |
303 | u16 chksum; | |
f7c1be0c MB |
304 | |
305 | chksum = ((((((usPtr[0] ^ usPtr[1]) ^ usPtr[2]) ^ usPtr[3]) ^ | |
306 | usPtr[4]) ^ usPtr[5]) ^ usPtr[6]); | |
307 | ||
308 | return chksum; | |
309 | } | |
310 | ||
ca145277 | 311 | int card_download(struct net_device *dev, const u8 *pFileStart, u32 FileLength) |
f7c1be0c | 312 | { |
d3706552 | 313 | struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev); |
f7c1be0c | 314 | int Status = SUCCESS; |
ca145277 OZ |
315 | u16 DspWordCnt = 0; |
316 | u32 uiState; | |
317 | u16 handshake; | |
2c9bf839 | 318 | struct pseudo_hdr *pHdr; |
ca145277 | 319 | u16 usHdrLength; |
8e0fd2cf | 320 | struct dsp_file_hdr *pFileHdr; |
f7c1be0c | 321 | long word_length; |
ca145277 OZ |
322 | u16 request; |
323 | u16 temp; | |
3aaf8073 | 324 | struct prov_record *pprov_record; |
ca145277 | 325 | u8 *pbuffer; |
8e0fd2cf OZ |
326 | struct dsp_file_hdr_5 *pFileHdr5; |
327 | struct dsp_image_info *pDspImageInfo = NULL; | |
328 | struct dsp_image_info_v6 *pDspImageInfoV6 = NULL; | |
f7c1be0c | 329 | long requested_version; |
ca145277 | 330 | bool bGoodVersion = 0; |
8bc0d6fb | 331 | struct drv_msg *pMailBoxData; |
ca145277 OZ |
332 | u16 *pUsData = NULL; |
333 | u16 *pUsFile = NULL; | |
334 | u8 *pUcFile = NULL; | |
335 | u8 *pBootEnd = NULL; | |
336 | u8 *pCodeEnd = NULL; | |
f7c1be0c MB |
337 | int imageN; |
338 | long file_version; | |
339 | long loader_code_address = 0; | |
340 | long loader_code_size = 0; | |
341 | long run_address = 0; | |
342 | long run_size = 0; | |
343 | unsigned long flags; | |
344 | unsigned long templong; | |
345 | unsigned long image_chksum = 0; | |
346 | ||
347 | // | |
348 | // Get version id of file, at first 4 bytes of file, for newer files. | |
349 | // | |
350 | file_version = *(long *)pFileStart; | |
351 | ||
352 | uiState = STATE_START_DWNLD; | |
353 | ||
8e0fd2cf OZ |
354 | pFileHdr = (struct dsp_file_hdr *) pFileStart; |
355 | pFileHdr5 = (struct dsp_file_hdr_5 *) pFileStart; | |
f7c1be0c MB |
356 | |
357 | switch (file_version) { | |
358 | case 5: | |
359 | case 6: | |
360 | pUsFile = | |
ca145277 | 361 | (u16 *) ((long)pFileStart + pFileHdr5->loader_offset); |
f7c1be0c | 362 | pUcFile = |
ca145277 | 363 | (u8 *) ((long)pFileStart + pFileHdr5->loader_offset); |
f7c1be0c MB |
364 | |
365 | pBootEnd = | |
ca145277 | 366 | (u8 *) ((long)pFileStart + pFileHdr5->loader_code_end); |
f7c1be0c MB |
367 | |
368 | loader_code_address = pFileHdr5->loader_code_address; | |
369 | loader_code_size = pFileHdr5->loader_code_size; | |
ca145277 | 370 | bGoodVersion = false; |
f7c1be0c MB |
371 | break; |
372 | ||
373 | default: | |
374 | Status = FAILURE; | |
375 | break; | |
376 | } | |
377 | ||
378 | while ((Status == SUCCESS) && (uiState != STATE_DONE_FILE)) { | |
379 | ||
380 | switch (uiState) { | |
381 | case STATE_START_DWNLD: | |
382 | ||
383 | handshake = get_handshake(dev, HANDSHAKE_DSP_BL_READY); | |
384 | ||
385 | if (handshake == HANDSHAKE_DSP_BL_READY) { | |
386 | put_handshake(dev, HANDSHAKE_DRIVER_READY); | |
387 | } else { | |
388 | Status = FAILURE; | |
389 | } | |
390 | ||
391 | uiState = STATE_BOOT_DWNLD; | |
392 | ||
393 | break; | |
394 | ||
395 | case STATE_BOOT_DWNLD: | |
396 | handshake = get_handshake(dev, HANDSHAKE_REQUEST); | |
397 | if (handshake == HANDSHAKE_REQUEST) { | |
398 | /* | |
399 | * Get type associated with the request. | |
400 | */ | |
401 | request = get_request_type(dev); | |
402 | switch (request) { | |
403 | case REQUEST_RUN_ADDRESS: | |
404 | put_request_value(dev, | |
405 | loader_code_address); | |
406 | break; | |
407 | case REQUEST_CODE_LENGTH: | |
408 | put_request_value(dev, | |
409 | loader_code_size); | |
410 | break; | |
411 | case REQUEST_DONE_BL: | |
412 | /* Reposition ptrs to beginning of code section */ | |
ca145277 OZ |
413 | pUsFile = (u16 *) ((long)pBootEnd); |
414 | pUcFile = (u8 *) ((long)pBootEnd); | |
f7c1be0c MB |
415 | uiState = STATE_CODE_DWNLD; |
416 | break; | |
417 | case REQUEST_CODE_SEGMENT: | |
418 | word_length = get_request_value(dev); | |
419 | if (word_length > MAX_LENGTH) { | |
420 | Status = FAILURE; | |
421 | break; | |
422 | } | |
423 | if ((word_length * 2 + (long)pUcFile) > | |
424 | (long)pBootEnd) { | |
425 | /* | |
426 | * Error, beyond boot code range. | |
427 | */ | |
428 | Status = FAILURE; | |
429 | break; | |
430 | } | |
431 | // Provide mutual exclusive access while reading ASIC registers. | |
432 | spin_lock_irqsave(&info->dpram_lock, | |
433 | flags); | |
434 | if (file_version == 5) { | |
435 | /* | |
436 | * Position ASIC DPRAM auto-increment pointer. | |
437 | */ | |
438 | ft1000_write_reg(dev, | |
439 | FT1000_REG_DPRAM_ADDR, | |
440 | DWNLD_PS_HDR_LOC); | |
441 | ||
442 | for (; word_length > 0; word_length--) { /* In words */ | |
443 | //temp = *pUsFile; | |
444 | //temp = RtlUshortByteSwap(temp); | |
445 | ft1000_write_reg(dev, | |
446 | FT1000_REG_DPRAM_DATA, | |
447 | *pUsFile); | |
448 | pUsFile++; | |
449 | pUcFile += 2; | |
450 | DspWordCnt++; | |
451 | } | |
452 | } else { | |
453 | /* | |
454 | * Position ASIC DPRAM auto-increment pointer. | |
455 | */ | |
456 | outw(DWNLD_MAG_PS_HDR_LOC, | |
457 | dev->base_addr + | |
458 | FT1000_REG_DPRAM_ADDR); | |
459 | if (word_length & 0x01) { | |
460 | word_length++; | |
461 | } | |
462 | word_length = word_length / 2; | |
463 | ||
464 | for (; word_length > 0; word_length--) { /* In words */ | |
465 | templong = *pUsFile++; | |
466 | templong |= | |
467 | (*pUsFile++ << 16); | |
468 | pUcFile += 4; | |
469 | outl(templong, | |
470 | dev->base_addr + | |
471 | FT1000_REG_MAG_DPDATAL); | |
472 | } | |
473 | } | |
474 | spin_unlock_irqrestore(&info-> | |
475 | dpram_lock, | |
476 | flags); | |
477 | break; | |
478 | default: | |
479 | Status = FAILURE; | |
480 | break; | |
481 | } | |
482 | put_handshake(dev, HANDSHAKE_RESPONSE); | |
483 | } else { | |
484 | Status = FAILURE; | |
485 | } | |
486 | ||
487 | break; | |
488 | ||
489 | case STATE_CODE_DWNLD: | |
490 | handshake = get_handshake(dev, HANDSHAKE_REQUEST); | |
491 | if (handshake == HANDSHAKE_REQUEST) { | |
492 | /* | |
493 | * Get type associated with the request. | |
494 | */ | |
495 | request = get_request_type(dev); | |
496 | switch (request) { | |
497 | case REQUEST_FILE_CHECKSUM: | |
498 | DEBUG(0, | |
499 | "ft1000_dnld: REQUEST_FOR_CHECKSUM\n"); | |
500 | put_request_value(dev, image_chksum); | |
501 | break; | |
502 | case REQUEST_RUN_ADDRESS: | |
503 | if (bGoodVersion) { | |
504 | put_request_value(dev, | |
505 | run_address); | |
506 | } else { | |
507 | Status = FAILURE; | |
508 | break; | |
509 | } | |
510 | break; | |
511 | case REQUEST_CODE_LENGTH: | |
512 | if (bGoodVersion) { | |
513 | put_request_value(dev, | |
514 | run_size); | |
515 | } else { | |
516 | Status = FAILURE; | |
517 | break; | |
518 | } | |
519 | break; | |
520 | case REQUEST_DONE_CL: | |
521 | /* Reposition ptrs to beginning of provisioning section */ | |
522 | switch (file_version) { | |
523 | case 5: | |
524 | case 6: | |
525 | pUsFile = | |
ca145277 | 526 | (u16 *) ((long)pFileStart |
f7c1be0c MB |
527 | + |
528 | pFileHdr5-> | |
529 | commands_offset); | |
530 | pUcFile = | |
ca145277 | 531 | (u8 *) ((long)pFileStart |
f7c1be0c MB |
532 | + |
533 | pFileHdr5-> | |
534 | commands_offset); | |
535 | break; | |
536 | default: | |
537 | Status = FAILURE; | |
538 | break; | |
539 | } | |
540 | uiState = STATE_DONE_DWNLD; | |
541 | break; | |
542 | case REQUEST_CODE_SEGMENT: | |
543 | if (!bGoodVersion) { | |
544 | Status = FAILURE; | |
545 | break; | |
546 | } | |
547 | word_length = get_request_value(dev); | |
548 | if (word_length > MAX_LENGTH) { | |
549 | Status = FAILURE; | |
550 | break; | |
551 | } | |
552 | if ((word_length * 2 + (long)pUcFile) > | |
553 | (long)pCodeEnd) { | |
554 | /* | |
555 | * Error, beyond boot code range. | |
556 | */ | |
557 | Status = FAILURE; | |
558 | break; | |
559 | } | |
560 | if (file_version == 5) { | |
561 | /* | |
562 | * Position ASIC DPRAM auto-increment pointer. | |
563 | */ | |
564 | ft1000_write_reg(dev, | |
565 | FT1000_REG_DPRAM_ADDR, | |
566 | DWNLD_PS_HDR_LOC); | |
567 | ||
568 | for (; word_length > 0; word_length--) { /* In words */ | |
569 | //temp = *pUsFile; | |
570 | //temp = RtlUshortByteSwap(temp); | |
571 | ft1000_write_reg(dev, | |
572 | FT1000_REG_DPRAM_DATA, | |
573 | *pUsFile); | |
574 | pUsFile++; | |
575 | pUcFile += 2; | |
576 | DspWordCnt++; | |
577 | } | |
578 | } else { | |
579 | /* | |
580 | * Position ASIC DPRAM auto-increment pointer. | |
581 | */ | |
582 | outw(DWNLD_MAG_PS_HDR_LOC, | |
583 | dev->base_addr + | |
584 | FT1000_REG_DPRAM_ADDR); | |
585 | if (word_length & 0x01) { | |
586 | word_length++; | |
587 | } | |
588 | word_length = word_length / 2; | |
589 | ||
590 | for (; word_length > 0; word_length--) { /* In words */ | |
591 | templong = *pUsFile++; | |
592 | templong |= | |
593 | (*pUsFile++ << 16); | |
594 | pUcFile += 4; | |
595 | outl(templong, | |
596 | dev->base_addr + | |
597 | FT1000_REG_MAG_DPDATAL); | |
598 | } | |
599 | } | |
600 | break; | |
601 | ||
602 | case REQUEST_MAILBOX_DATA: | |
603 | // Convert length from byte count to word count. Make sure we round up. | |
604 | word_length = | |
605 | (long)(info->DSPInfoBlklen + 1) / 2; | |
606 | put_request_value(dev, word_length); | |
607 | pMailBoxData = | |
8bc0d6fb | 608 | (struct drv_msg *) & info->DSPInfoBlk[0]; |
f7c1be0c | 609 | pUsData = |
ca145277 | 610 | (u16 *) & pMailBoxData->data[0]; |
f7c1be0c MB |
611 | // Provide mutual exclusive access while reading ASIC registers. |
612 | spin_lock_irqsave(&info->dpram_lock, | |
613 | flags); | |
614 | if (file_version == 5) { | |
615 | /* | |
616 | * Position ASIC DPRAM auto-increment pointer. | |
617 | */ | |
618 | ft1000_write_reg(dev, | |
619 | FT1000_REG_DPRAM_ADDR, | |
620 | DWNLD_PS_HDR_LOC); | |
621 | ||
622 | for (; word_length > 0; word_length--) { /* In words */ | |
623 | temp = ntohs(*pUsData); | |
624 | ft1000_write_reg(dev, | |
625 | FT1000_REG_DPRAM_DATA, | |
626 | temp); | |
627 | pUsData++; | |
628 | } | |
629 | } else { | |
630 | /* | |
631 | * Position ASIC DPRAM auto-increment pointer. | |
632 | */ | |
633 | outw(DWNLD_MAG_PS_HDR_LOC, | |
634 | dev->base_addr + | |
635 | FT1000_REG_DPRAM_ADDR); | |
636 | if (word_length & 0x01) { | |
637 | word_length++; | |
638 | } | |
639 | word_length = word_length / 2; | |
640 | ||
641 | for (; word_length > 0; word_length--) { /* In words */ | |
642 | templong = *pUsData++; | |
643 | templong |= | |
644 | (*pUsData++ << 16); | |
645 | outl(templong, | |
646 | dev->base_addr + | |
647 | FT1000_REG_MAG_DPDATAL); | |
648 | } | |
649 | } | |
650 | spin_unlock_irqrestore(&info-> | |
651 | dpram_lock, | |
652 | flags); | |
653 | break; | |
654 | ||
655 | case REQUEST_VERSION_INFO: | |
656 | word_length = | |
657 | pFileHdr5->version_data_size; | |
658 | put_request_value(dev, word_length); | |
659 | pUsFile = | |
ca145277 | 660 | (u16 *) ((long)pFileStart + |
f7c1be0c MB |
661 | pFileHdr5-> |
662 | version_data_offset); | |
663 | // Provide mutual exclusive access while reading ASIC registers. | |
664 | spin_lock_irqsave(&info->dpram_lock, | |
665 | flags); | |
666 | if (file_version == 5) { | |
667 | /* | |
668 | * Position ASIC DPRAM auto-increment pointer. | |
669 | */ | |
670 | ft1000_write_reg(dev, | |
671 | FT1000_REG_DPRAM_ADDR, | |
672 | DWNLD_PS_HDR_LOC); | |
673 | ||
674 | for (; word_length > 0; word_length--) { /* In words */ | |
675 | ft1000_write_reg(dev, | |
676 | FT1000_REG_DPRAM_DATA, | |
677 | *pUsFile | |
678 | /*temp */ | |
679 | ); | |
680 | pUsFile++; | |
681 | } | |
682 | } else { | |
683 | /* | |
684 | * Position ASIC DPRAM auto-increment pointer. | |
685 | */ | |
686 | outw(DWNLD_MAG_PS_HDR_LOC, | |
687 | dev->base_addr + | |
688 | FT1000_REG_DPRAM_ADDR); | |
689 | if (word_length & 0x01) { | |
690 | word_length++; | |
691 | } | |
692 | word_length = word_length / 2; | |
693 | ||
694 | for (; word_length > 0; word_length--) { /* In words */ | |
695 | templong = | |
696 | ntohs(*pUsFile++); | |
697 | temp = | |
698 | ntohs(*pUsFile++); | |
699 | templong |= | |
700 | (temp << 16); | |
701 | outl(templong, | |
702 | dev->base_addr + | |
703 | FT1000_REG_MAG_DPDATAL); | |
704 | } | |
705 | } | |
706 | spin_unlock_irqrestore(&info-> | |
707 | dpram_lock, | |
708 | flags); | |
709 | break; | |
710 | ||
711 | case REQUEST_CODE_BY_VERSION: | |
ca145277 | 712 | bGoodVersion = false; |
f7c1be0c MB |
713 | requested_version = |
714 | get_request_value(dev); | |
715 | if (file_version == 5) { | |
716 | pDspImageInfo = | |
8e0fd2cf | 717 | (struct dsp_image_info *) ((long) |
f7c1be0c MB |
718 | pFileStart |
719 | + | |
720 | sizeof | |
8e0fd2cf | 721 | (struct dsp_file_hdr_5)); |
f7c1be0c MB |
722 | for (imageN = 0; |
723 | imageN < | |
724 | pFileHdr5->nDspImages; | |
725 | imageN++) { | |
726 | if (pDspImageInfo-> | |
727 | version == | |
728 | requested_version) { | |
729 | bGoodVersion = | |
ca145277 | 730 | true; |
f7c1be0c | 731 | pUsFile = |
ca145277 | 732 | (u16 |
f7c1be0c MB |
733 | *) ((long) |
734 | pFileStart | |
735 | + | |
736 | pDspImageInfo-> | |
737 | begin_offset); | |
738 | pUcFile = | |
ca145277 | 739 | (u8 |
f7c1be0c MB |
740 | *) ((long) |
741 | pFileStart | |
742 | + | |
743 | pDspImageInfo-> | |
744 | begin_offset); | |
745 | pCodeEnd = | |
ca145277 | 746 | (u8 |
f7c1be0c MB |
747 | *) ((long) |
748 | pFileStart | |
749 | + | |
750 | pDspImageInfo-> | |
751 | end_offset); | |
752 | run_address = | |
753 | pDspImageInfo-> | |
754 | run_address; | |
755 | run_size = | |
756 | pDspImageInfo-> | |
757 | image_size; | |
758 | break; | |
759 | } | |
760 | pDspImageInfo++; | |
761 | } | |
762 | } else { | |
763 | pDspImageInfoV6 = | |
8e0fd2cf | 764 | (struct dsp_image_info_v6 *) ((long) |
f7c1be0c MB |
765 | pFileStart |
766 | + | |
767 | sizeof | |
8e0fd2cf | 768 | (struct dsp_file_hdr_5)); |
f7c1be0c MB |
769 | for (imageN = 0; |
770 | imageN < | |
771 | pFileHdr5->nDspImages; | |
772 | imageN++) { | |
ca145277 | 773 | temp = (u16) |
f7c1be0c MB |
774 | (pDspImageInfoV6-> |
775 | version); | |
776 | templong = temp; | |
ca145277 | 777 | temp = (u16) |
f7c1be0c MB |
778 | (pDspImageInfoV6-> |
779 | version >> 16); | |
780 | templong |= | |
781 | (temp << 16); | |
782 | if (templong == | |
783 | requested_version) { | |
784 | bGoodVersion = | |
ca145277 | 785 | true; |
f7c1be0c | 786 | pUsFile = |
ca145277 | 787 | (u16 |
f7c1be0c MB |
788 | *) ((long) |
789 | pFileStart | |
790 | + | |
791 | pDspImageInfoV6-> | |
792 | begin_offset); | |
793 | pUcFile = | |
ca145277 | 794 | (u8 |
f7c1be0c MB |
795 | *) ((long) |
796 | pFileStart | |
797 | + | |
798 | pDspImageInfoV6-> | |
799 | begin_offset); | |
800 | pCodeEnd = | |
ca145277 | 801 | (u8 |
f7c1be0c MB |
802 | *) ((long) |
803 | pFileStart | |
804 | + | |
805 | pDspImageInfoV6-> | |
806 | end_offset); | |
807 | run_address = | |
808 | pDspImageInfoV6-> | |
809 | run_address; | |
810 | run_size = | |
811 | pDspImageInfoV6-> | |
812 | image_size; | |
813 | image_chksum = | |
ca145277 | 814 | (u32) |
f7c1be0c MB |
815 | pDspImageInfoV6-> |
816 | checksum; | |
817 | DEBUG(0, | |
818 | "ft1000_dnld: image_chksum = 0x%8x\n", | |
819 | (unsigned | |
820 | int) | |
821 | image_chksum); | |
822 | break; | |
823 | } | |
824 | pDspImageInfoV6++; | |
825 | } | |
826 | } | |
827 | if (!bGoodVersion) { | |
828 | /* | |
829 | * Error, beyond boot code range. | |
830 | */ | |
831 | Status = FAILURE; | |
832 | break; | |
833 | } | |
834 | break; | |
835 | ||
836 | default: | |
837 | Status = FAILURE; | |
838 | break; | |
839 | } | |
840 | put_handshake(dev, HANDSHAKE_RESPONSE); | |
841 | } else { | |
842 | Status = FAILURE; | |
843 | } | |
844 | ||
845 | break; | |
846 | ||
847 | case STATE_DONE_DWNLD: | |
14e5d8ef | 848 | if (((unsigned long) (pUcFile) - (unsigned long) pFileStart) >= |
849 | (unsigned long) FileLength) { | |
f7c1be0c MB |
850 | uiState = STATE_DONE_FILE; |
851 | break; | |
852 | } | |
853 | ||
2c9bf839 | 854 | pHdr = (struct pseudo_hdr *) pUsFile; |
f7c1be0c MB |
855 | |
856 | if (pHdr->portdest == 0x80 /* DspOAM */ | |
857 | && (pHdr->portsrc == 0x00 /* Driver */ | |
858 | || pHdr->portsrc == 0x10 /* FMM */ )) { | |
859 | uiState = STATE_SECTION_PROV; | |
860 | } else { | |
861 | DEBUG(1, | |
862 | "FT1000:download:Download error: Bad Port IDs in Pseudo Record\n"); | |
863 | DEBUG(1, "\t Port Source = 0x%2.2x\n", | |
864 | pHdr->portsrc); | |
865 | DEBUG(1, "\t Port Destination = 0x%2.2x\n", | |
866 | pHdr->portdest); | |
867 | Status = FAILURE; | |
868 | } | |
869 | ||
870 | break; | |
871 | ||
872 | case STATE_SECTION_PROV: | |
873 | ||
2c9bf839 | 874 | pHdr = (struct pseudo_hdr *) pUcFile; |
f7c1be0c MB |
875 | |
876 | if (pHdr->checksum == hdr_checksum(pHdr)) { | |
877 | if (pHdr->portdest != 0x80 /* Dsp OAM */ ) { | |
878 | uiState = STATE_DONE_PROV; | |
879 | break; | |
880 | } | |
881 | usHdrLength = ntohs(pHdr->length); /* Byte length for PROV records */ | |
882 | ||
883 | // Get buffer for provisioning data | |
884 | pbuffer = | |
2c9bf839 | 885 | kmalloc((usHdrLength + sizeof(struct pseudo_hdr)), |
f7c1be0c MB |
886 | GFP_ATOMIC); |
887 | if (pbuffer) { | |
888 | memcpy(pbuffer, (void *)pUcFile, | |
ca145277 | 889 | (u32) (usHdrLength + |
2c9bf839 | 890 | sizeof(struct pseudo_hdr))); |
bf3146c8 | 891 | // link provisioning data |
f7c1be0c | 892 | pprov_record = |
3aaf8073 | 893 | kmalloc(sizeof(struct prov_record), |
f7c1be0c MB |
894 | GFP_ATOMIC); |
895 | if (pprov_record) { | |
896 | pprov_record->pprov_data = | |
897 | pbuffer; | |
898 | list_add_tail(&pprov_record-> | |
899 | list, | |
900 | &info->prov_list); | |
901 | // Move to next entry if available | |
902 | pUcFile = | |
ca145277 | 903 | (u8 *) ((unsigned long) pUcFile + |
2c9bf839 | 904 | (unsigned long) ((usHdrLength + 1) & 0xFFFFFFFE) + sizeof(struct pseudo_hdr)); |
14e5d8ef | 905 | if ((unsigned long) (pUcFile) - |
906 | (unsigned long) (pFileStart) >= | |
907 | (unsigned long) FileLength) { | |
f7c1be0c MB |
908 | uiState = |
909 | STATE_DONE_FILE; | |
910 | } | |
911 | } else { | |
912 | kfree(pbuffer); | |
913 | Status = FAILURE; | |
914 | } | |
915 | } else { | |
916 | Status = FAILURE; | |
917 | } | |
918 | } else { | |
919 | /* Checksum did not compute */ | |
920 | Status = FAILURE; | |
921 | } | |
922 | ||
923 | break; | |
924 | ||
925 | case STATE_DONE_PROV: | |
926 | uiState = STATE_DONE_FILE; | |
927 | break; | |
928 | ||
929 | default: | |
930 | Status = FAILURE; | |
931 | break; | |
932 | } /* End Switch */ | |
933 | ||
934 | } /* End while */ | |
935 | ||
936 | return Status; | |
937 | ||
938 | } |