blob: f8b8e71284d9bb2b0f7aec8b7983919180dfbdb6 [file] [log] [blame]
Marek Beliskof7c1be02010-09-22 07:56:27 +02001/*---------------------------------------------------------------------------
2 FT1000 driver for Flarion Flash OFDM NIC Device
Greg Kroah-Hartmanbf3146c2010-09-22 08:34:49 -07003
Marek Beliskof7c1be02010-09-22 07:56:27 +02004 Copyright (C) 2002 Flarion Technologies, All rights reserved.
Greg Kroah-Hartmanbf3146c2010-09-22 08:34:49 -07005
6 This program is free software; you can redistribute it and/or modify it
Marek Beliskof7c1be02010-09-22 07:56:27 +02007 under the terms of the GNU General Public License as published by the Free
Greg Kroah-Hartmanbf3146c2010-09-22 08:34:49 -07008 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.
Marek Beliskof7c1be02010-09-22 07:56:27 +020016 --------------------------------------------------------------------------
17
Greg Kroah-Hartmanbf3146c2010-09-22 08:34:49 -070018 Description: This module will handshake with the DSP bootloader to
Marek Beliskof7c1be02010-09-22 07:56:27 +020019 download the DSP runtime image.
Greg Kroah-Hartmanbf3146c2010-09-22 08:34:49 -070020
Marek Beliskof7c1be02010-09-22 07:56:27 +020021---------------------------------------------------------------------------*/
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>
Marek Beliskof7c1be02010-09-22 07:56:27 +020033#include <asm/io.h>
34#include <asm/uaccess.h>
35#include <linux/vmalloc.h>
36
Marek Beliskof7c1be02010-09-22 07:56:27 +020037#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
Ondrej Zaryca145272011-07-01 00:03:47 +020089u16 get_handshake(struct net_device *dev, u16 expected_value);
90void put_handshake(struct net_device *dev, u16 handshake_value);
91u16 get_request_type(struct net_device *dev);
Marek Beliskof7c1be02010-09-22 07:56:27 +020092long get_request_value(struct net_device *dev);
93void put_request_value(struct net_device *dev, long lvalue);
Ondrej Zaryca145272011-07-01 00:03:47 +020094u16 hdr_checksum(struct pseudo_hdr *pHdr);
Marek Beliskof7c1be02010-09-22 07:56:27 +020095
Ondrej Zary8e0fd2c2011-07-01 00:03:45 +020096struct dsp_file_hdr {
pixo14e5d8e2011-03-23 08:08:08 +010097 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.
104 u32 loader_code_size;
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.
Ondrej Zary8e0fd2c2011-07-01 00:03:45 +0200108} __attribute__ ((packed));
Marek Beliskof7c1be02010-09-22 07:56:27 +0200109
Ondrej Zary8e0fd2c2011-07-01 00:03:45 +0200110struct dsp_image_info {
pixo14e5d8e2011-03-23 08:08:08 +0100111 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.
Marek Beliskof7c1be02010-09-22 07:56:27 +0200117 unsigned short checksum; // Dsp File checksum
118 unsigned short pad1;
Ondrej Zary8e0fd2c2011-07-01 00:03:45 +0200119} __attribute__ ((packed));
Marek Beliskof7c1be02010-09-22 07:56:27 +0200120
121void card_bootload(struct net_device *dev)
122{
Ondrej Zaryd3706552011-07-01 00:03:42 +0200123 struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev);
Marek Beliskof7c1be02010-09-22 07:56:27 +0200124 unsigned long flags;
Ondrej Zaryca145272011-07-01 00:03:47 +0200125 u32 *pdata;
126 u32 size;
127 u32 i;
128 u32 templong;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200129
130 DEBUG(0, "card_bootload is called\n");
131
Ondrej Zaryca145272011-07-01 00:03:47 +0200132 pdata = (u32 *) bootimage;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200133 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
Ondrej Zaryca145272011-07-01 00:03:47 +0200153u16 get_handshake(struct net_device *dev, u16 expected_value)
Marek Beliskof7c1be02010-09-22 07:56:27 +0200154{
Ondrej Zaryd3706552011-07-01 00:03:42 +0200155 struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev);
Ondrej Zaryca145272011-07-01 00:03:47 +0200156 u16 handshake;
157 u32 tempx;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200158 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));
Ondrej Zaryca145272011-07-01 00:03:47 +0200171 handshake = (u16) tempx;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200172 }
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
Ondrej Zaryca145272011-07-01 00:03:47 +0200188void put_handshake(struct net_device *dev, u16 handshake_value)
Marek Beliskof7c1be02010-09-22 07:56:27 +0200189{
Ondrej Zaryd3706552011-07-01 00:03:42 +0200190 struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev);
Ondrej Zaryca145272011-07-01 00:03:47 +0200191 u32 tempx;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200192
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 {
Ondrej Zaryca145272011-07-01 00:03:47 +0200198 tempx = (u32) handshake_value;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200199 tempx = ntohl(tempx);
200 ft1000_write_dpram_mag_32(dev, DWNLD_MAG_HANDSHAKE_LOC, tempx); /* Handshake */
201 }
202}
203
Ondrej Zaryca145272011-07-01 00:03:47 +0200204u16 get_request_type(struct net_device *dev)
Marek Beliskof7c1be02010-09-22 07:56:27 +0200205{
Ondrej Zaryd3706552011-07-01 00:03:42 +0200206 struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev);
Ondrej Zaryca145272011-07-01 00:03:47 +0200207 u16 request_type;
208 u32 tempx;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200209
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);
Ondrej Zaryca145272011-07-01 00:03:47 +0200216 request_type = (u16) tempx;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200217 }
218
219 return request_type;
220
221}
222
223long get_request_value(struct net_device *dev)
224{
Ondrej Zaryd3706552011-07-01 00:03:42 +0200225 struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev);
Marek Beliskof7c1be02010-09-22 07:56:27 +0200226 long value;
Ondrej Zaryca145272011-07-01 00:03:47 +0200227 u16 w_val;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200228
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
252void put_request_value(struct net_device *dev, long lvalue)
253{
Ondrej Zaryd3706552011-07-01 00:03:42 +0200254 struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev);
Ondrej Zaryca145272011-07-01 00:03:47 +0200255 u16 size;
256 u32 tempx;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200257
258 if (info->AsicID == ELECTRABUZZ_ID) {
Ondrej Zaryca145272011-07-01 00:03:47 +0200259 size = (u16) (lvalue >> 16);
Marek Beliskof7c1be02010-09-22 07:56:27 +0200260
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
Ondrej Zaryca145272011-07-01 00:03:47 +0200266 size = (u16) (lvalue);
Marek Beliskof7c1be02010-09-22 07:56:27 +0200267
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
Ondrej Zaryca145272011-07-01 00:03:47 +0200279u16 hdr_checksum(struct pseudo_hdr *pHdr)
Marek Beliskof7c1be02010-09-22 07:56:27 +0200280{
Ondrej Zaryca145272011-07-01 00:03:47 +0200281 u16 *usPtr = (u16 *) pHdr;
282 u16 chksum;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200283
284 chksum = ((((((usPtr[0] ^ usPtr[1]) ^ usPtr[2]) ^ usPtr[3]) ^
285 usPtr[4]) ^ usPtr[5]) ^ usPtr[6]);
286
287 return chksum;
288}
289
Randy Dunlapeb93ca42011-08-08 11:31:48 -0700290int card_download(struct net_device *dev, const u8 *pFileStart,
291 size_t FileLength)
Marek Beliskof7c1be02010-09-22 07:56:27 +0200292{
Ondrej Zaryd3706552011-07-01 00:03:42 +0200293 struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev);
Marek Beliskof7c1be02010-09-22 07:56:27 +0200294 int Status = SUCCESS;
Ondrej Zaryca145272011-07-01 00:03:47 +0200295 u32 uiState;
296 u16 handshake;
Ondrej Zary2c9bf832011-07-01 00:03:34 +0200297 struct pseudo_hdr *pHdr;
Ondrej Zaryca145272011-07-01 00:03:47 +0200298 u16 usHdrLength;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200299 long word_length;
Ondrej Zaryca145272011-07-01 00:03:47 +0200300 u16 request;
301 u16 temp;
Ondrej Zary3aaf8072011-07-01 00:03:41 +0200302 struct prov_record *pprov_record;
Ondrej Zaryca145272011-07-01 00:03:47 +0200303 u8 *pbuffer;
Ondrej Zarye161a442011-07-01 00:03:57 +0200304 struct dsp_file_hdr *pFileHdr5;
305 struct dsp_image_info *pDspImageInfoV6 = NULL;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200306 long requested_version;
Ondrej Zaryca145272011-07-01 00:03:47 +0200307 bool bGoodVersion = 0;
Ondrej Zary8bc0d6f2011-07-01 00:03:35 +0200308 struct drv_msg *pMailBoxData;
Ondrej Zaryca145272011-07-01 00:03:47 +0200309 u16 *pUsData = NULL;
310 u16 *pUsFile = NULL;
311 u8 *pUcFile = NULL;
312 u8 *pBootEnd = NULL;
313 u8 *pCodeEnd = NULL;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200314 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
Marek Beliskof7c1be02010-09-22 07:56:27 +0200324 file_version = *(long *)pFileStart;
Ondrej Zarye161a442011-07-01 00:03:57 +0200325 if (file_version != 6) {
326 printk(KERN_ERR "ft1000: unsupported firmware version %ld\n", file_version);
327 Status = FAILURE;
328 }
Marek Beliskof7c1be02010-09-22 07:56:27 +0200329
330 uiState = STATE_START_DWNLD;
331
Ondrej Zarye161a442011-07-01 00:03:57 +0200332 pFileHdr5 = (struct dsp_file_hdr *) pFileStart;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200333
Ondrej Zarye161a442011-07-01 00:03:57 +0200334 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;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200340
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 */
Ondrej Zaryca145272011-07-01 00:03:47 +0200376 pUsFile = (u16 *) ((long)pBootEnd);
377 pUcFile = (u8 *) ((long)pBootEnd);
Marek Beliskof7c1be02010-09-22 07:56:27 +0200378 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);
Ondrej Zarye161a442011-07-01 00:03:57 +0200397 /*
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;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200406
Ondrej Zarye161a442011-07-01 00:03:57 +0200407 for (; word_length > 0; word_length--) { /* In words */
408 templong = *pUsFile++;
409 templong |=
410 (*pUsFile++ << 16);
411 pUcFile += 4;
412 outl(templong,
Marek Beliskof7c1be02010-09-22 07:56:27 +0200413 dev->base_addr +
Ondrej Zarye161a442011-07-01 00:03:57 +0200414 FT1000_REG_MAG_DPDATAL);
Marek Beliskof7c1be02010-09-22 07:56:27 +0200415 }
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 */
Ondrej Zarye161a442011-07-01 00:03:57 +0200464 pUsFile = (u16 *) ((long)pFileStart + pFileHdr5->commands_offset);
465 pUcFile = (u8 *) ((long)pFileStart + pFileHdr5->commands_offset);
Marek Beliskof7c1be02010-09-22 07:56:27 +0200466 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 }
Ondrej Zarye161a442011-07-01 00:03:57 +0200486 /*
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;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200495
Ondrej Zarye161a442011-07-01 00:03:57 +0200496 for (; word_length > 0; word_length--) { /* In words */
497 templong = *pUsFile++;
498 templong |=
499 (*pUsFile++ << 16);
500 pUcFile += 4;
501 outl(templong,
Marek Beliskof7c1be02010-09-22 07:56:27 +0200502 dev->base_addr +
Ondrej Zarye161a442011-07-01 00:03:57 +0200503 FT1000_REG_MAG_DPDATAL);
Marek Beliskof7c1be02010-09-22 07:56:27 +0200504 }
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 =
Ondrej Zary8bc0d6f2011-07-01 00:03:35 +0200513 (struct drv_msg *) & info->DSPInfoBlk[0];
Marek Beliskof7c1be02010-09-22 07:56:27 +0200514 pUsData =
Ondrej Zaryca145272011-07-01 00:03:47 +0200515 (u16 *) & pMailBoxData->data[0];
Marek Beliskof7c1be02010-09-22 07:56:27 +0200516 // 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 =
Ondrej Zaryca145272011-07-01 00:03:47 +0200565 (u16 *) ((long)pFileStart +
Marek Beliskof7c1be02010-09-22 07:56:27 +0200566 pFileHdr5->
567 version_data_offset);
568 // Provide mutual exclusive access while reading ASIC registers.
569 spin_lock_irqsave(&info->dpram_lock,
570 flags);
Ondrej Zarye161a442011-07-01 00:03:57 +0200571 /*
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;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200580
Ondrej Zarye161a442011-07-01 00:03:57 +0200581 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,
Marek Beliskof7c1be02010-09-22 07:56:27 +0200589 dev->base_addr +
Ondrej Zarye161a442011-07-01 00:03:57 +0200590 FT1000_REG_MAG_DPDATAL);
Marek Beliskof7c1be02010-09-22 07:56:27 +0200591 }
592 spin_unlock_irqrestore(&info->
593 dpram_lock,
594 flags);
595 break;
596
597 case REQUEST_CODE_BY_VERSION:
Ondrej Zaryca145272011-07-01 00:03:47 +0200598 bGoodVersion = false;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200599 requested_version =
600 get_request_value(dev);
Ondrej Zarye161a442011-07-01 00:03:57 +0200601 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;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200661 }
Ondrej Zarye161a442011-07-01 00:03:57 +0200662 pDspImageInfoV6++;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200663 }
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:
pixo14e5d8e2011-03-23 08:08:08 +0100685 if (((unsigned long) (pUcFile) - (unsigned long) pFileStart) >=
686 (unsigned long) FileLength) {
Marek Beliskof7c1be02010-09-22 07:56:27 +0200687 uiState = STATE_DONE_FILE;
688 break;
689 }
690
Ondrej Zary2c9bf832011-07-01 00:03:34 +0200691 pHdr = (struct pseudo_hdr *) pUsFile;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200692
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
Ondrej Zary2c9bf832011-07-01 00:03:34 +0200711 pHdr = (struct pseudo_hdr *) pUcFile;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200712
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 =
Ondrej Zary2c9bf832011-07-01 00:03:34 +0200722 kmalloc((usHdrLength + sizeof(struct pseudo_hdr)),
Marek Beliskof7c1be02010-09-22 07:56:27 +0200723 GFP_ATOMIC);
724 if (pbuffer) {
725 memcpy(pbuffer, (void *)pUcFile,
Ondrej Zaryca145272011-07-01 00:03:47 +0200726 (u32) (usHdrLength +
Ondrej Zary2c9bf832011-07-01 00:03:34 +0200727 sizeof(struct pseudo_hdr)));
Greg Kroah-Hartmanbf3146c2010-09-22 08:34:49 -0700728 // link provisioning data
Marek Beliskof7c1be02010-09-22 07:56:27 +0200729 pprov_record =
Ondrej Zary3aaf8072011-07-01 00:03:41 +0200730 kmalloc(sizeof(struct prov_record),
Marek Beliskof7c1be02010-09-22 07:56:27 +0200731 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 =
Ondrej Zaryca145272011-07-01 00:03:47 +0200740 (u8 *) ((unsigned long) pUcFile +
Ondrej Zary2c9bf832011-07-01 00:03:34 +0200741 (unsigned long) ((usHdrLength + 1) & 0xFFFFFFFE) + sizeof(struct pseudo_hdr));
pixo14e5d8e2011-03-23 08:08:08 +0100742 if ((unsigned long) (pUcFile) -
743 (unsigned long) (pFileStart) >=
744 (unsigned long) FileLength) {
Marek Beliskof7c1be02010-09-22 07:56:27 +0200745 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}