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>
34 #include <asm/uaccess.h>
35 #include <linux/vmalloc.h>
37 #include "ft1000_dev.h"
42 #define DEBUG(n, args...) printk(KERN_DEBUG args);
44 #define DEBUG(n, args...)
47 #define MAX_DSP_WAIT_LOOPS 100
48 #define DSP_WAIT_SLEEP_TIME 1 /* 1 millisecond */
50 #define MAX_LENGTH 0x7f0
52 #define DWNLD_MAG_HANDSHAKE_LOC 0x00
53 #define DWNLD_MAG_TYPE_LOC 0x01
54 #define DWNLD_MAG_SIZE_LOC 0x02
55 #define DWNLD_MAG_PS_HDR_LOC 0x03
57 #define DWNLD_HANDSHAKE_LOC 0x02
58 #define DWNLD_TYPE_LOC 0x04
59 #define DWNLD_SIZE_MSW_LOC 0x06
60 #define DWNLD_SIZE_LSW_LOC 0x08
61 #define DWNLD_PS_HDR_LOC 0x0A
63 #define HANDSHAKE_TIMEOUT_VALUE 0xF1F1
64 #define HANDSHAKE_RESET_VALUE 0xFEFE /* When DSP requests startover */
65 #define HANDSHAKE_DSP_BL_READY 0xFEFE /* At start DSP writes this when bootloader ready */
66 #define HANDSHAKE_DRIVER_READY 0xFFFF /* Driver writes after receiving 0xFEFE */
67 #define HANDSHAKE_SEND_DATA 0x0000 /* DSP writes this when ready for more data */
69 #define HANDSHAKE_REQUEST 0x0001 /* Request from DSP */
70 #define HANDSHAKE_RESPONSE 0x0000 /* Satisfied DSP request */
72 #define REQUEST_CODE_LENGTH 0x0000
73 #define REQUEST_RUN_ADDRESS 0x0001
74 #define REQUEST_CODE_SEGMENT 0x0002 /* In WORD count */
75 #define REQUEST_DONE_BL 0x0003
76 #define REQUEST_DONE_CL 0x0004
77 #define REQUEST_VERSION_INFO 0x0005
78 #define REQUEST_CODE_BY_VERSION 0x0006
79 #define REQUEST_MAILBOX_DATA 0x0007
80 #define REQUEST_FILE_CHECKSUM 0x0008
82 #define STATE_START_DWNLD 0x01
83 #define STATE_BOOT_DWNLD 0x02
84 #define STATE_CODE_DWNLD 0x03
85 #define STATE_DONE_DWNLD 0x04
86 #define STATE_SECTION_PROV 0x05
87 #define STATE_DONE_PROV 0x06
88 #define STATE_DONE_FILE 0x07
90 USHORT
get_handshake(struct net_device
*dev
, USHORT expected_value
);
91 void put_handshake(struct net_device
*dev
, USHORT handshake_value
);
92 USHORT
get_request_type(struct net_device
*dev
);
93 long get_request_value(struct net_device
*dev
);
94 void put_request_value(struct net_device
*dev
, long lvalue
);
95 USHORT
hdr_checksum(PPSEUDO_HDR pHdr
);
97 typedef struct _DSP_FILE_HDR
{
100 u32 loader_code_address
;
101 u32 loader_code_size
;
103 u32 dsp_code_address
;
107 } __attribute__ ((packed
)) DSP_FILE_HDR
, *PDSP_FILE_HDR
;
109 typedef struct _DSP_FILE_HDR_5
{
110 u32 version_id
; // Version ID of this image format.
111 u32 package_id
; // Package ID of code release.
112 u32 build_date
; // Date/time stamp when file was built.
113 u32 commands_offset
; // Offset to attached commands in Pseudo Hdr format.
114 u32 loader_offset
; // Offset to bootloader code.
115 u32 loader_code_address
; // Start address of bootloader.
116 u32 loader_code_end
; // Where bootloader code ends.
117 u32 loader_code_size
;
118 u32 version_data_offset
; // Offset were scrambled version data begins.
119 u32 version_data_size
; // Size, in words, of scrambled version data.
120 u32 nDspImages
; // Number of DSP images in file.
121 } __attribute__ ((packed
)) DSP_FILE_HDR_5
, *PDSP_FILE_HDR_5
;
123 typedef struct _DSP_IMAGE_INFO
{
124 u32 coff_date
; // Date/time when DSP Coff image was built.
125 u32 begin_offset
; // Offset in file where image begins.
126 u32 end_offset
; // Offset in file where image begins.
127 u32 run_address
; // On chip Start address of DSP code.
128 u32 image_size
; // Size of image.
129 u32 version
; // Embedded version # of DSP code.
130 } __attribute__ ((packed
)) DSP_IMAGE_INFO
, *PDSP_IMAGE_INFO
;
132 typedef struct _DSP_IMAGE_INFO_V6
{
133 u32 coff_date
; // Date/time when DSP Coff image was built.
134 u32 begin_offset
; // Offset in file where image begins.
135 u32 end_offset
; // Offset in file where image begins.
136 u32 run_address
; // On chip Start address of DSP code.
137 u32 image_size
; // Size of image.
138 u32 version
; // Embedded version # of DSP code.
139 unsigned short checksum
; // Dsp File checksum
141 } __attribute__ ((packed
)) DSP_IMAGE_INFO_V6
, *PDSP_IMAGE_INFO_V6
;
143 void card_bootload(struct net_device
*dev
)
145 FT1000_INFO
*info
= (PFT1000_INFO
) netdev_priv(dev
);
152 DEBUG(0, "card_bootload is called\n");
154 pdata
= (PULONG
) bootimage
;
155 size
= sizeof(bootimage
);
157 // check for odd word
161 // Provide mutual exclusive access while reading ASIC registers.
162 spin_lock_irqsave(&info
->dpram_lock
, flags
);
164 // need to set i/o base address initially and hardware will autoincrement
165 ft1000_write_reg(dev
, FT1000_REG_DPRAM_ADDR
, FT1000_DPRAM_BASE
);
167 for (i
= 0; i
< (size
>> 2); i
++) {
169 outl(templong
, dev
->base_addr
+ FT1000_REG_MAG_DPDATA
);
172 spin_unlock_irqrestore(&info
->dpram_lock
, flags
);
175 USHORT
get_handshake(struct net_device
*dev
, USHORT expected_value
)
177 FT1000_INFO
*info
= (PFT1000_INFO
) netdev_priv(dev
);
183 while (loopcnt
< MAX_DSP_WAIT_LOOPS
) {
184 if (info
->AsicID
== ELECTRABUZZ_ID
) {
185 ft1000_write_reg(dev
, FT1000_REG_DPRAM_ADDR
,
186 DWNLD_HANDSHAKE_LOC
);
188 handshake
= ft1000_read_reg(dev
, FT1000_REG_DPRAM_DATA
);
191 ntohl(ft1000_read_dpram_mag_32
192 (dev
, DWNLD_MAG_HANDSHAKE_LOC
));
193 handshake
= (USHORT
) tempx
;
196 if ((handshake
== expected_value
)
197 || (handshake
== HANDSHAKE_RESET_VALUE
)) {
201 mdelay(DSP_WAIT_SLEEP_TIME
);
206 return HANDSHAKE_TIMEOUT_VALUE
;
210 void put_handshake(struct net_device
*dev
, USHORT handshake_value
)
212 FT1000_INFO
*info
= (PFT1000_INFO
) netdev_priv(dev
);
215 if (info
->AsicID
== ELECTRABUZZ_ID
) {
216 ft1000_write_reg(dev
, FT1000_REG_DPRAM_ADDR
,
217 DWNLD_HANDSHAKE_LOC
);
218 ft1000_write_reg(dev
, FT1000_REG_DPRAM_DATA
, handshake_value
); /* Handshake */
220 tempx
= (ULONG
) handshake_value
;
221 tempx
= ntohl(tempx
);
222 ft1000_write_dpram_mag_32(dev
, DWNLD_MAG_HANDSHAKE_LOC
, tempx
); /* Handshake */
226 USHORT
get_request_type(struct net_device
*dev
)
228 FT1000_INFO
*info
= (PFT1000_INFO
) netdev_priv(dev
);
232 if (info
->AsicID
== ELECTRABUZZ_ID
) {
233 ft1000_write_reg(dev
, FT1000_REG_DPRAM_ADDR
, DWNLD_TYPE_LOC
);
234 request_type
= ft1000_read_reg(dev
, FT1000_REG_DPRAM_DATA
);
236 tempx
= ft1000_read_dpram_mag_32(dev
, DWNLD_MAG_TYPE_LOC
);
237 tempx
= ntohl(tempx
);
238 request_type
= (USHORT
) tempx
;
245 long get_request_value(struct net_device
*dev
)
247 FT1000_INFO
*info
= (PFT1000_INFO
) netdev_priv(dev
);
251 if (info
->AsicID
== ELECTRABUZZ_ID
) {
252 ft1000_write_reg(dev
, FT1000_REG_DPRAM_ADDR
,
255 w_val
= ft1000_read_reg(dev
, FT1000_REG_DPRAM_DATA
);
257 value
= (long)(w_val
<< 16);
259 ft1000_write_reg(dev
, FT1000_REG_DPRAM_ADDR
,
262 w_val
= ft1000_read_reg(dev
, FT1000_REG_DPRAM_DATA
);
264 value
= (long)(value
| w_val
);
266 value
= ft1000_read_dpram_mag_32(dev
, DWNLD_MAG_SIZE_LOC
);
267 value
= ntohl(value
);
274 void put_request_value(struct net_device
*dev
, long lvalue
)
276 FT1000_INFO
*info
= (PFT1000_INFO
) netdev_priv(dev
);
280 if (info
->AsicID
== ELECTRABUZZ_ID
) {
281 size
= (USHORT
) (lvalue
>> 16);
283 ft1000_write_reg(dev
, FT1000_REG_DPRAM_ADDR
,
286 ft1000_write_reg(dev
, FT1000_REG_DPRAM_DATA
, size
);
288 size
= (USHORT
) (lvalue
);
290 ft1000_write_reg(dev
, FT1000_REG_DPRAM_ADDR
,
293 ft1000_write_reg(dev
, FT1000_REG_DPRAM_DATA
, size
);
295 tempx
= ntohl(lvalue
);
296 ft1000_write_dpram_mag_32(dev
, DWNLD_MAG_SIZE_LOC
, tempx
); /* Handshake */
301 USHORT
hdr_checksum(PPSEUDO_HDR pHdr
)
303 USHORT
*usPtr
= (USHORT
*) pHdr
;
306 chksum
= ((((((usPtr
[0] ^ usPtr
[1]) ^ usPtr
[2]) ^ usPtr
[3]) ^
307 usPtr
[4]) ^ usPtr
[5]) ^ usPtr
[6]);
312 int card_download(struct net_device
*dev
, const u8
*pFileStart
, UINT FileLength
)
314 FT1000_INFO
*info
= (PFT1000_INFO
) netdev_priv(dev
);
315 int Status
= SUCCESS
;
316 USHORT DspWordCnt
= 0;
321 PDSP_FILE_HDR pFileHdr
;
325 PPROV_RECORD pprov_record
;
327 PDSP_FILE_HDR_5 pFileHdr5
;
328 PDSP_IMAGE_INFO pDspImageInfo
= NULL
;
329 PDSP_IMAGE_INFO_V6 pDspImageInfoV6
= NULL
;
330 long requested_version
;
331 BOOLEAN bGoodVersion
= 0;
332 PDRVMSG pMailBoxData
;
333 USHORT
*pUsData
= NULL
;
334 USHORT
*pUsFile
= NULL
;
335 UCHAR
*pUcFile
= NULL
;
336 UCHAR
*pBootEnd
= NULL
;
337 UCHAR
*pCodeEnd
= NULL
;
340 long loader_code_address
= 0;
341 long loader_code_size
= 0;
342 long run_address
= 0;
345 unsigned long templong
;
346 unsigned long image_chksum
= 0;
349 // Get version id of file, at first 4 bytes of file, for newer files.
351 file_version
= *(long *)pFileStart
;
353 uiState
= STATE_START_DWNLD
;
355 pFileHdr
= (PDSP_FILE_HDR
) pFileStart
;
356 pFileHdr5
= (PDSP_FILE_HDR_5
) pFileStart
;
358 switch (file_version
) {
362 (USHORT
*) ((long)pFileStart
+ pFileHdr5
->loader_offset
);
364 (UCHAR
*) ((long)pFileStart
+ pFileHdr5
->loader_offset
);
367 (UCHAR
*) ((long)pFileStart
+ pFileHdr5
->loader_code_end
);
369 loader_code_address
= pFileHdr5
->loader_code_address
;
370 loader_code_size
= pFileHdr5
->loader_code_size
;
371 bGoodVersion
= FALSE
;
379 while ((Status
== SUCCESS
) && (uiState
!= STATE_DONE_FILE
)) {
382 case STATE_START_DWNLD
:
384 handshake
= get_handshake(dev
, HANDSHAKE_DSP_BL_READY
);
386 if (handshake
== HANDSHAKE_DSP_BL_READY
) {
387 put_handshake(dev
, HANDSHAKE_DRIVER_READY
);
392 uiState
= STATE_BOOT_DWNLD
;
396 case STATE_BOOT_DWNLD
:
397 handshake
= get_handshake(dev
, HANDSHAKE_REQUEST
);
398 if (handshake
== HANDSHAKE_REQUEST
) {
400 * Get type associated with the request.
402 request
= get_request_type(dev
);
404 case REQUEST_RUN_ADDRESS
:
405 put_request_value(dev
,
406 loader_code_address
);
408 case REQUEST_CODE_LENGTH
:
409 put_request_value(dev
,
412 case REQUEST_DONE_BL
:
413 /* Reposition ptrs to beginning of code section */
414 pUsFile
= (USHORT
*) ((long)pBootEnd
);
415 pUcFile
= (UCHAR
*) ((long)pBootEnd
);
416 uiState
= STATE_CODE_DWNLD
;
418 case REQUEST_CODE_SEGMENT
:
419 word_length
= get_request_value(dev
);
420 if (word_length
> MAX_LENGTH
) {
424 if ((word_length
* 2 + (long)pUcFile
) >
427 * Error, beyond boot code range.
432 // Provide mutual exclusive access while reading ASIC registers.
433 spin_lock_irqsave(&info
->dpram_lock
,
435 if (file_version
== 5) {
437 * Position ASIC DPRAM auto-increment pointer.
439 ft1000_write_reg(dev
,
440 FT1000_REG_DPRAM_ADDR
,
443 for (; word_length
> 0; word_length
--) { /* In words */
445 //temp = RtlUshortByteSwap(temp);
446 ft1000_write_reg(dev
,
447 FT1000_REG_DPRAM_DATA
,
455 * Position ASIC DPRAM auto-increment pointer.
457 outw(DWNLD_MAG_PS_HDR_LOC
,
459 FT1000_REG_DPRAM_ADDR
);
460 if (word_length
& 0x01) {
463 word_length
= word_length
/ 2;
465 for (; word_length
> 0; word_length
--) { /* In words */
466 templong
= *pUsFile
++;
472 FT1000_REG_MAG_DPDATAL
);
475 spin_unlock_irqrestore(&info
->
483 put_handshake(dev
, HANDSHAKE_RESPONSE
);
490 case STATE_CODE_DWNLD
:
491 handshake
= get_handshake(dev
, HANDSHAKE_REQUEST
);
492 if (handshake
== HANDSHAKE_REQUEST
) {
494 * Get type associated with the request.
496 request
= get_request_type(dev
);
498 case REQUEST_FILE_CHECKSUM
:
500 "ft1000_dnld: REQUEST_FOR_CHECKSUM\n");
501 put_request_value(dev
, image_chksum
);
503 case REQUEST_RUN_ADDRESS
:
505 put_request_value(dev
,
512 case REQUEST_CODE_LENGTH
:
514 put_request_value(dev
,
521 case REQUEST_DONE_CL
:
522 /* Reposition ptrs to beginning of provisioning section */
523 switch (file_version
) {
527 (USHORT
*) ((long)pFileStart
532 (UCHAR
*) ((long)pFileStart
541 uiState
= STATE_DONE_DWNLD
;
543 case REQUEST_CODE_SEGMENT
:
548 word_length
= get_request_value(dev
);
549 if (word_length
> MAX_LENGTH
) {
553 if ((word_length
* 2 + (long)pUcFile
) >
556 * Error, beyond boot code range.
561 if (file_version
== 5) {
563 * Position ASIC DPRAM auto-increment pointer.
565 ft1000_write_reg(dev
,
566 FT1000_REG_DPRAM_ADDR
,
569 for (; word_length
> 0; word_length
--) { /* In words */
571 //temp = RtlUshortByteSwap(temp);
572 ft1000_write_reg(dev
,
573 FT1000_REG_DPRAM_DATA
,
581 * Position ASIC DPRAM auto-increment pointer.
583 outw(DWNLD_MAG_PS_HDR_LOC
,
585 FT1000_REG_DPRAM_ADDR
);
586 if (word_length
& 0x01) {
589 word_length
= word_length
/ 2;
591 for (; word_length
> 0; word_length
--) { /* In words */
592 templong
= *pUsFile
++;
598 FT1000_REG_MAG_DPDATAL
);
603 case REQUEST_MAILBOX_DATA
:
604 // Convert length from byte count to word count. Make sure we round up.
606 (long)(info
->DSPInfoBlklen
+ 1) / 2;
607 put_request_value(dev
, word_length
);
609 (PDRVMSG
) & info
->DSPInfoBlk
[0];
611 (USHORT
*) & pMailBoxData
->data
[0];
612 // Provide mutual exclusive access while reading ASIC registers.
613 spin_lock_irqsave(&info
->dpram_lock
,
615 if (file_version
== 5) {
617 * Position ASIC DPRAM auto-increment pointer.
619 ft1000_write_reg(dev
,
620 FT1000_REG_DPRAM_ADDR
,
623 for (; word_length
> 0; word_length
--) { /* In words */
624 temp
= ntohs(*pUsData
);
625 ft1000_write_reg(dev
,
626 FT1000_REG_DPRAM_DATA
,
632 * Position ASIC DPRAM auto-increment pointer.
634 outw(DWNLD_MAG_PS_HDR_LOC
,
636 FT1000_REG_DPRAM_ADDR
);
637 if (word_length
& 0x01) {
640 word_length
= word_length
/ 2;
642 for (; word_length
> 0; word_length
--) { /* In words */
643 templong
= *pUsData
++;
648 FT1000_REG_MAG_DPDATAL
);
651 spin_unlock_irqrestore(&info
->
656 case REQUEST_VERSION_INFO
:
658 pFileHdr5
->version_data_size
;
659 put_request_value(dev
, word_length
);
661 (USHORT
*) ((long)pFileStart
+
663 version_data_offset
);
664 // Provide mutual exclusive access while reading ASIC registers.
665 spin_lock_irqsave(&info
->dpram_lock
,
667 if (file_version
== 5) {
669 * Position ASIC DPRAM auto-increment pointer.
671 ft1000_write_reg(dev
,
672 FT1000_REG_DPRAM_ADDR
,
675 for (; word_length
> 0; word_length
--) { /* In words */
676 ft1000_write_reg(dev
,
677 FT1000_REG_DPRAM_DATA
,
685 * Position ASIC DPRAM auto-increment pointer.
687 outw(DWNLD_MAG_PS_HDR_LOC
,
689 FT1000_REG_DPRAM_ADDR
);
690 if (word_length
& 0x01) {
693 word_length
= word_length
/ 2;
695 for (; word_length
> 0; word_length
--) { /* In words */
704 FT1000_REG_MAG_DPDATAL
);
707 spin_unlock_irqrestore(&info
->
712 case REQUEST_CODE_BY_VERSION
:
713 bGoodVersion
= FALSE
;
715 get_request_value(dev
);
716 if (file_version
== 5) {
718 (PDSP_IMAGE_INFO
) ((long)
725 pFileHdr5
->nDspImages
;
765 (PDSP_IMAGE_INFO_V6
) ((long)
772 pFileHdr5
->nDspImages
;
819 "ft1000_dnld: image_chksum = 0x%8x\n",
830 * Error, beyond boot code range.
841 put_handshake(dev
, HANDSHAKE_RESPONSE
);
848 case STATE_DONE_DWNLD
:
849 if (((unsigned long) (pUcFile
) - (unsigned long) pFileStart
) >=
850 (unsigned long) FileLength
) {
851 uiState
= STATE_DONE_FILE
;
855 pHdr
= (PPSEUDO_HDR
) pUsFile
;
857 if (pHdr
->portdest
== 0x80 /* DspOAM */
858 && (pHdr
->portsrc
== 0x00 /* Driver */
859 || pHdr
->portsrc
== 0x10 /* FMM */ )) {
860 uiState
= STATE_SECTION_PROV
;
863 "FT1000:download:Download error: Bad Port IDs in Pseudo Record\n");
864 DEBUG(1, "\t Port Source = 0x%2.2x\n",
866 DEBUG(1, "\t Port Destination = 0x%2.2x\n",
873 case STATE_SECTION_PROV
:
875 pHdr
= (PPSEUDO_HDR
) pUcFile
;
877 if (pHdr
->checksum
== hdr_checksum(pHdr
)) {
878 if (pHdr
->portdest
!= 0x80 /* Dsp OAM */ ) {
879 uiState
= STATE_DONE_PROV
;
882 usHdrLength
= ntohs(pHdr
->length
); /* Byte length for PROV records */
884 // Get buffer for provisioning data
886 kmalloc((usHdrLength
+ sizeof(PSEUDO_HDR
)),
889 memcpy(pbuffer
, (void *)pUcFile
,
890 (UINT
) (usHdrLength
+
891 sizeof(PSEUDO_HDR
)));
892 // link provisioning data
894 kmalloc(sizeof(PROV_RECORD
),
897 pprov_record
->pprov_data
=
899 list_add_tail(&pprov_record
->
902 // Move to next entry if available
904 (UCHAR
*) ((unsigned long) pUcFile
+
905 (unsigned long) ((usHdrLength
+ 1) & 0xFFFFFFFE) + sizeof(PSEUDO_HDR
));
906 if ((unsigned long) (pUcFile
) -
907 (unsigned long) (pFileStart
) >=
908 (unsigned long) FileLength
) {
920 /* Checksum did not compute */
926 case STATE_DONE_PROV
:
927 uiState
= STATE_DONE_FILE
;