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;