| /* |
| * CopyRight (C) 2007 Qualcomm Inc. All Rights Reserved. |
| * |
| * This file is part of Express Card USB Driver |
| */ |
| |
| #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
| |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/netdevice.h> |
| #include <linux/etherdevice.h> |
| #include <linux/usb.h> |
| #include <linux/vmalloc.h> |
| #include "ft1000_usb.h" |
| |
| |
| #define DWNLD_HANDSHAKE_LOC 0x02 |
| #define DWNLD_TYPE_LOC 0x04 |
| #define DWNLD_SIZE_MSW_LOC 0x06 |
| #define DWNLD_SIZE_LSW_LOC 0x08 |
| #define DWNLD_PS_HDR_LOC 0x0A |
| |
| #define MAX_DSP_WAIT_LOOPS 40 |
| #define DSP_WAIT_SLEEP_TIME 1000 /* 1 millisecond */ |
| #define DSP_WAIT_DISPATCH_LVL 50 /* 50 usec */ |
| |
| #define HANDSHAKE_TIMEOUT_VALUE 0xF1F1 |
| #define HANDSHAKE_RESET_VALUE 0xFEFE /* When DSP requests startover */ |
| #define HANDSHAKE_RESET_VALUE_USB 0xFE7E /* When DSP requests startover */ |
| #define HANDSHAKE_DSP_BL_READY 0xFEFE /* At start DSP writes this when bootloader ready */ |
| #define HANDSHAKE_DSP_BL_READY_USB 0xFE7E /* At start DSP writes this when bootloader ready */ |
| #define HANDSHAKE_DRIVER_READY 0xFFFF /* Driver writes after receiving 0xFEFE */ |
| #define HANDSHAKE_SEND_DATA 0x0000 /* DSP writes this when ready for more data */ |
| |
| #define HANDSHAKE_REQUEST 0x0001 /* Request from DSP */ |
| #define HANDSHAKE_RESPONSE 0x0000 /* Satisfied DSP request */ |
| |
| #define REQUEST_CODE_LENGTH 0x0000 |
| #define REQUEST_RUN_ADDRESS 0x0001 |
| #define REQUEST_CODE_SEGMENT 0x0002 /* In WORD count */ |
| #define REQUEST_DONE_BL 0x0003 |
| #define REQUEST_DONE_CL 0x0004 |
| #define REQUEST_VERSION_INFO 0x0005 |
| #define REQUEST_CODE_BY_VERSION 0x0006 |
| #define REQUEST_MAILBOX_DATA 0x0007 |
| #define REQUEST_FILE_CHECKSUM 0x0008 |
| |
| #define STATE_START_DWNLD 0x01 |
| #define STATE_BOOT_DWNLD 0x02 |
| #define STATE_CODE_DWNLD 0x03 |
| #define STATE_DONE_DWNLD 0x04 |
| #define STATE_SECTION_PROV 0x05 |
| #define STATE_DONE_PROV 0x06 |
| #define STATE_DONE_FILE 0x07 |
| |
| #define MAX_LENGTH 0x7f0 |
| |
| /* Temporary download mechanism for Magnemite */ |
| #define DWNLD_MAG_TYPE_LOC 0x00 |
| #define DWNLD_MAG_LEN_LOC 0x01 |
| #define DWNLD_MAG_ADDR_LOC 0x02 |
| #define DWNLD_MAG_CHKSUM_LOC 0x03 |
| #define DWNLD_MAG_VAL_LOC 0x04 |
| |
| #define HANDSHAKE_MAG_DSP_BL_READY 0xFEFE0000 /* At start DSP writes this when bootloader ready */ |
| #define HANDSHAKE_MAG_DSP_ENTRY 0x01000000 /* Dsp writes this to request for entry address */ |
| #define HANDSHAKE_MAG_DSP_DATA 0x02000000 /* Dsp writes this to request for data block */ |
| #define HANDSHAKE_MAG_DSP_DONE 0x03000000 /* Dsp writes this to indicate download done */ |
| |
| #define HANDSHAKE_MAG_DRV_READY 0xFFFF0000 /* Driver writes this to indicate ready to download */ |
| #define HANDSHAKE_MAG_DRV_DATA 0x02FECDAB /* Driver writes this to indicate data available to DSP */ |
| #define HANDSHAKE_MAG_DRV_ENTRY 0x01FECDAB /* Driver writes this to indicate entry point to DSP */ |
| |
| #define HANDSHAKE_MAG_TIMEOUT_VALUE 0xF1F1 |
| |
| |
| /* New Magnemite downloader */ |
| #define DWNLD_MAG1_HANDSHAKE_LOC 0x00 |
| #define DWNLD_MAG1_TYPE_LOC 0x01 |
| #define DWNLD_MAG1_SIZE_LOC 0x02 |
| #define DWNLD_MAG1_PS_HDR_LOC 0x03 |
| |
| struct dsp_file_hdr { |
| long version_id; /* Version ID of this image format. */ |
| long package_id; /* Package ID of code release. */ |
| long build_date; /* Date/time stamp when file was built. */ |
| long commands_offset; /* Offset to attached commands in Pseudo Hdr format. */ |
| long loader_offset; /* Offset to bootloader code. */ |
| long loader_code_address; /* Start address of bootloader. */ |
| long loader_code_end; /* Where bootloader code ends. */ |
| long loader_code_size; |
| long version_data_offset; /* Offset were scrambled version data begins. */ |
| long version_data_size; /* Size, in words, of scrambled version data. */ |
| long nDspImages; /* Number of DSP images in file. */ |
| }; |
| |
| #pragma pack(1) |
| struct dsp_image_info { |
| long coff_date; /* Date/time when DSP Coff image was built. */ |
| long begin_offset; /* Offset in file where image begins. */ |
| long end_offset; /* Offset in file where image begins. */ |
| long run_address; /* On chip Start address of DSP code. */ |
| long image_size; /* Size of image. */ |
| long version; /* Embedded version # of DSP code. */ |
| unsigned short checksum; /* DSP File checksum */ |
| unsigned short pad1; |
| }; |
| |
| |
| /* checks if the doorbell register is cleared */ |
| static int check_usb_db(struct ft1000_usb *ft1000dev) |
| { |
| int loopcnt; |
| u16 temp; |
| int status; |
| |
| loopcnt = 0; |
| |
| while (loopcnt < 10) { |
| status = ft1000_read_register(ft1000dev, &temp, |
| FT1000_REG_DOORBELL); |
| pr_debug("read FT1000_REG_DOORBELL value is %x\n", temp); |
| if (temp & 0x0080) { |
| pr_debug("Got checkusb doorbell\n"); |
| status = ft1000_write_register(ft1000dev, 0x0080, |
| FT1000_REG_DOORBELL); |
| status = ft1000_write_register(ft1000dev, 0x0100, |
| FT1000_REG_DOORBELL); |
| status = ft1000_write_register(ft1000dev, 0x8000, |
| FT1000_REG_DOORBELL); |
| break; |
| } |
| loopcnt++; |
| msleep(10); |
| |
| } |
| |
| loopcnt = 0; |
| while (loopcnt < 20) { |
| status = ft1000_read_register(ft1000dev, &temp, |
| FT1000_REG_DOORBELL); |
| pr_debug("Doorbell = 0x%x\n", temp); |
| if (temp & 0x8000) { |
| loopcnt++; |
| msleep(10); |
| } else { |
| pr_debug("door bell is cleared, return 0\n"); |
| return 0; |
| } |
| } |
| |
| return -1; |
| } |
| |
| /* gets the handshake and compares it with the expected value */ |
| static u16 get_handshake(struct ft1000_usb *ft1000dev, u16 expected_value) |
| { |
| u16 handshake; |
| int loopcnt; |
| int status = 0; |
| |
| loopcnt = 0; |
| |
| while (loopcnt < 100) { |
| /* Need to clear downloader doorbell if Hartley ASIC */ |
| status = ft1000_write_register(ft1000dev, FT1000_DB_DNLD_RX, |
| FT1000_REG_DOORBELL); |
| if (ft1000dev->fcodeldr) { |
| pr_debug("fcodeldr is %d\n", ft1000dev->fcodeldr); |
| ft1000dev->fcodeldr = 0; |
| status = check_usb_db(ft1000dev); |
| if (status != 0) { |
| pr_debug("check_usb_db failed\n"); |
| break; |
| } |
| status = ft1000_write_register(ft1000dev, |
| FT1000_DB_DNLD_RX, |
| FT1000_REG_DOORBELL); |
| } |
| |
| status = ft1000_read_dpram16(ft1000dev, |
| DWNLD_MAG1_HANDSHAKE_LOC, (u8 *)&handshake, 1); |
| handshake = ntohs(handshake); |
| |
| if (status) |
| return HANDSHAKE_TIMEOUT_VALUE; |
| |
| if ((handshake == expected_value) || |
| (handshake == HANDSHAKE_RESET_VALUE_USB)) { |
| return handshake; |
| } |
| loopcnt++; |
| msleep(10); |
| } |
| |
| return HANDSHAKE_TIMEOUT_VALUE; |
| } |
| |
| /* write the handshake value to the handshake location */ |
| static void put_handshake(struct ft1000_usb *ft1000dev, u16 handshake_value) |
| { |
| u32 tempx; |
| u16 tempword; |
| int status; |
| |
| tempx = (u32)handshake_value; |
| tempx = ntohl(tempx); |
| |
| tempword = (u16)(tempx & 0xffff); |
| status = ft1000_write_dpram16(ft1000dev, DWNLD_MAG1_HANDSHAKE_LOC, |
| tempword, 0); |
| tempword = (u16)(tempx >> 16); |
| status = ft1000_write_dpram16(ft1000dev, DWNLD_MAG1_HANDSHAKE_LOC, |
| tempword, 1); |
| status = ft1000_write_register(ft1000dev, FT1000_DB_DNLD_TX, |
| FT1000_REG_DOORBELL); |
| } |
| |
| static u16 get_handshake_usb(struct ft1000_usb *ft1000dev, u16 expected_value) |
| { |
| u16 handshake; |
| int loopcnt; |
| u16 temp; |
| int status = 0; |
| |
| loopcnt = 0; |
| handshake = 0; |
| |
| while (loopcnt < 100) { |
| if (ft1000dev->usbboot == 2) { |
| status = ft1000_read_dpram32(ft1000dev, 0, |
| (u8 *)&ft1000dev->tempbuf[0], 64); |
| for (temp = 0; temp < 16; temp++) { |
| pr_debug("tempbuf %d = 0x%x\n", |
| temp, ft1000dev->tempbuf[temp]); |
| } |
| status = ft1000_read_dpram16(ft1000dev, |
| DWNLD_MAG1_HANDSHAKE_LOC, |
| (u8 *)&handshake, 1); |
| pr_debug("handshake from read_dpram16 = 0x%x\n", |
| handshake); |
| if (ft1000dev->dspalive == ft1000dev->tempbuf[6]) { |
| handshake = 0; |
| } else { |
| handshake = ft1000dev->tempbuf[1]; |
| ft1000dev->dspalive = |
| ft1000dev->tempbuf[6]; |
| } |
| } else { |
| status = ft1000_read_dpram16(ft1000dev, |
| DWNLD_MAG1_HANDSHAKE_LOC, |
| (u8 *)&handshake, 1); |
| } |
| |
| loopcnt++; |
| msleep(10); |
| handshake = ntohs(handshake); |
| if ((handshake == expected_value) || |
| (handshake == HANDSHAKE_RESET_VALUE_USB)) |
| return handshake; |
| } |
| |
| return HANDSHAKE_TIMEOUT_VALUE; |
| } |
| |
| static void put_handshake_usb(struct ft1000_usb *ft1000dev, u16 handshake_value) |
| { |
| int i; |
| |
| for (i = 0; i < 1000; i++) |
| ; |
| } |
| |
| static u16 get_request_type(struct ft1000_usb *ft1000dev) |
| { |
| u16 request_type; |
| int status; |
| u16 tempword; |
| u32 tempx; |
| |
| if (ft1000dev->bootmode == 1) { |
| status = fix_ft1000_read_dpram32(ft1000dev, |
| DWNLD_MAG1_TYPE_LOC, (u8 *)&tempx); |
| tempx = ntohl(tempx); |
| } else { |
| tempx = 0; |
| status = ft1000_read_dpram16(ft1000dev, |
| DWNLD_MAG1_TYPE_LOC, (u8 *)&tempword, 1); |
| tempx |= (tempword << 16); |
| tempx = ntohl(tempx); |
| } |
| request_type = (u16)tempx; |
| |
| return request_type; |
| } |
| |
| static u16 get_request_type_usb(struct ft1000_usb *ft1000dev) |
| { |
| u16 request_type; |
| int status; |
| u16 tempword; |
| u32 tempx; |
| |
| if (ft1000dev->bootmode == 1) { |
| status = fix_ft1000_read_dpram32(ft1000dev, |
| DWNLD_MAG1_TYPE_LOC, (u8 *)&tempx); |
| tempx = ntohl(tempx); |
| } else { |
| if (ft1000dev->usbboot == 2) { |
| tempx = ft1000dev->tempbuf[2]; |
| tempword = ft1000dev->tempbuf[3]; |
| } else { |
| tempx = 0; |
| status = ft1000_read_dpram16(ft1000dev, |
| DWNLD_MAG1_TYPE_LOC, |
| (u8 *)&tempword, 1); |
| } |
| tempx |= (tempword << 16); |
| tempx = ntohl(tempx); |
| } |
| request_type = (u16)tempx; |
| |
| return request_type; |
| } |
| |
| static long get_request_value(struct ft1000_usb *ft1000dev) |
| { |
| u32 value; |
| u16 tempword; |
| int status; |
| |
| if (ft1000dev->bootmode == 1) { |
| status = fix_ft1000_read_dpram32(ft1000dev, |
| DWNLD_MAG1_SIZE_LOC, (u8 *)&value); |
| value = ntohl(value); |
| } else { |
| status = ft1000_read_dpram16(ft1000dev, |
| DWNLD_MAG1_SIZE_LOC, (u8 *)&tempword, 0); |
| value = tempword; |
| status = ft1000_read_dpram16(ft1000dev, |
| DWNLD_MAG1_SIZE_LOC, (u8 *)&tempword, 1); |
| value |= (tempword << 16); |
| value = ntohl(value); |
| } |
| |
| return value; |
| } |
| |
| |
| /* writes a value to DWNLD_MAG1_SIZE_LOC */ |
| static void put_request_value(struct ft1000_usb *ft1000dev, long lvalue) |
| { |
| u32 tempx; |
| int status; |
| |
| tempx = ntohl(lvalue); |
| status = fix_ft1000_write_dpram32(ft1000dev, DWNLD_MAG1_SIZE_LOC, |
| (u8 *)&tempx); |
| } |
| |
| |
| |
| /* returns the checksum of the pseudo header */ |
| static u16 hdr_checksum(struct pseudo_hdr *pHdr) |
| { |
| u16 *usPtr = (u16 *)pHdr; |
| u16 chksum; |
| |
| |
| chksum = (((((usPtr[0] ^ usPtr[1]) ^ usPtr[2]) ^ usPtr[3]) ^ |
| usPtr[4]) ^ usPtr[5]) ^ usPtr[6]; |
| |
| return chksum; |
| } |
| |
| static int check_buffers(u16 *buff_w, u16 *buff_r, int len, int offset) |
| { |
| int i; |
| |
| for (i = 0; i < len; i++) { |
| if (buff_w[i] != buff_r[i + offset]) |
| return -EREMOTEIO; |
| } |
| |
| return 0; |
| } |
| |
| static int write_dpram32_and_check(struct ft1000_usb *ft1000dev, |
| u16 tempbuffer[], u16 dpram) |
| { |
| int status; |
| u16 resultbuffer[64]; |
| int i; |
| |
| for (i = 0; i < 10; i++) { |
| status = ft1000_write_dpram32(ft1000dev, dpram, |
| (u8 *)&tempbuffer[0], 64); |
| if (status == 0) { |
| /* Work around for ASIC bit stuffing problem. */ |
| if ((tempbuffer[31] & 0xfe00) == 0xfe00) { |
| status = ft1000_write_dpram32(ft1000dev, |
| dpram+12, (u8 *)&tempbuffer[24], |
| 64); |
| } |
| /* Let's check the data written */ |
| status = ft1000_read_dpram32(ft1000dev, dpram, |
| (u8 *)&resultbuffer[0], 64); |
| if ((tempbuffer[31] & 0xfe00) == 0xfe00) { |
| if (check_buffers(tempbuffer, resultbuffer, 28, |
| 0)) { |
| pr_debug("DPRAM write failed 1 during bootloading\n"); |
| usleep_range(9000, 11000); |
| break; |
| } |
| status = ft1000_read_dpram32(ft1000dev, |
| dpram+12, |
| (u8 *)&resultbuffer[0], 64); |
| |
| if (check_buffers(tempbuffer, resultbuffer, 16, |
| 24)) { |
| pr_debug("DPRAM write failed 2 during bootloading\n"); |
| usleep_range(9000, 11000); |
| break; |
| } |
| } else { |
| if (check_buffers(tempbuffer, resultbuffer, 32, |
| 0)) { |
| pr_debug("DPRAM write failed 3 during bootloading\n"); |
| usleep_range(9000, 11000); |
| break; |
| } |
| } |
| if (status == 0) |
| break; |
| } |
| } |
| return status; |
| } |
| |
| /* writes a block of DSP image to DPRAM |
| * Parameters: struct ft1000_usb - device structure |
| * u16 **pUsFile - DSP image file pointer in u16 |
| * u8 **pUcFile - DSP image file pointer in u8 |
| * long word_length - length of the buffer to be written to DPRAM |
| */ |
| static int write_blk(struct ft1000_usb *ft1000dev, u16 **pUsFile, u8 **pUcFile, |
| long word_length) |
| { |
| int status = 0; |
| u16 dpram; |
| int loopcnt, i; |
| u16 tempword; |
| u16 tempbuffer[64]; |
| |
| /*pr_debug("start word_length = %d\n",(int)word_length); */ |
| dpram = (u16)DWNLD_MAG1_PS_HDR_LOC; |
| tempword = *(*pUsFile); |
| (*pUsFile)++; |
| status = ft1000_write_dpram16(ft1000dev, dpram, tempword, 0); |
| tempword = *(*pUsFile); |
| (*pUsFile)++; |
| status = ft1000_write_dpram16(ft1000dev, dpram++, tempword, 1); |
| |
| *pUcFile = *pUcFile + 4; |
| word_length--; |
| tempword = (u16)word_length; |
| word_length = (word_length / 16) + 1; |
| for (; word_length > 0; word_length--) { /* In words */ |
| loopcnt = 0; |
| for (i = 0; i < 32; i++) { |
| if (tempword != 0) { |
| tempbuffer[i++] = *(*pUsFile); |
| (*pUsFile)++; |
| tempbuffer[i] = *(*pUsFile); |
| (*pUsFile)++; |
| *pUcFile = *pUcFile + 4; |
| loopcnt++; |
| tempword--; |
| } else { |
| tempbuffer[i++] = 0; |
| tempbuffer[i] = 0; |
| } |
| } |
| |
| /*pr_debug("loopcnt is %d\n", loopcnt); */ |
| /*pr_debug("write_blk: bootmode = %d\n", bootmode); */ |
| /*pr_debug("write_blk: dpram = %x\n", dpram); */ |
| if (ft1000dev->bootmode == 0) { |
| if (dpram >= 0x3F4) |
| status = ft1000_write_dpram32(ft1000dev, dpram, |
| (u8 *)&tempbuffer[0], 8); |
| else |
| status = ft1000_write_dpram32(ft1000dev, dpram, |
| (u8 *)&tempbuffer[0], 64); |
| } else { |
| status = write_dpram32_and_check(ft1000dev, tempbuffer, |
| dpram); |
| if (status != 0) { |
| pr_debug("Write failed tempbuffer[31] = 0x%x\n", |
| tempbuffer[31]); |
| break; |
| } |
| } |
| dpram = dpram + loopcnt; |
| } |
| return status; |
| } |
| |
| static void usb_dnld_complete(struct urb *urb) |
| { |
| /* pr_debug("****** usb_dnld_complete\n"); */ |
| } |
| |
| /* writes a block of DSP image to DPRAM |
| * Parameters: struct ft1000_usb - device structure |
| * u16 **pUsFile - DSP image file pointer in u16 |
| * u8 **pUcFile - DSP image file pointer in u8 |
| * long word_length - length of the buffer to be written to DPRAM |
| */ |
| static int write_blk_fifo(struct ft1000_usb *ft1000dev, u16 **pUsFile, |
| u8 **pUcFile, long word_length) |
| { |
| int byte_length; |
| |
| byte_length = word_length * 4; |
| |
| if (byte_length && ((byte_length % 64) == 0)) |
| byte_length += 4; |
| |
| if (byte_length < 64) |
| byte_length = 68; |
| |
| usb_init_urb(ft1000dev->tx_urb); |
| memcpy(ft1000dev->tx_buf, *pUcFile, byte_length); |
| usb_fill_bulk_urb(ft1000dev->tx_urb, |
| ft1000dev->dev, |
| usb_sndbulkpipe(ft1000dev->dev, |
| ft1000dev->bulk_out_endpointAddr), |
| ft1000dev->tx_buf, byte_length, usb_dnld_complete, |
| ft1000dev); |
| |
| usb_submit_urb(ft1000dev->tx_urb, GFP_ATOMIC); |
| |
| *pUsFile = *pUsFile + (word_length << 1); |
| *pUcFile = *pUcFile + (word_length << 2); |
| |
| return 0; |
| } |
| |
| static int scram_start_dwnld(struct ft1000_usb *ft1000dev, u16 *hshake, |
| u32 *state) |
| { |
| int status = 0; |
| |
| if (ft1000dev->usbboot) |
| *hshake = get_handshake_usb(ft1000dev, HANDSHAKE_DSP_BL_READY); |
| else |
| *hshake = get_handshake(ft1000dev, HANDSHAKE_DSP_BL_READY); |
| if (*hshake == HANDSHAKE_DSP_BL_READY) { |
| pr_debug("handshake is HANDSHAKE_DSP_BL_READY, call put_handshake(HANDSHAKE_DRIVER_READY)\n"); |
| put_handshake(ft1000dev, HANDSHAKE_DRIVER_READY); |
| } else if (*hshake == HANDSHAKE_TIMEOUT_VALUE) { |
| status = -ETIMEDOUT; |
| } else { |
| pr_debug("Download error: Handshake failed\n"); |
| status = -ENETRESET; |
| } |
| *state = STATE_BOOT_DWNLD; |
| return status; |
| } |
| |
| static int request_code_segment(struct ft1000_usb *ft1000dev, u16 **s_file, |
| u8 **c_file, const u8 *endpoint, bool boot_case) |
| { |
| long word_length; |
| int status = 0; |
| |
| word_length = get_request_value(ft1000dev); |
| /*pr_debug("word_length = 0x%x\n", (int)word_length); */ |
| /*NdisMSleep (100); */ |
| if (word_length > MAX_LENGTH) { |
| pr_debug("Download error: Max length exceeded\n"); |
| return -1; |
| } |
| if ((word_length * 2 + (long)c_file) > (long)endpoint) { |
| /* Error, beyond boot code range.*/ |
| pr_debug("Download error: Requested len=%d exceeds BOOT code boundary\n", |
| (int)word_length); |
| return -1; |
| } |
| if (word_length & 0x1) |
| word_length++; |
| word_length = word_length / 2; |
| |
| if (boot_case) { |
| status = write_blk(ft1000dev, s_file, c_file, word_length); |
| /*pr_debug("write_blk returned %d\n", status); */ |
| } else { |
| status = write_blk_fifo(ft1000dev, s_file, c_file, word_length); |
| if (ft1000dev->usbboot == 0) |
| ft1000dev->usbboot++; |
| if (ft1000dev->usbboot == 1) |
| status |= ft1000_write_dpram16(ft1000dev, |
| DWNLD_MAG1_PS_HDR_LOC, 0, 0); |
| } |
| return status; |
| } |
| |
| /* Scramble downloader for Harley based ASIC via USB interface */ |
| int scram_dnldr(struct ft1000_usb *ft1000dev, void *pFileStart, |
| u32 FileLength) |
| { |
| int status = 0; |
| u32 state; |
| u16 handshake; |
| struct pseudo_hdr *pseudo_header; |
| u16 pseudo_header_len; |
| long word_length; |
| u16 request; |
| u16 temp; |
| |
| struct dsp_file_hdr *file_hdr; |
| struct dsp_image_info *dsp_img_info = NULL; |
| long requested_version; |
| bool correct_version; |
| struct drv_msg *mailbox_data; |
| u16 *data = NULL; |
| u16 *s_file = NULL; |
| u8 *c_file = NULL; |
| u8 *boot_end = NULL, *code_end = NULL; |
| int image; |
| long loader_code_address, loader_code_size = 0; |
| long run_address = 0, run_size = 0; |
| |
| u32 templong; |
| u32 image_chksum = 0; |
| |
| u16 dpram = 0; |
| u8 *pbuffer; |
| struct prov_record *pprov_record; |
| struct ft1000_info *pft1000info = netdev_priv(ft1000dev->net); |
| |
| ft1000dev->fcodeldr = 0; |
| ft1000dev->usbboot = 0; |
| ft1000dev->dspalive = 0xffff; |
| |
| /* |
| * Get version id of file, at first 4 bytes of file, for newer files. |
| */ |
| |
| state = STATE_START_DWNLD; |
| |
| file_hdr = pFileStart; |
| |
| ft1000_write_register(ft1000dev, 0x800, FT1000_REG_MAG_WATERMARK); |
| |
| s_file = (u16 *) (pFileStart + file_hdr->loader_offset); |
| c_file = (u8 *) (pFileStart + file_hdr->loader_offset); |
| |
| boot_end = (u8 *) (pFileStart + file_hdr->loader_code_end); |
| |
| loader_code_address = file_hdr->loader_code_address; |
| loader_code_size = file_hdr->loader_code_size; |
| correct_version = false; |
| |
| while ((status == 0) && (state != STATE_DONE_FILE)) { |
| switch (state) { |
| case STATE_START_DWNLD: |
| status = scram_start_dwnld(ft1000dev, &handshake, |
| &state); |
| break; |
| |
| case STATE_BOOT_DWNLD: |
| pr_debug("STATE_BOOT_DWNLD\n"); |
| ft1000dev->bootmode = 1; |
| handshake = get_handshake(ft1000dev, HANDSHAKE_REQUEST); |
| if (handshake == HANDSHAKE_REQUEST) { |
| /* |
| * Get type associated with the request. |
| */ |
| request = get_request_type(ft1000dev); |
| switch (request) { |
| case REQUEST_RUN_ADDRESS: |
| pr_debug("REQUEST_RUN_ADDRESS\n"); |
| put_request_value(ft1000dev, |
| loader_code_address); |
| break; |
| case REQUEST_CODE_LENGTH: |
| pr_debug("REQUEST_CODE_LENGTH\n"); |
| put_request_value(ft1000dev, |
| loader_code_size); |
| break; |
| case REQUEST_DONE_BL: |
| pr_debug("REQUEST_DONE_BL\n"); |
| /* Reposition ptrs to beginning of code section */ |
| s_file = (u16 *) (boot_end); |
| c_file = (u8 *) (boot_end); |
| /* pr_debug("download:s_file = 0x%8x\n", (int)s_file); */ |
| /* pr_debug("FT1000:download:c_file = 0x%8x\n", (int)c_file); */ |
| state = STATE_CODE_DWNLD; |
| ft1000dev->fcodeldr = 1; |
| break; |
| case REQUEST_CODE_SEGMENT: |
| status = request_code_segment(ft1000dev, |
| &s_file, &c_file, |
| boot_end, |
| true); |
| break; |
| default: |
| pr_debug("Download error: Bad request type=%d in BOOT download state\n", |
| request); |
| status = -1; |
| break; |
| } |
| if (ft1000dev->usbboot) |
| put_handshake_usb(ft1000dev, |
| HANDSHAKE_RESPONSE); |
| else |
| put_handshake(ft1000dev, |
| HANDSHAKE_RESPONSE); |
| } else { |
| pr_debug("Download error: Handshake failed\n"); |
| status = -1; |
| } |
| |
| break; |
| |
| case STATE_CODE_DWNLD: |
| /* pr_debug("STATE_CODE_DWNLD\n"); */ |
| ft1000dev->bootmode = 0; |
| if (ft1000dev->usbboot) |
| handshake = |
| get_handshake_usb(ft1000dev, |
| HANDSHAKE_REQUEST); |
| else |
| handshake = |
| get_handshake(ft1000dev, HANDSHAKE_REQUEST); |
| if (handshake == HANDSHAKE_REQUEST) { |
| /* |
| * Get type associated with the request. |
| */ |
| if (ft1000dev->usbboot) |
| request = |
| get_request_type_usb(ft1000dev); |
| else |
| request = get_request_type(ft1000dev); |
| switch (request) { |
| case REQUEST_FILE_CHECKSUM: |
| pr_debug("image_chksum = 0x%8x\n", |
| image_chksum); |
| put_request_value(ft1000dev, |
| image_chksum); |
| break; |
| case REQUEST_RUN_ADDRESS: |
| pr_debug("REQUEST_RUN_ADDRESS\n"); |
| if (correct_version) { |
| pr_debug("run_address = 0x%8x\n", |
| (int)run_address); |
| put_request_value(ft1000dev, |
| run_address); |
| } else { |
| pr_debug("Download error: Got Run address request before image offset request\n"); |
| status = -1; |
| break; |
| } |
| break; |
| case REQUEST_CODE_LENGTH: |
| pr_debug("REQUEST_CODE_LENGTH\n"); |
| if (correct_version) { |
| pr_debug("run_size = 0x%8x\n", |
| (int)run_size); |
| put_request_value(ft1000dev, |
| run_size); |
| } else { |
| pr_debug("Download error: Got Size request before image offset request\n"); |
| status = -1; |
| break; |
| } |
| break; |
| case REQUEST_DONE_CL: |
| ft1000dev->usbboot = 3; |
| /* Reposition ptrs to beginning of provisioning section */ |
| s_file = |
| (u16 *) (pFileStart + |
| file_hdr->commands_offset); |
| c_file = |
| (u8 *) (pFileStart + |
| file_hdr->commands_offset); |
| state = STATE_DONE_DWNLD; |
| break; |
| case REQUEST_CODE_SEGMENT: |
| /* pr_debug("REQUEST_CODE_SEGMENT - CODELOADER\n"); */ |
| if (!correct_version) { |
| pr_debug("Download error: Got Code Segment request before image offset request\n"); |
| status = -1; |
| break; |
| } |
| |
| status = request_code_segment(ft1000dev, |
| &s_file, &c_file, |
| code_end, |
| false); |
| |
| break; |
| |
| case REQUEST_MAILBOX_DATA: |
| pr_debug("REQUEST_MAILBOX_DATA\n"); |
| /* Convert length from byte count to word count. Make sure we round up. */ |
| word_length = |
| (long)(pft1000info->DSPInfoBlklen + |
| 1) / 2; |
| put_request_value(ft1000dev, |
| word_length); |
| mailbox_data = |
| (struct drv_msg *)&(pft1000info-> |
| DSPInfoBlk[0]); |
| /* |
| * Position ASIC DPRAM auto-increment pointer. |
| */ |
| |
| data = (u16 *)&mailbox_data->data[0]; |
| dpram = (u16)DWNLD_MAG1_PS_HDR_LOC; |
| if (word_length & 0x1) |
| word_length++; |
| |
| word_length = word_length / 2; |
| |
| for (; word_length > 0; word_length--) { /* In words */ |
| |
| templong = *data++; |
| templong |= (*data++ << 16); |
| status = |
| fix_ft1000_write_dpram32 |
| (ft1000dev, dpram++, |
| (u8 *)&templong); |
| |
| } |
| break; |
| |
| case REQUEST_VERSION_INFO: |
| pr_debug("REQUEST_VERSION_INFO\n"); |
| word_length = |
| file_hdr->version_data_size; |
| put_request_value(ft1000dev, |
| word_length); |
| /* |
| * Position ASIC DPRAM auto-increment pointer. |
| */ |
| |
| s_file = |
| (u16 *) (pFileStart + |
| file_hdr-> |
| version_data_offset); |
| |
| dpram = (u16)DWNLD_MAG1_PS_HDR_LOC; |
| if (word_length & 0x1) |
| word_length++; |
| |
| word_length = word_length / 2; |
| |
| for (; word_length > 0; word_length--) { /* In words */ |
| |
| templong = ntohs(*s_file++); |
| temp = ntohs(*s_file++); |
| templong |= (temp << 16); |
| status = |
| fix_ft1000_write_dpram32 |
| (ft1000dev, dpram++, |
| (u8 *)&templong); |
| |
| } |
| break; |
| |
| case REQUEST_CODE_BY_VERSION: |
| pr_debug("REQUEST_CODE_BY_VERSION\n"); |
| correct_version = false; |
| requested_version = |
| get_request_value(ft1000dev); |
| |
| dsp_img_info = |
| (struct dsp_image_info *)(pFileStart |
| + |
| sizeof |
| (struct |
| dsp_file_hdr)); |
| |
| for (image = 0; |
| image < file_hdr->nDspImages; |
| image++) { |
| |
| if (dsp_img_info->version == |
| requested_version) { |
| correct_version = true; |
| pr_debug("correct_version is TRUE\n"); |
| s_file = |
| (u16 *) (pFileStart |
| + |
| dsp_img_info-> |
| begin_offset); |
| c_file = |
| (u8 *) (pFileStart + |
| dsp_img_info-> |
| begin_offset); |
| code_end = |
| (u8 *) (pFileStart + |
| dsp_img_info-> |
| end_offset); |
| run_address = |
| dsp_img_info-> |
| run_address; |
| run_size = |
| dsp_img_info-> |
| image_size; |
| image_chksum = |
| (u32)dsp_img_info-> |
| checksum; |
| break; |
| } |
| dsp_img_info++; |
| |
| } /* end of for */ |
| |
| if (!correct_version) { |
| /* |
| * Error, beyond boot code range. |
| */ |
| pr_debug("Download error: Bad Version Request = 0x%x.\n", |
| (int)requested_version); |
| status = -1; |
| break; |
| } |
| break; |
| |
| default: |
| pr_debug("Download error: Bad request type=%d in CODE download state.\n", |
| request); |
| status = -1; |
| break; |
| } |
| if (ft1000dev->usbboot) |
| put_handshake_usb(ft1000dev, |
| HANDSHAKE_RESPONSE); |
| else |
| put_handshake(ft1000dev, |
| HANDSHAKE_RESPONSE); |
| } else { |
| pr_debug("Download error: Handshake failed\n"); |
| status = -1; |
| } |
| |
| break; |
| |
| case STATE_DONE_DWNLD: |
| pr_debug("Code loader is done...\n"); |
| state = STATE_SECTION_PROV; |
| break; |
| |
| case STATE_SECTION_PROV: |
| pr_debug("STATE_SECTION_PROV\n"); |
| pseudo_header = (struct pseudo_hdr *)c_file; |
| |
| if (pseudo_header->checksum == |
| hdr_checksum(pseudo_header)) { |
| if (pseudo_header->portdest != |
| 0x80 /* Dsp OAM */) { |
| state = STATE_DONE_PROV; |
| break; |
| } |
| pseudo_header_len = ntohs(pseudo_header->length); /* Byte length for PROV records */ |
| |
| /* Get buffer for provisioning data */ |
| pbuffer = |
| kmalloc(pseudo_header_len + |
| sizeof(struct pseudo_hdr), |
| GFP_ATOMIC); |
| if (pbuffer) { |
| memcpy(pbuffer, c_file, |
| (u32) (pseudo_header_len + |
| sizeof(struct |
| pseudo_hdr))); |
| /* link provisioning data */ |
| pprov_record = |
| kmalloc(sizeof(struct prov_record), |
| GFP_ATOMIC); |
| if (pprov_record) { |
| pprov_record->pprov_data = |
| pbuffer; |
| list_add_tail(&pprov_record-> |
| list, |
| &pft1000info-> |
| prov_list); |
| /* Move to next entry if available */ |
| c_file = |
| (u8 *) ((unsigned long) |
| c_file + |
| (u32) ((pseudo_header_len + 1) & 0xFFFFFFFE) + sizeof(struct pseudo_hdr)); |
| if ((unsigned long)(c_file) - |
| (unsigned long)(pFileStart) |
| >= |
| (unsigned long)FileLength) { |
| state = STATE_DONE_FILE; |
| } |
| } else { |
| kfree(pbuffer); |
| status = -1; |
| } |
| } else { |
| status = -1; |
| } |
| } else { |
| /* Checksum did not compute */ |
| status = -1; |
| } |
| pr_debug("after STATE_SECTION_PROV, state = %d, status= %d\n", |
| state, status); |
| break; |
| |
| case STATE_DONE_PROV: |
| pr_debug("STATE_DONE_PROV\n"); |
| state = STATE_DONE_FILE; |
| break; |
| |
| default: |
| status = -1; |
| break; |
| } /* End Switch */ |
| |
| if (status != 0) |
| break; |
| |
| /**** |
| // Check if Card is present |
| status = Harley_Read_Register(&temp, FT1000_REG_SUP_IMASK); |
| if ( (status != NDIS_STATUS_SUCCESS) || (temp == 0x0000) ) { |
| break; |
| } |
| |
| status = Harley_Read_Register(&temp, FT1000_REG_ASIC_ID); |
| if ( (status != NDIS_STATUS_SUCCESS) || (temp == 0xffff) ) { |
| break; |
| } |
| ****/ |
| |
| } /* End while */ |
| |
| pr_debug("Download exiting with status = 0x%8x\n", status); |
| ft1000_write_register(ft1000dev, FT1000_DB_DNLD_TX, |
| FT1000_REG_DOORBELL); |
| |
| return status; |
| } |