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>
41 #define DEBUG(n, args...) printk(KERN_DEBUG args);
43 #define DEBUG(n, args...)
46 #define MAX_DSP_WAIT_LOOPS 100
47 #define DSP_WAIT_SLEEP_TIME 1 /* 1 millisecond */
49 #define MAX_LENGTH 0x7f0
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
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
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 */
68 #define HANDSHAKE_REQUEST 0x0001 /* Request from DSP */
69 #define HANDSHAKE_RESPONSE 0x0000 /* Satisfied DSP request */
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
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
89 USHORT get_handshake(struct net_device *dev, USHORT expected_value);
90 void put_handshake(struct net_device *dev, USHORT handshake_value);
91 USHORT get_request_type(struct net_device *dev);
92 long get_request_value(struct net_device *dev);
93 void put_request_value(struct net_device *dev, long lvalue);
94 USHORT hdr_checksum(struct pseudo_hdr *pHdr);
96 typedef struct _DSP_FILE_HDR {
99 u32 loader_code_address;
100 u32 loader_code_size;
102 u32 dsp_code_address;
106 } __attribute__ ((packed)) DSP_FILE_HDR, *PDSP_FILE_HDR;
108 typedef struct _DSP_FILE_HDR_5 {
109 u32 version_id; // Version ID of this image format.
110 u32 package_id; // Package ID of code release.
111 u32 build_date; // Date/time stamp when file was built.
112 u32 commands_offset; // Offset to attached commands in Pseudo Hdr format.
113 u32 loader_offset; // Offset to bootloader code.
114 u32 loader_code_address; // Start address of bootloader.
115 u32 loader_code_end; // Where bootloader code ends.
116 u32 loader_code_size;
117 u32 version_data_offset; // Offset were scrambled version data begins.
118 u32 version_data_size; // Size, in words, of scrambled version data.
119 u32 nDspImages; // Number of DSP images in file.
120 } __attribute__ ((packed)) DSP_FILE_HDR_5, *PDSP_FILE_HDR_5;
122 typedef struct _DSP_IMAGE_INFO {
123 u32 coff_date; // Date/time when DSP Coff image was built.
124 u32 begin_offset; // Offset in file where image begins.
125 u32 end_offset; // Offset in file where image begins.
126 u32 run_address; // On chip Start address of DSP code.
127 u32 image_size; // Size of image.
128 u32 version; // Embedded version # of DSP code.
129 } __attribute__ ((packed)) DSP_IMAGE_INFO, *PDSP_IMAGE_INFO;
131 typedef struct _DSP_IMAGE_INFO_V6 {
132 u32 coff_date; // Date/time when DSP Coff image was built.
133 u32 begin_offset; // Offset in file where image begins.
134 u32 end_offset; // Offset in file where image begins.
135 u32 run_address; // On chip Start address of DSP code.
136 u32 image_size; // Size of image.
137 u32 version; // Embedded version # of DSP code.
138 unsigned short checksum; // Dsp File checksum
140 } __attribute__ ((packed)) DSP_IMAGE_INFO_V6, *PDSP_IMAGE_INFO_V6;
142 void card_bootload(struct net_device *dev)
144 FT1000_INFO *info = (PFT1000_INFO) netdev_priv(dev);
151 DEBUG(0, "card_bootload is called\n");
153 pdata = (PULONG) bootimage;
154 size = sizeof(bootimage);
156 // check for odd word
160 // Provide mutual exclusive access while reading ASIC registers.
161 spin_lock_irqsave(&info->dpram_lock, flags);
163 // need to set i/o base address initially and hardware will autoincrement
164 ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, FT1000_DPRAM_BASE);
166 for (i = 0; i < (size >> 2); i++) {
168 outl(templong, dev->base_addr + FT1000_REG_MAG_DPDATA);
171 spin_unlock_irqrestore(&info->dpram_lock, flags);
174 USHORT get_handshake(struct net_device *dev, USHORT expected_value)
176 FT1000_INFO *info = (PFT1000_INFO) netdev_priv(dev);
182 while (loopcnt < MAX_DSP_WAIT_LOOPS) {
183 if (info->AsicID == ELECTRABUZZ_ID) {
184 ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
185 DWNLD_HANDSHAKE_LOC);
187 handshake = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
190 ntohl(ft1000_read_dpram_mag_32
191 (dev, DWNLD_MAG_HANDSHAKE_LOC));
192 handshake = (USHORT) tempx;
195 if ((handshake == expected_value)
196 || (handshake == HANDSHAKE_RESET_VALUE)) {
200 mdelay(DSP_WAIT_SLEEP_TIME);
205 return HANDSHAKE_TIMEOUT_VALUE;
209 void put_handshake(struct net_device *dev, USHORT handshake_value)
211 FT1000_INFO *info = (PFT1000_INFO) netdev_priv(dev);
214 if (info->AsicID == ELECTRABUZZ_ID) {
215 ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
216 DWNLD_HANDSHAKE_LOC);
217 ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, handshake_value); /* Handshake */
219 tempx = (ULONG) handshake_value;
220 tempx = ntohl(tempx);
221 ft1000_write_dpram_mag_32(dev, DWNLD_MAG_HANDSHAKE_LOC, tempx); /* Handshake */
225 USHORT get_request_type(struct net_device *dev)
227 FT1000_INFO *info = (PFT1000_INFO) netdev_priv(dev);
231 if (info->AsicID == ELECTRABUZZ_ID) {
232 ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, DWNLD_TYPE_LOC);
233 request_type = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
235 tempx = ft1000_read_dpram_mag_32(dev, DWNLD_MAG_TYPE_LOC);
236 tempx = ntohl(tempx);
237 request_type = (USHORT) tempx;
244 long get_request_value(struct net_device *dev)
246 FT1000_INFO *info = (PFT1000_INFO) netdev_priv(dev);
250 if (info->AsicID == ELECTRABUZZ_ID) {
251 ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
254 w_val = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
256 value = (long)(w_val << 16);
258 ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
261 w_val = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
263 value = (long)(value | w_val);
265 value = ft1000_read_dpram_mag_32(dev, DWNLD_MAG_SIZE_LOC);
266 value = ntohl(value);
273 void put_request_value(struct net_device *dev, long lvalue)
275 FT1000_INFO *info = (PFT1000_INFO) netdev_priv(dev);
279 if (info->AsicID == ELECTRABUZZ_ID) {
280 size = (USHORT) (lvalue >> 16);
282 ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
285 ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, size);
287 size = (USHORT) (lvalue);
289 ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
292 ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, size);
294 tempx = ntohl(lvalue);
295 ft1000_write_dpram_mag_32(dev, DWNLD_MAG_SIZE_LOC, tempx); /* Handshake */
300 USHORT hdr_checksum(struct pseudo_hdr *pHdr)
302 USHORT *usPtr = (USHORT *) pHdr;
305 chksum = ((((((usPtr[0] ^ usPtr[1]) ^ usPtr[2]) ^ usPtr[3]) ^
306 usPtr[4]) ^ usPtr[5]) ^ usPtr[6]);
311 int card_download(struct net_device *dev, const u8 *pFileStart, UINT FileLength)
313 FT1000_INFO *info = (PFT1000_INFO) netdev_priv(dev);
314 int Status = SUCCESS;
315 USHORT DspWordCnt = 0;
318 struct pseudo_hdr *pHdr;
320 PDSP_FILE_HDR pFileHdr;
324 PPROV_RECORD pprov_record;
326 PDSP_FILE_HDR_5 pFileHdr5;
327 PDSP_IMAGE_INFO pDspImageInfo = NULL;
328 PDSP_IMAGE_INFO_V6 pDspImageInfoV6 = NULL;
329 long requested_version;
330 BOOLEAN bGoodVersion = 0;
331 PDRVMSG pMailBoxData;
332 USHORT *pUsData = NULL;
333 USHORT *pUsFile = NULL;
334 UCHAR *pUcFile = NULL;
335 UCHAR *pBootEnd = NULL;
336 UCHAR *pCodeEnd = NULL;
339 long loader_code_address = 0;
340 long loader_code_size = 0;
341 long run_address = 0;
344 unsigned long templong;
345 unsigned long image_chksum = 0;
348 // Get version id of file, at first 4 bytes of file, for newer files.
350 file_version = *(long *)pFileStart;
352 uiState = STATE_START_DWNLD;
354 pFileHdr = (PDSP_FILE_HDR) pFileStart;
355 pFileHdr5 = (PDSP_FILE_HDR_5) pFileStart;
357 switch (file_version) {
361 (USHORT *) ((long)pFileStart + pFileHdr5->loader_offset);
363 (UCHAR *) ((long)pFileStart + pFileHdr5->loader_offset);
366 (UCHAR *) ((long)pFileStart + pFileHdr5->loader_code_end);
368 loader_code_address = pFileHdr5->loader_code_address;
369 loader_code_size = pFileHdr5->loader_code_size;
370 bGoodVersion = FALSE;
378 while ((Status == SUCCESS) && (uiState != STATE_DONE_FILE)) {
381 case STATE_START_DWNLD:
383 handshake = get_handshake(dev, HANDSHAKE_DSP_BL_READY);
385 if (handshake == HANDSHAKE_DSP_BL_READY) {
386 put_handshake(dev, HANDSHAKE_DRIVER_READY);
391 uiState = STATE_BOOT_DWNLD;
395 case STATE_BOOT_DWNLD:
396 handshake = get_handshake(dev, HANDSHAKE_REQUEST);
397 if (handshake == HANDSHAKE_REQUEST) {
399 * Get type associated with the request.
401 request = get_request_type(dev);
403 case REQUEST_RUN_ADDRESS:
404 put_request_value(dev,
405 loader_code_address);
407 case REQUEST_CODE_LENGTH:
408 put_request_value(dev,
411 case REQUEST_DONE_BL:
412 /* Reposition ptrs to beginning of code section */
413 pUsFile = (USHORT *) ((long)pBootEnd);
414 pUcFile = (UCHAR *) ((long)pBootEnd);
415 uiState = STATE_CODE_DWNLD;
417 case REQUEST_CODE_SEGMENT:
418 word_length = get_request_value(dev);
419 if (word_length > MAX_LENGTH) {
423 if ((word_length * 2 + (long)pUcFile) >
426 * Error, beyond boot code range.
431 // Provide mutual exclusive access while reading ASIC registers.
432 spin_lock_irqsave(&info->dpram_lock,
434 if (file_version == 5) {
436 * Position ASIC DPRAM auto-increment pointer.
438 ft1000_write_reg(dev,
439 FT1000_REG_DPRAM_ADDR,
442 for (; word_length > 0; word_length--) { /* In words */
444 //temp = RtlUshortByteSwap(temp);
445 ft1000_write_reg(dev,
446 FT1000_REG_DPRAM_DATA,
454 * Position ASIC DPRAM auto-increment pointer.
456 outw(DWNLD_MAG_PS_HDR_LOC,
458 FT1000_REG_DPRAM_ADDR);
459 if (word_length & 0x01) {
462 word_length = word_length / 2;
464 for (; word_length > 0; word_length--) { /* In words */
465 templong = *pUsFile++;
471 FT1000_REG_MAG_DPDATAL);
474 spin_unlock_irqrestore(&info->
482 put_handshake(dev, HANDSHAKE_RESPONSE);
489 case STATE_CODE_DWNLD:
490 handshake = get_handshake(dev, HANDSHAKE_REQUEST);
491 if (handshake == HANDSHAKE_REQUEST) {
493 * Get type associated with the request.
495 request = get_request_type(dev);
497 case REQUEST_FILE_CHECKSUM:
499 "ft1000_dnld: REQUEST_FOR_CHECKSUM\n");
500 put_request_value(dev, image_chksum);
502 case REQUEST_RUN_ADDRESS:
504 put_request_value(dev,
511 case REQUEST_CODE_LENGTH:
513 put_request_value(dev,
520 case REQUEST_DONE_CL:
521 /* Reposition ptrs to beginning of provisioning section */
522 switch (file_version) {
526 (USHORT *) ((long)pFileStart
531 (UCHAR *) ((long)pFileStart
540 uiState = STATE_DONE_DWNLD;
542 case REQUEST_CODE_SEGMENT:
547 word_length = get_request_value(dev);
548 if (word_length > MAX_LENGTH) {
552 if ((word_length * 2 + (long)pUcFile) >
555 * Error, beyond boot code range.
560 if (file_version == 5) {
562 * Position ASIC DPRAM auto-increment pointer.
564 ft1000_write_reg(dev,
565 FT1000_REG_DPRAM_ADDR,
568 for (; word_length > 0; word_length--) { /* In words */
570 //temp = RtlUshortByteSwap(temp);
571 ft1000_write_reg(dev,
572 FT1000_REG_DPRAM_DATA,
580 * Position ASIC DPRAM auto-increment pointer.
582 outw(DWNLD_MAG_PS_HDR_LOC,
584 FT1000_REG_DPRAM_ADDR);
585 if (word_length & 0x01) {
588 word_length = word_length / 2;
590 for (; word_length > 0; word_length--) { /* In words */
591 templong = *pUsFile++;
597 FT1000_REG_MAG_DPDATAL);
602 case REQUEST_MAILBOX_DATA:
603 // Convert length from byte count to word count. Make sure we round up.
605 (long)(info->DSPInfoBlklen + 1) / 2;
606 put_request_value(dev, word_length);
608 (PDRVMSG) & info->DSPInfoBlk[0];
610 (USHORT *) & pMailBoxData->data[0];
611 // Provide mutual exclusive access while reading ASIC registers.
612 spin_lock_irqsave(&info->dpram_lock,
614 if (file_version == 5) {
616 * Position ASIC DPRAM auto-increment pointer.
618 ft1000_write_reg(dev,
619 FT1000_REG_DPRAM_ADDR,
622 for (; word_length > 0; word_length--) { /* In words */
623 temp = ntohs(*pUsData);
624 ft1000_write_reg(dev,
625 FT1000_REG_DPRAM_DATA,
631 * Position ASIC DPRAM auto-increment pointer.
633 outw(DWNLD_MAG_PS_HDR_LOC,
635 FT1000_REG_DPRAM_ADDR);
636 if (word_length & 0x01) {
639 word_length = word_length / 2;
641 for (; word_length > 0; word_length--) { /* In words */
642 templong = *pUsData++;
647 FT1000_REG_MAG_DPDATAL);
650 spin_unlock_irqrestore(&info->
655 case REQUEST_VERSION_INFO:
657 pFileHdr5->version_data_size;
658 put_request_value(dev, word_length);
660 (USHORT *) ((long)pFileStart +
662 version_data_offset);
663 // Provide mutual exclusive access while reading ASIC registers.
664 spin_lock_irqsave(&info->dpram_lock,
666 if (file_version == 5) {
668 * Position ASIC DPRAM auto-increment pointer.
670 ft1000_write_reg(dev,
671 FT1000_REG_DPRAM_ADDR,
674 for (; word_length > 0; word_length--) { /* In words */
675 ft1000_write_reg(dev,
676 FT1000_REG_DPRAM_DATA,
684 * Position ASIC DPRAM auto-increment pointer.
686 outw(DWNLD_MAG_PS_HDR_LOC,
688 FT1000_REG_DPRAM_ADDR);
689 if (word_length & 0x01) {
692 word_length = word_length / 2;
694 for (; word_length > 0; word_length--) { /* In words */
703 FT1000_REG_MAG_DPDATAL);
706 spin_unlock_irqrestore(&info->
711 case REQUEST_CODE_BY_VERSION:
712 bGoodVersion = FALSE;
714 get_request_value(dev);
715 if (file_version == 5) {
717 (PDSP_IMAGE_INFO) ((long)
724 pFileHdr5->nDspImages;
764 (PDSP_IMAGE_INFO_V6) ((long)
771 pFileHdr5->nDspImages;
818 "ft1000_dnld: image_chksum = 0x%8x\n",
829 * Error, beyond boot code range.
840 put_handshake(dev, HANDSHAKE_RESPONSE);
847 case STATE_DONE_DWNLD:
848 if (((unsigned long) (pUcFile) - (unsigned long) pFileStart) >=
849 (unsigned long) FileLength) {
850 uiState = STATE_DONE_FILE;
854 pHdr = (struct pseudo_hdr *) pUsFile;
856 if (pHdr->portdest == 0x80 /* DspOAM */
857 && (pHdr->portsrc == 0x00 /* Driver */
858 || pHdr->portsrc == 0x10 /* FMM */ )) {
859 uiState = STATE_SECTION_PROV;
862 "FT1000:download:Download error: Bad Port IDs in Pseudo Record\n");
863 DEBUG(1, "\t Port Source = 0x%2.2x\n",
865 DEBUG(1, "\t Port Destination = 0x%2.2x\n",
872 case STATE_SECTION_PROV:
874 pHdr = (struct pseudo_hdr *) pUcFile;
876 if (pHdr->checksum == hdr_checksum(pHdr)) {
877 if (pHdr->portdest != 0x80 /* Dsp OAM */ ) {
878 uiState = STATE_DONE_PROV;
881 usHdrLength = ntohs(pHdr->length); /* Byte length for PROV records */
883 // Get buffer for provisioning data
885 kmalloc((usHdrLength + sizeof(struct pseudo_hdr)),
888 memcpy(pbuffer, (void *)pUcFile,
889 (UINT) (usHdrLength +
890 sizeof(struct pseudo_hdr)));
891 // link provisioning data
893 kmalloc(sizeof(PROV_RECORD),
896 pprov_record->pprov_data =
898 list_add_tail(&pprov_record->
901 // Move to next entry if available
903 (UCHAR *) ((unsigned long) pUcFile +
904 (unsigned long) ((usHdrLength + 1) & 0xFFFFFFFE) + sizeof(struct pseudo_hdr));
905 if ((unsigned long) (pUcFile) -
906 (unsigned long) (pFileStart) >=
907 (unsigned long) FileLength) {
919 /* Checksum did not compute */
925 case STATE_DONE_PROV:
926 uiState = STATE_DONE_FILE;