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 __KERNEL_SYSCALLS__
25 #include <linux/module.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>
33 #include <linux/slab.h>
35 #include <asm/uaccess.h>
36 #include <linux/vmalloc.h>
38 #include "ft1000_dev.h"
43 #define DEBUG(n, args...) printk(KERN_DEBUG args);
45 #define DEBUG(n, args...)
48 #define MAX_DSP_WAIT_LOOPS 100
49 #define DSP_WAIT_SLEEP_TIME 1 /* 1 millisecond */
51 #define MAX_LENGTH 0x7f0
53 #define DWNLD_MAG_HANDSHAKE_LOC 0x00
54 #define DWNLD_MAG_TYPE_LOC 0x01
55 #define DWNLD_MAG_SIZE_LOC 0x02
56 #define DWNLD_MAG_PS_HDR_LOC 0x03
58 #define DWNLD_HANDSHAKE_LOC 0x02
59 #define DWNLD_TYPE_LOC 0x04
60 #define DWNLD_SIZE_MSW_LOC 0x06
61 #define DWNLD_SIZE_LSW_LOC 0x08
62 #define DWNLD_PS_HDR_LOC 0x0A
64 #define HANDSHAKE_TIMEOUT_VALUE 0xF1F1
65 #define HANDSHAKE_RESET_VALUE 0xFEFE /* When DSP requests startover */
66 #define HANDSHAKE_DSP_BL_READY 0xFEFE /* At start DSP writes this when bootloader ready */
67 #define HANDSHAKE_DRIVER_READY 0xFFFF /* Driver writes after receiving 0xFEFE */
68 #define HANDSHAKE_SEND_DATA 0x0000 /* DSP writes this when ready for more data */
70 #define HANDSHAKE_REQUEST 0x0001 /* Request from DSP */
71 #define HANDSHAKE_RESPONSE 0x0000 /* Satisfied DSP request */
73 #define REQUEST_CODE_LENGTH 0x0000
74 #define REQUEST_RUN_ADDRESS 0x0001
75 #define REQUEST_CODE_SEGMENT 0x0002 /* In WORD count */
76 #define REQUEST_DONE_BL 0x0003
77 #define REQUEST_DONE_CL 0x0004
78 #define REQUEST_VERSION_INFO 0x0005
79 #define REQUEST_CODE_BY_VERSION 0x0006
80 #define REQUEST_MAILBOX_DATA 0x0007
81 #define REQUEST_FILE_CHECKSUM 0x0008
83 #define STATE_START_DWNLD 0x01
84 #define STATE_BOOT_DWNLD 0x02
85 #define STATE_CODE_DWNLD 0x03
86 #define STATE_DONE_DWNLD 0x04
87 #define STATE_SECTION_PROV 0x05
88 #define STATE_DONE_PROV 0x06
89 #define STATE_DONE_FILE 0x07
91 USHORT
get_handshake(struct net_device
*dev
, USHORT expected_value
);
92 void put_handshake(struct net_device
*dev
, USHORT handshake_value
);
93 USHORT
get_request_type(struct net_device
*dev
);
94 long get_request_value(struct net_device
*dev
);
95 void put_request_value(struct net_device
*dev
, long lvalue
);
96 USHORT
hdr_checksum(PPSEUDO_HDR pHdr
);
98 typedef struct _DSP_FILE_HDR
{
101 long loader_code_address
;
102 long loader_code_size
;
103 long loader_code_end
;
104 long dsp_code_address
;
108 } __attribute__ ((packed
)) DSP_FILE_HDR
, *PDSP_FILE_HDR
;
110 typedef struct _DSP_FILE_HDR_5
{
111 long version_id
; // Version ID of this image format.
112 long package_id
; // Package ID of code release.
113 long build_date
; // Date/time stamp when file was built.
114 long commands_offset
; // Offset to attached commands in Pseudo Hdr format.
115 long loader_offset
; // Offset to bootloader code.
116 long loader_code_address
; // Start address of bootloader.
117 long loader_code_end
; // Where bootloader code ends.
118 long loader_code_size
;
119 long version_data_offset
; // Offset were scrambled version data begins.
120 long version_data_size
; // Size, in words, of scrambled version data.
121 long nDspImages
; // Number of DSP images in file.
122 } __attribute__ ((packed
)) DSP_FILE_HDR_5
, *PDSP_FILE_HDR_5
;
124 typedef struct _DSP_IMAGE_INFO
{
125 long coff_date
; // Date/time when DSP Coff image was built.
126 long begin_offset
; // Offset in file where image begins.
127 long end_offset
; // Offset in file where image begins.
128 long run_address
; // On chip Start address of DSP code.
129 long image_size
; // Size of image.
130 long version
; // Embedded version # of DSP code.
131 } __attribute__ ((packed
)) DSP_IMAGE_INFO
, *PDSP_IMAGE_INFO
;
133 typedef struct _DSP_IMAGE_INFO_V6
{
134 long coff_date
; // Date/time when DSP Coff image was built.
135 long begin_offset
; // Offset in file where image begins.
136 long end_offset
; // Offset in file where image begins.
137 long run_address
; // On chip Start address of DSP code.
138 long image_size
; // Size of image.
139 long version
; // Embedded version # of DSP code.
140 unsigned short checksum
; // Dsp File checksum
142 } __attribute__ ((packed
)) DSP_IMAGE_INFO_V6
, *PDSP_IMAGE_INFO_V6
;
144 void card_bootload(struct net_device
*dev
)
146 FT1000_INFO
*info
= (PFT1000_INFO
) netdev_priv(dev
);
153 DEBUG(0, "card_bootload is called\n");
155 pdata
= (PULONG
) bootimage
;
156 size
= sizeof(bootimage
);
158 // check for odd word
162 // Provide mutual exclusive access while reading ASIC registers.
163 spin_lock_irqsave(&info
->dpram_lock
, flags
);
165 // need to set i/o base address initially and hardware will autoincrement
166 ft1000_write_reg(dev
, FT1000_REG_DPRAM_ADDR
, FT1000_DPRAM_BASE
);
168 for (i
= 0; i
< (size
>> 2); i
++) {
170 outl(templong
, dev
->base_addr
+ FT1000_REG_MAG_DPDATA
);
173 spin_unlock_irqrestore(&info
->dpram_lock
, flags
);
176 USHORT
get_handshake(struct net_device
*dev
, USHORT expected_value
)
178 FT1000_INFO
*info
= (PFT1000_INFO
) netdev_priv(dev
);
184 while (loopcnt
< MAX_DSP_WAIT_LOOPS
) {
185 if (info
->AsicID
== ELECTRABUZZ_ID
) {
186 ft1000_write_reg(dev
, FT1000_REG_DPRAM_ADDR
,
187 DWNLD_HANDSHAKE_LOC
);
189 handshake
= ft1000_read_reg(dev
, FT1000_REG_DPRAM_DATA
);
192 ntohl(ft1000_read_dpram_mag_32
193 (dev
, DWNLD_MAG_HANDSHAKE_LOC
));
194 handshake
= (USHORT
) tempx
;
197 if ((handshake
== expected_value
)
198 || (handshake
== HANDSHAKE_RESET_VALUE
)) {
202 mdelay(DSP_WAIT_SLEEP_TIME
);
207 return HANDSHAKE_TIMEOUT_VALUE
;
211 void put_handshake(struct net_device
*dev
, USHORT handshake_value
)
213 FT1000_INFO
*info
= (PFT1000_INFO
) netdev_priv(dev
);
216 if (info
->AsicID
== ELECTRABUZZ_ID
) {
217 ft1000_write_reg(dev
, FT1000_REG_DPRAM_ADDR
,
218 DWNLD_HANDSHAKE_LOC
);
219 ft1000_write_reg(dev
, FT1000_REG_DPRAM_DATA
, handshake_value
); /* Handshake */
221 tempx
= (ULONG
) handshake_value
;
222 tempx
= ntohl(tempx
);
223 ft1000_write_dpram_mag_32(dev
, DWNLD_MAG_HANDSHAKE_LOC
, tempx
); /* Handshake */
227 USHORT
get_request_type(struct net_device
*dev
)
229 FT1000_INFO
*info
= (PFT1000_INFO
) netdev_priv(dev
);
233 if (info
->AsicID
== ELECTRABUZZ_ID
) {
234 ft1000_write_reg(dev
, FT1000_REG_DPRAM_ADDR
, DWNLD_TYPE_LOC
);
235 request_type
= ft1000_read_reg(dev
, FT1000_REG_DPRAM_DATA
);
237 tempx
= ft1000_read_dpram_mag_32(dev
, DWNLD_MAG_TYPE_LOC
);
238 tempx
= ntohl(tempx
);
239 request_type
= (USHORT
) tempx
;
246 long get_request_value(struct net_device
*dev
)
248 FT1000_INFO
*info
= (PFT1000_INFO
) netdev_priv(dev
);
252 if (info
->AsicID
== ELECTRABUZZ_ID
) {
253 ft1000_write_reg(dev
, FT1000_REG_DPRAM_ADDR
,
256 w_val
= ft1000_read_reg(dev
, FT1000_REG_DPRAM_DATA
);
258 value
= (long)(w_val
<< 16);
260 ft1000_write_reg(dev
, FT1000_REG_DPRAM_ADDR
,
263 w_val
= ft1000_read_reg(dev
, FT1000_REG_DPRAM_DATA
);
265 value
= (long)(value
| w_val
);
267 value
= ft1000_read_dpram_mag_32(dev
, DWNLD_MAG_SIZE_LOC
);
268 value
= ntohl(value
);
275 void put_request_value(struct net_device
*dev
, long lvalue
)
277 FT1000_INFO
*info
= (PFT1000_INFO
) netdev_priv(dev
);
281 if (info
->AsicID
== ELECTRABUZZ_ID
) {
282 size
= (USHORT
) (lvalue
>> 16);
284 ft1000_write_reg(dev
, FT1000_REG_DPRAM_ADDR
,
287 ft1000_write_reg(dev
, FT1000_REG_DPRAM_DATA
, size
);
289 size
= (USHORT
) (lvalue
);
291 ft1000_write_reg(dev
, FT1000_REG_DPRAM_ADDR
,
294 ft1000_write_reg(dev
, FT1000_REG_DPRAM_DATA
, size
);
296 tempx
= ntohl(lvalue
);
297 ft1000_write_dpram_mag_32(dev
, DWNLD_MAG_SIZE_LOC
, tempx
); /* Handshake */
302 USHORT
hdr_checksum(PPSEUDO_HDR pHdr
)
304 USHORT
*usPtr
= (USHORT
*) pHdr
;
307 chksum
= ((((((usPtr
[0] ^ usPtr
[1]) ^ usPtr
[2]) ^ usPtr
[3]) ^
308 usPtr
[4]) ^ usPtr
[5]) ^ usPtr
[6]);
313 int card_download(struct net_device
*dev
, void *pFileStart
, UINT FileLength
)
315 FT1000_INFO
*info
= (PFT1000_INFO
) netdev_priv(dev
);
316 int Status
= SUCCESS
;
317 USHORT DspWordCnt
= 0;
322 PDSP_FILE_HDR pFileHdr
;
326 PPROV_RECORD pprov_record
;
328 PDSP_FILE_HDR_5 pFileHdr5
;
329 PDSP_IMAGE_INFO pDspImageInfo
= NULL
;
330 PDSP_IMAGE_INFO_V6 pDspImageInfoV6
= NULL
;
331 long requested_version
;
332 BOOLEAN bGoodVersion
= 0;
333 PDRVMSG pMailBoxData
;
334 USHORT
*pUsData
= NULL
;
335 USHORT
*pUsFile
= NULL
;
336 UCHAR
*pUcFile
= NULL
;
337 UCHAR
*pBootEnd
= NULL
;
338 UCHAR
*pCodeEnd
= NULL
;
341 long loader_code_address
= 0;
342 long loader_code_size
= 0;
343 long run_address
= 0;
346 unsigned long templong
;
347 unsigned long image_chksum
= 0;
350 // Get version id of file, at first 4 bytes of file, for newer files.
352 file_version
= *(long *)pFileStart
;
354 uiState
= STATE_START_DWNLD
;
356 pFileHdr
= (PDSP_FILE_HDR
) pFileStart
;
357 pFileHdr5
= (PDSP_FILE_HDR_5
) pFileStart
;
359 switch (file_version
) {
363 (USHORT
*) ((long)pFileStart
+ pFileHdr5
->loader_offset
);
365 (UCHAR
*) ((long)pFileStart
+ pFileHdr5
->loader_offset
);
368 (UCHAR
*) ((long)pFileStart
+ pFileHdr5
->loader_code_end
);
370 loader_code_address
= pFileHdr5
->loader_code_address
;
371 loader_code_size
= pFileHdr5
->loader_code_size
;
372 bGoodVersion
= FALSE
;
380 while ((Status
== SUCCESS
) && (uiState
!= STATE_DONE_FILE
)) {
383 case STATE_START_DWNLD
:
385 handshake
= get_handshake(dev
, HANDSHAKE_DSP_BL_READY
);
387 if (handshake
== HANDSHAKE_DSP_BL_READY
) {
388 put_handshake(dev
, HANDSHAKE_DRIVER_READY
);
393 uiState
= STATE_BOOT_DWNLD
;
397 case STATE_BOOT_DWNLD
:
398 handshake
= get_handshake(dev
, HANDSHAKE_REQUEST
);
399 if (handshake
== HANDSHAKE_REQUEST
) {
401 * Get type associated with the request.
403 request
= get_request_type(dev
);
405 case REQUEST_RUN_ADDRESS
:
406 put_request_value(dev
,
407 loader_code_address
);
409 case REQUEST_CODE_LENGTH
:
410 put_request_value(dev
,
413 case REQUEST_DONE_BL
:
414 /* Reposition ptrs to beginning of code section */
415 pUsFile
= (USHORT
*) ((long)pBootEnd
);
416 pUcFile
= (UCHAR
*) ((long)pBootEnd
);
417 uiState
= STATE_CODE_DWNLD
;
419 case REQUEST_CODE_SEGMENT
:
420 word_length
= get_request_value(dev
);
421 if (word_length
> MAX_LENGTH
) {
425 if ((word_length
* 2 + (long)pUcFile
) >
428 * Error, beyond boot code range.
433 // Provide mutual exclusive access while reading ASIC registers.
434 spin_lock_irqsave(&info
->dpram_lock
,
436 if (file_version
== 5) {
438 * Position ASIC DPRAM auto-increment pointer.
440 ft1000_write_reg(dev
,
441 FT1000_REG_DPRAM_ADDR
,
444 for (; word_length
> 0; word_length
--) { /* In words */
446 //temp = RtlUshortByteSwap(temp);
447 ft1000_write_reg(dev
,
448 FT1000_REG_DPRAM_DATA
,
456 * Position ASIC DPRAM auto-increment pointer.
458 outw(DWNLD_MAG_PS_HDR_LOC
,
460 FT1000_REG_DPRAM_ADDR
);
461 if (word_length
& 0x01) {
464 word_length
= word_length
/ 2;
466 for (; word_length
> 0; word_length
--) { /* In words */
467 templong
= *pUsFile
++;
473 FT1000_REG_MAG_DPDATAL
);
476 spin_unlock_irqrestore(&info
->
484 put_handshake(dev
, HANDSHAKE_RESPONSE
);
491 case STATE_CODE_DWNLD
:
492 handshake
= get_handshake(dev
, HANDSHAKE_REQUEST
);
493 if (handshake
== HANDSHAKE_REQUEST
) {
495 * Get type associated with the request.
497 request
= get_request_type(dev
);
499 case REQUEST_FILE_CHECKSUM
:
501 "ft1000_dnld: REQUEST_FOR_CHECKSUM\n");
502 put_request_value(dev
, image_chksum
);
504 case REQUEST_RUN_ADDRESS
:
506 put_request_value(dev
,
513 case REQUEST_CODE_LENGTH
:
515 put_request_value(dev
,
522 case REQUEST_DONE_CL
:
523 /* Reposition ptrs to beginning of provisioning section */
524 switch (file_version
) {
528 (USHORT
*) ((long)pFileStart
533 (UCHAR
*) ((long)pFileStart
542 uiState
= STATE_DONE_DWNLD
;
544 case REQUEST_CODE_SEGMENT
:
549 word_length
= get_request_value(dev
);
550 if (word_length
> MAX_LENGTH
) {
554 if ((word_length
* 2 + (long)pUcFile
) >
557 * Error, beyond boot code range.
562 if (file_version
== 5) {
564 * Position ASIC DPRAM auto-increment pointer.
566 ft1000_write_reg(dev
,
567 FT1000_REG_DPRAM_ADDR
,
570 for (; word_length
> 0; word_length
--) { /* In words */
572 //temp = RtlUshortByteSwap(temp);
573 ft1000_write_reg(dev
,
574 FT1000_REG_DPRAM_DATA
,
582 * Position ASIC DPRAM auto-increment pointer.
584 outw(DWNLD_MAG_PS_HDR_LOC
,
586 FT1000_REG_DPRAM_ADDR
);
587 if (word_length
& 0x01) {
590 word_length
= word_length
/ 2;
592 for (; word_length
> 0; word_length
--) { /* In words */
593 templong
= *pUsFile
++;
599 FT1000_REG_MAG_DPDATAL
);
604 case REQUEST_MAILBOX_DATA
:
605 // Convert length from byte count to word count. Make sure we round up.
607 (long)(info
->DSPInfoBlklen
+ 1) / 2;
608 put_request_value(dev
, word_length
);
610 (PDRVMSG
) & info
->DSPInfoBlk
[0];
612 (USHORT
*) & pMailBoxData
->data
[0];
613 // Provide mutual exclusive access while reading ASIC registers.
614 spin_lock_irqsave(&info
->dpram_lock
,
616 if (file_version
== 5) {
618 * Position ASIC DPRAM auto-increment pointer.
620 ft1000_write_reg(dev
,
621 FT1000_REG_DPRAM_ADDR
,
624 for (; word_length
> 0; word_length
--) { /* In words */
625 temp
= ntohs(*pUsData
);
626 ft1000_write_reg(dev
,
627 FT1000_REG_DPRAM_DATA
,
633 * Position ASIC DPRAM auto-increment pointer.
635 outw(DWNLD_MAG_PS_HDR_LOC
,
637 FT1000_REG_DPRAM_ADDR
);
638 if (word_length
& 0x01) {
641 word_length
= word_length
/ 2;
643 for (; word_length
> 0; word_length
--) { /* In words */
644 templong
= *pUsData
++;
649 FT1000_REG_MAG_DPDATAL
);
652 spin_unlock_irqrestore(&info
->
657 case REQUEST_VERSION_INFO
:
659 pFileHdr5
->version_data_size
;
660 put_request_value(dev
, word_length
);
662 (USHORT
*) ((long)pFileStart
+
664 version_data_offset
);
665 // Provide mutual exclusive access while reading ASIC registers.
666 spin_lock_irqsave(&info
->dpram_lock
,
668 if (file_version
== 5) {
670 * Position ASIC DPRAM auto-increment pointer.
672 ft1000_write_reg(dev
,
673 FT1000_REG_DPRAM_ADDR
,
676 for (; word_length
> 0; word_length
--) { /* In words */
677 ft1000_write_reg(dev
,
678 FT1000_REG_DPRAM_DATA
,
686 * Position ASIC DPRAM auto-increment pointer.
688 outw(DWNLD_MAG_PS_HDR_LOC
,
690 FT1000_REG_DPRAM_ADDR
);
691 if (word_length
& 0x01) {
694 word_length
= word_length
/ 2;
696 for (; word_length
> 0; word_length
--) { /* In words */
705 FT1000_REG_MAG_DPDATAL
);
708 spin_unlock_irqrestore(&info
->
713 case REQUEST_CODE_BY_VERSION
:
714 bGoodVersion
= FALSE
;
716 get_request_value(dev
);
717 if (file_version
== 5) {
719 (PDSP_IMAGE_INFO
) ((long)
726 pFileHdr5
->nDspImages
;
766 (PDSP_IMAGE_INFO_V6
) ((long)
773 pFileHdr5
->nDspImages
;
820 "ft1000_dnld: image_chksum = 0x%8x\n",
831 * Error, beyond boot code range.
842 put_handshake(dev
, HANDSHAKE_RESPONSE
);
849 case STATE_DONE_DWNLD
:
850 if (((UINT
) (pUcFile
) - (UINT
) pFileStart
) >=
852 uiState
= STATE_DONE_FILE
;
856 pHdr
= (PPSEUDO_HDR
) pUsFile
;
858 if (pHdr
->portdest
== 0x80 /* DspOAM */
859 && (pHdr
->portsrc
== 0x00 /* Driver */
860 || pHdr
->portsrc
== 0x10 /* FMM */ )) {
861 uiState
= STATE_SECTION_PROV
;
864 "FT1000:download:Download error: Bad Port IDs in Pseudo Record\n");
865 DEBUG(1, "\t Port Source = 0x%2.2x\n",
867 DEBUG(1, "\t Port Destination = 0x%2.2x\n",
874 case STATE_SECTION_PROV
:
876 pHdr
= (PPSEUDO_HDR
) pUcFile
;
878 if (pHdr
->checksum
== hdr_checksum(pHdr
)) {
879 if (pHdr
->portdest
!= 0x80 /* Dsp OAM */ ) {
880 uiState
= STATE_DONE_PROV
;
883 usHdrLength
= ntohs(pHdr
->length
); /* Byte length for PROV records */
885 // Get buffer for provisioning data
887 kmalloc((usHdrLength
+ sizeof(PSEUDO_HDR
)),
890 memcpy(pbuffer
, (void *)pUcFile
,
891 (UINT
) (usHdrLength
+
892 sizeof(PSEUDO_HDR
)));
893 // link provisioning data
895 kmalloc(sizeof(PROV_RECORD
),
898 pprov_record
->pprov_data
=
900 list_add_tail(&pprov_record
->
903 // Move to next entry if available
905 (UCHAR
*) ((UINT
) pUcFile
+
906 (UINT
) ((usHdrLength
+ 1) & 0xFFFFFFFE) + sizeof(PSEUDO_HDR
));
907 if ((UINT
) (pUcFile
) -
908 (UINT
) (pFileStart
) >=
921 /* Checksum did not compute */
927 case STATE_DONE_PROV
:
928 uiState
= STATE_DONE_FILE
;