1 /*---------------------------------------------------------------------------
2 FT1000 driver for Flarion Flash OFDM NIC Device
4 Copyright (C) 2002 Flarion Technologies, All rights reserved.
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the Free
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.
16 --------------------------------------------------------------------------
18 Description: This module will handshake with the DSP bootloader to
19 download the DSP runtime image.
21 ---------------------------------------------------------------------------*/
23 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
25 #define __KERNEL_SYSCALLS__
27 #include <linux/module.h>
30 #include <linux/slab.h>
31 #include <linux/unistd.h>
32 #include <linux/netdevice.h>
33 #include <linux/timer.h>
34 #include <linux/delay.h>
36 #include <linux/uaccess.h>
37 #include <linux/vmalloc.h>
42 #define MAX_DSP_WAIT_LOOPS 100
43 #define DSP_WAIT_SLEEP_TIME 1 /* 1 millisecond */
45 #define MAX_LENGTH 0x7f0
47 #define DWNLD_MAG_HANDSHAKE_LOC 0x00
48 #define DWNLD_MAG_TYPE_LOC 0x01
49 #define DWNLD_MAG_SIZE_LOC 0x02
50 #define DWNLD_MAG_PS_HDR_LOC 0x03
52 #define DWNLD_HANDSHAKE_LOC 0x02
53 #define DWNLD_TYPE_LOC 0x04
54 #define DWNLD_SIZE_MSW_LOC 0x06
55 #define DWNLD_SIZE_LSW_LOC 0x08
56 #define DWNLD_PS_HDR_LOC 0x0A
58 #define HANDSHAKE_TIMEOUT_VALUE 0xF1F1
59 #define HANDSHAKE_RESET_VALUE 0xFEFE /* When DSP requests startover */
60 #define HANDSHAKE_DSP_BL_READY 0xFEFE /* At start DSP writes this when bootloader ready */
61 #define HANDSHAKE_DRIVER_READY 0xFFFF /* Driver writes after receiving 0xFEFE */
62 #define HANDSHAKE_SEND_DATA 0x0000 /* DSP writes this when ready for more data */
64 #define HANDSHAKE_REQUEST 0x0001 /* Request from DSP */
65 #define HANDSHAKE_RESPONSE 0x0000 /* Satisfied DSP request */
67 #define REQUEST_CODE_LENGTH 0x0000
68 #define REQUEST_RUN_ADDRESS 0x0001
69 #define REQUEST_CODE_SEGMENT 0x0002 /* In WORD count */
70 #define REQUEST_DONE_BL 0x0003
71 #define REQUEST_DONE_CL 0x0004
72 #define REQUEST_VERSION_INFO 0x0005
73 #define REQUEST_CODE_BY_VERSION 0x0006
74 #define REQUEST_MAILBOX_DATA 0x0007
75 #define REQUEST_FILE_CHECKSUM 0x0008
77 #define STATE_START_DWNLD 0x01
78 #define STATE_BOOT_DWNLD 0x02
79 #define STATE_CODE_DWNLD 0x03
80 #define STATE_DONE_DWNLD 0x04
81 #define STATE_SECTION_PROV 0x05
82 #define STATE_DONE_PROV 0x06
83 #define STATE_DONE_FILE 0x07
86 u32 version_id
; /* Version ID of this image format. */
87 u32 package_id
; /* Package ID of code release. */
88 u32 build_date
; /* Date/time stamp when file was built. */
89 u32 commands_offset
; /* Offset to attached commands in Pseudo Hdr format. */
90 u32 loader_offset
; /* Offset to bootloader code. */
91 u32 loader_code_address
; /* Start address of bootloader. */
92 u32 loader_code_end
; /* Where bootloader code ends. */
94 u32 version_data_offset
; /* Offset were scrambled version data begins. */
95 u32 version_data_size
; /* Size, in words, of scrambled version data. */
96 u32 nDspImages
; /* Number of DSP images in file. */
99 struct dsp_image_info
{
100 u32 coff_date
; /* Date/time when DSP Coff image was built. */
101 u32 begin_offset
; /* Offset in file where image begins. */
102 u32 end_offset
; /* Offset in file where image begins. */
103 u32 run_address
; /* On chip Start address of DSP code. */
104 u32 image_size
; /* Size of image. */
105 u32 version
; /* Embedded version # of DSP code. */
106 unsigned short checksum
; /* Dsp File checksum */
110 void card_bootload(struct net_device
*dev
)
112 struct ft1000_info
*info
= netdev_priv(dev
);
119 netdev_dbg(dev
, "card_bootload is called\n");
121 pdata
= (u32
*)bootimage
;
122 size
= sizeof(bootimage
);
124 /* check for odd word */
128 /* Provide mutual exclusive access while reading ASIC registers. */
129 spin_lock_irqsave(&info
->dpram_lock
, flags
);
131 /* need to set i/o base address initially and hardware will autoincrement */
132 ft1000_write_reg(dev
, FT1000_REG_DPRAM_ADDR
, FT1000_DPRAM_BASE
);
134 for (i
= 0; i
< (size
>> 2); i
++) {
136 outl(templong
, dev
->base_addr
+ FT1000_REG_MAG_DPDATA
);
139 spin_unlock_irqrestore(&info
->dpram_lock
, flags
);
142 static u16
get_handshake(struct net_device
*dev
, u16 expected_value
)
144 struct ft1000_info
*info
= netdev_priv(dev
);
150 while (loopcnt
< MAX_DSP_WAIT_LOOPS
) {
151 if (info
->AsicID
== ELECTRABUZZ_ID
) {
152 ft1000_write_reg(dev
, FT1000_REG_DPRAM_ADDR
,
153 DWNLD_HANDSHAKE_LOC
);
155 handshake
= ft1000_read_reg(dev
, FT1000_REG_DPRAM_DATA
);
158 ntohl(ft1000_read_dpram_mag_32
159 (dev
, DWNLD_MAG_HANDSHAKE_LOC
));
160 handshake
= (u16
)tempx
;
163 if ((handshake
== expected_value
)
164 || (handshake
== HANDSHAKE_RESET_VALUE
)) {
168 mdelay(DSP_WAIT_SLEEP_TIME
);
172 return HANDSHAKE_TIMEOUT_VALUE
;
176 static void put_handshake(struct net_device
*dev
, u16 handshake_value
)
178 struct ft1000_info
*info
= netdev_priv(dev
);
181 if (info
->AsicID
== ELECTRABUZZ_ID
) {
182 ft1000_write_reg(dev
, FT1000_REG_DPRAM_ADDR
,
183 DWNLD_HANDSHAKE_LOC
);
184 ft1000_write_reg(dev
, FT1000_REG_DPRAM_DATA
, handshake_value
); /* Handshake */
186 tempx
= (u32
)handshake_value
;
187 tempx
= ntohl(tempx
);
188 ft1000_write_dpram_mag_32(dev
, DWNLD_MAG_HANDSHAKE_LOC
, tempx
); /* Handshake */
192 static u16
get_request_type(struct net_device
*dev
)
194 struct ft1000_info
*info
= netdev_priv(dev
);
198 if (info
->AsicID
== ELECTRABUZZ_ID
) {
199 ft1000_write_reg(dev
, FT1000_REG_DPRAM_ADDR
, DWNLD_TYPE_LOC
);
200 request_type
= ft1000_read_reg(dev
, FT1000_REG_DPRAM_DATA
);
202 tempx
= ft1000_read_dpram_mag_32(dev
, DWNLD_MAG_TYPE_LOC
);
203 tempx
= ntohl(tempx
);
204 request_type
= (u16
)tempx
;
211 static long get_request_value(struct net_device
*dev
)
213 struct ft1000_info
*info
= netdev_priv(dev
);
217 if (info
->AsicID
== ELECTRABUZZ_ID
) {
218 ft1000_write_reg(dev
, FT1000_REG_DPRAM_ADDR
,
221 w_val
= ft1000_read_reg(dev
, FT1000_REG_DPRAM_DATA
);
223 value
= (long)(w_val
<< 16);
225 ft1000_write_reg(dev
, FT1000_REG_DPRAM_ADDR
,
228 w_val
= ft1000_read_reg(dev
, FT1000_REG_DPRAM_DATA
);
230 value
= (long)(value
| w_val
);
232 value
= ft1000_read_dpram_mag_32(dev
, DWNLD_MAG_SIZE_LOC
);
233 value
= ntohl(value
);
240 static void put_request_value(struct net_device
*dev
, long lvalue
)
242 struct ft1000_info
*info
= netdev_priv(dev
);
246 if (info
->AsicID
== ELECTRABUZZ_ID
) {
247 size
= (u16
) (lvalue
>> 16);
249 ft1000_write_reg(dev
, FT1000_REG_DPRAM_ADDR
,
252 ft1000_write_reg(dev
, FT1000_REG_DPRAM_DATA
, size
);
254 size
= (u16
) (lvalue
);
256 ft1000_write_reg(dev
, FT1000_REG_DPRAM_ADDR
,
259 ft1000_write_reg(dev
, FT1000_REG_DPRAM_DATA
, size
);
261 tempx
= ntohl(lvalue
);
262 ft1000_write_dpram_mag_32(dev
, DWNLD_MAG_SIZE_LOC
, tempx
); /* Handshake */
267 static u16
hdr_checksum(struct pseudo_hdr
*pHdr
)
269 u16
*usPtr
= (u16
*)pHdr
;
272 chksum
= (((((usPtr
[0] ^ usPtr
[1]) ^ usPtr
[2]) ^ usPtr
[3]) ^
273 usPtr
[4]) ^ usPtr
[5]) ^ usPtr
[6];
278 int card_download(struct net_device
*dev
, const u8
*pFileStart
,
281 struct ft1000_info
*info
= netdev_priv(dev
);
282 int Status
= SUCCESS
;
285 struct pseudo_hdr
*pHdr
;
290 struct prov_record
*pprov_record
;
292 struct dsp_file_hdr
*pFileHdr5
;
293 struct dsp_image_info
*pDspImageInfoV6
= NULL
;
294 long requested_version
;
295 bool bGoodVersion
= false;
296 struct drv_msg
*pMailBoxData
;
304 long loader_code_address
= 0;
305 long loader_code_size
= 0;
306 long run_address
= 0;
309 unsigned long templong
;
310 unsigned long image_chksum
= 0;
312 file_version
= *(long *)pFileStart
;
313 if (file_version
!= 6) {
314 pr_err("unsupported firmware version %ld\n", file_version
);
318 uiState
= STATE_START_DWNLD
;
320 pFileHdr5
= (struct dsp_file_hdr
*)pFileStart
;
322 pUsFile
= (u16
*) ((long)pFileStart
+ pFileHdr5
->loader_offset
);
323 pUcFile
= (u8
*) ((long)pFileStart
+ pFileHdr5
->loader_offset
);
324 pBootEnd
= (u8
*) ((long)pFileStart
+ pFileHdr5
->loader_code_end
);
325 loader_code_address
= pFileHdr5
->loader_code_address
;
326 loader_code_size
= pFileHdr5
->loader_code_size
;
327 bGoodVersion
= false;
329 while ((Status
== SUCCESS
) && (uiState
!= STATE_DONE_FILE
)) {
332 case STATE_START_DWNLD
:
334 handshake
= get_handshake(dev
, HANDSHAKE_DSP_BL_READY
);
336 if (handshake
== HANDSHAKE_DSP_BL_READY
)
337 put_handshake(dev
, HANDSHAKE_DRIVER_READY
);
341 uiState
= STATE_BOOT_DWNLD
;
345 case STATE_BOOT_DWNLD
:
346 handshake
= get_handshake(dev
, HANDSHAKE_REQUEST
);
347 if (handshake
== HANDSHAKE_REQUEST
) {
349 * Get type associated with the request.
351 request
= get_request_type(dev
);
353 case REQUEST_RUN_ADDRESS
:
354 put_request_value(dev
,
355 loader_code_address
);
357 case REQUEST_CODE_LENGTH
:
358 put_request_value(dev
,
361 case REQUEST_DONE_BL
:
362 /* Reposition ptrs to beginning of code section */
363 pUsFile
= (u16
*) ((long)pBootEnd
);
364 pUcFile
= (u8
*) ((long)pBootEnd
);
365 uiState
= STATE_CODE_DWNLD
;
367 case REQUEST_CODE_SEGMENT
:
368 word_length
= get_request_value(dev
);
369 if (word_length
> MAX_LENGTH
) {
373 if ((word_length
* 2 + (long)pUcFile
) >
376 * Error, beyond boot code range.
381 /* Provide mutual exclusive access while reading ASIC registers. */
382 spin_lock_irqsave(&info
->dpram_lock
,
385 * Position ASIC DPRAM auto-increment pointer.
387 outw(DWNLD_MAG_PS_HDR_LOC
,
389 FT1000_REG_DPRAM_ADDR
);
390 if (word_length
& 0x01)
392 word_length
= word_length
/ 2;
394 for (; word_length
> 0; word_length
--) { /* In words */
395 templong
= *pUsFile
++;
401 FT1000_REG_MAG_DPDATAL
);
403 spin_unlock_irqrestore(&info
->
411 put_handshake(dev
, HANDSHAKE_RESPONSE
);
418 case STATE_CODE_DWNLD
:
419 handshake
= get_handshake(dev
, HANDSHAKE_REQUEST
);
420 if (handshake
== HANDSHAKE_REQUEST
) {
422 * Get type associated with the request.
424 request
= get_request_type(dev
);
426 case REQUEST_FILE_CHECKSUM
:
428 "ft1000_dnld: REQUEST_FOR_CHECKSUM\n");
429 put_request_value(dev
, image_chksum
);
431 case REQUEST_RUN_ADDRESS
:
433 put_request_value(dev
,
440 case REQUEST_CODE_LENGTH
:
442 put_request_value(dev
,
449 case REQUEST_DONE_CL
:
450 /* Reposition ptrs to beginning of provisioning section */
451 pUsFile
= (u16
*) ((long)pFileStart
+ pFileHdr5
->commands_offset
);
452 pUcFile
= (u8
*) ((long)pFileStart
+ pFileHdr5
->commands_offset
);
453 uiState
= STATE_DONE_DWNLD
;
455 case REQUEST_CODE_SEGMENT
:
460 word_length
= get_request_value(dev
);
461 if (word_length
> MAX_LENGTH
) {
465 if ((word_length
* 2 + (long)pUcFile
) >
468 * Error, beyond boot code range.
474 * Position ASIC DPRAM auto-increment pointer.
476 outw(DWNLD_MAG_PS_HDR_LOC
,
478 FT1000_REG_DPRAM_ADDR
);
479 if (word_length
& 0x01)
481 word_length
= word_length
/ 2;
483 for (; word_length
> 0; word_length
--) { /* In words */
484 templong
= *pUsFile
++;
490 FT1000_REG_MAG_DPDATAL
);
494 case REQUEST_MAILBOX_DATA
:
495 /* Convert length from byte count to word count. Make sure we round up. */
497 (long)(info
->DSPInfoBlklen
+ 1) / 2;
498 put_request_value(dev
, word_length
);
500 (struct drv_msg
*)&info
->DSPInfoBlk
[0];
502 (u16
*)&pMailBoxData
->data
[0];
503 /* Provide mutual exclusive access while reading ASIC registers. */
504 spin_lock_irqsave(&info
->dpram_lock
,
506 if (file_version
== 5) {
508 * Position ASIC DPRAM auto-increment pointer.
510 ft1000_write_reg(dev
,
511 FT1000_REG_DPRAM_ADDR
,
514 for (; word_length
> 0; word_length
--) { /* In words */
515 temp
= ntohs(*pUsData
);
516 ft1000_write_reg(dev
,
517 FT1000_REG_DPRAM_DATA
,
523 * Position ASIC DPRAM auto-increment pointer.
525 outw(DWNLD_MAG_PS_HDR_LOC
,
527 FT1000_REG_DPRAM_ADDR
);
528 if (word_length
& 0x01)
531 word_length
= word_length
/ 2;
533 for (; word_length
> 0; word_length
--) { /* In words */
534 templong
= *pUsData
++;
539 FT1000_REG_MAG_DPDATAL
);
542 spin_unlock_irqrestore(&info
->
547 case REQUEST_VERSION_INFO
:
549 pFileHdr5
->version_data_size
;
550 put_request_value(dev
, word_length
);
552 (u16
*) ((long)pFileStart
+
554 version_data_offset
);
555 /* Provide mutual exclusive access while reading ASIC registers. */
556 spin_lock_irqsave(&info
->dpram_lock
,
559 * Position ASIC DPRAM auto-increment pointer.
561 outw(DWNLD_MAG_PS_HDR_LOC
,
563 FT1000_REG_DPRAM_ADDR
);
564 if (word_length
& 0x01)
566 word_length
= word_length
/ 2;
568 for (; word_length
> 0; word_length
--) { /* In words */
577 FT1000_REG_MAG_DPDATAL
);
579 spin_unlock_irqrestore(&info
->
584 case REQUEST_CODE_BY_VERSION
:
585 bGoodVersion
= false;
587 get_request_value(dev
);
589 (struct dsp_image_info
*) ((long)
593 (struct dsp_file_hdr
));
596 pFileHdr5
->nDspImages
;
643 "ft1000_dnld: image_chksum = 0x%8x\n",
653 * Error, beyond boot code range.
664 put_handshake(dev
, HANDSHAKE_RESPONSE
);
671 case STATE_DONE_DWNLD
:
672 if (((unsigned long)(pUcFile
) - (unsigned long) pFileStart
) >=
673 (unsigned long)FileLength
) {
674 uiState
= STATE_DONE_FILE
;
678 pHdr
= (struct pseudo_hdr
*)pUsFile
;
680 if (pHdr
->portdest
== 0x80 /* DspOAM */
681 && (pHdr
->portsrc
== 0x00 /* Driver */
682 || pHdr
->portsrc
== 0x10 /* FMM */)) {
683 uiState
= STATE_SECTION_PROV
;
686 "Download error: Bad Port IDs in Pseudo Record\n");
687 netdev_dbg(dev
, "\t Port Source = 0x%2.2x\n",
689 netdev_dbg(dev
, "\t Port Destination = 0x%2.2x\n",
696 case STATE_SECTION_PROV
:
698 pHdr
= (struct pseudo_hdr
*)pUcFile
;
700 if (pHdr
->checksum
== hdr_checksum(pHdr
)) {
701 if (pHdr
->portdest
!= 0x80 /* Dsp OAM */) {
702 uiState
= STATE_DONE_PROV
;
705 usHdrLength
= ntohs(pHdr
->length
); /* Byte length for PROV records */
707 /* Get buffer for provisioning data */
709 kmalloc(usHdrLength
+ sizeof(struct pseudo_hdr
),
712 memcpy(pbuffer
, pUcFile
,
714 sizeof(struct pseudo_hdr
)));
715 /* link provisioning data */
717 kmalloc(sizeof(struct prov_record
),
720 pprov_record
->pprov_data
=
722 list_add_tail(&pprov_record
->
725 /* Move to next entry if available */
727 (u8
*)((unsigned long) pUcFile
+
728 (unsigned long) ((usHdrLength
+ 1) & 0xFFFFFFFE) + sizeof(struct pseudo_hdr
));
729 if ((unsigned long) (pUcFile
) -
730 (unsigned long) (pFileStart
) >=
731 (unsigned long)FileLength
) {
743 /* Checksum did not compute */
749 case STATE_DONE_PROV
:
750 uiState
= STATE_DONE_FILE
;