]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/staging/ft1000/ft1000-pcmcia/ft1000_dnld.c
Merge branch 'master' into csb1725
[mv-sheeva.git] / drivers / staging / ft1000 / ft1000-pcmcia / ft1000_dnld.c
diff --git a/drivers/staging/ft1000/ft1000-pcmcia/ft1000_dnld.c b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_dnld.c
new file mode 100644 (file)
index 0000000..0bf398d
--- /dev/null
@@ -0,0 +1,940 @@
+/*---------------------------------------------------------------------------
+   FT1000 driver for Flarion Flash OFDM NIC Device
+
+   Copyright (C) 2002 Flarion Technologies, All rights reserved.
+
+   This program is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published by the Free
+   Software Foundation; either version 2 of the License, or (at your option) any
+   later version. This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+   more details. You should have received a copy of the GNU General Public
+   License along with this program; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place -
+   Suite 330, Boston, MA 02111-1307, USA.
+  --------------------------------------------------------------------------
+
+   Description:  This module will handshake with the DSP bootloader to
+                 download the DSP runtime image.
+
+---------------------------------------------------------------------------*/
+
+#define __KERNEL_SYSCALLS__
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/unistd.h>
+#include <linux/netdevice.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/vmalloc.h>
+
+#include "ft1000_dev.h"
+#include "ft1000.h"
+#include "boot.h"
+
+#ifdef FT_DEBUG
+#define DEBUG(n, args...) printk(KERN_DEBUG args);
+#else
+#define DEBUG(n, args...)
+#endif
+
+#define  MAX_DSP_WAIT_LOOPS      100
+#define  DSP_WAIT_SLEEP_TIME     1     /* 1 millisecond */
+
+#define  MAX_LENGTH              0x7f0
+
+#define  DWNLD_MAG_HANDSHAKE_LOC 0x00
+#define  DWNLD_MAG_TYPE_LOC      0x01
+#define  DWNLD_MAG_SIZE_LOC      0x02
+#define  DWNLD_MAG_PS_HDR_LOC    0x03
+
+#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  HANDSHAKE_TIMEOUT_VALUE 0xF1F1
+#define  HANDSHAKE_RESET_VALUE   0xFEFE        /* When DSP requests startover */
+#define  HANDSHAKE_DSP_BL_READY  0xFEFE        /* 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
+
+USHORT get_handshake(struct net_device *dev, USHORT expected_value);
+void put_handshake(struct net_device *dev, USHORT handshake_value);
+USHORT get_request_type(struct net_device *dev);
+long get_request_value(struct net_device *dev);
+void put_request_value(struct net_device *dev, long lvalue);
+USHORT hdr_checksum(PPSEUDO_HDR pHdr);
+
+typedef struct _DSP_FILE_HDR {
+       long build_date;
+       long dsp_coff_date;
+       long loader_code_address;
+       long loader_code_size;
+       long loader_code_end;
+       long dsp_code_address;
+       long dsp_code_size;
+       long dsp_code_end;
+       long reserved[8];
+} __attribute__ ((packed)) DSP_FILE_HDR, *PDSP_FILE_HDR;
+
+typedef struct _DSP_FILE_HDR_5 {
+       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.
+} __attribute__ ((packed)) DSP_FILE_HDR_5, *PDSP_FILE_HDR_5;
+
+typedef 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.
+} __attribute__ ((packed)) DSP_IMAGE_INFO, *PDSP_IMAGE_INFO;
+
+typedef struct _DSP_IMAGE_INFO_V6 {
+       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;
+} __attribute__ ((packed)) DSP_IMAGE_INFO_V6, *PDSP_IMAGE_INFO_V6;
+
+void card_bootload(struct net_device *dev)
+{
+       FT1000_INFO *info = (PFT1000_INFO) netdev_priv(dev);
+       unsigned long flags;
+       PULONG pdata;
+       UINT size;
+       UINT i;
+       ULONG templong;
+
+       DEBUG(0, "card_bootload is called\n");
+
+       pdata = (PULONG) bootimage;
+       size = sizeof(bootimage);
+
+       // check for odd word
+       if (size & 0x0003) {
+               size += 4;
+       }
+       // Provide mutual exclusive access while reading ASIC registers.
+       spin_lock_irqsave(&info->dpram_lock, flags);
+
+       // need to set i/o base address initially and hardware will autoincrement
+       ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, FT1000_DPRAM_BASE);
+       // write bytes
+       for (i = 0; i < (size >> 2); i++) {
+               templong = *pdata++;
+               outl(templong, dev->base_addr + FT1000_REG_MAG_DPDATA);
+       }
+
+       spin_unlock_irqrestore(&info->dpram_lock, flags);
+}
+
+USHORT get_handshake(struct net_device *dev, USHORT expected_value)
+{
+       FT1000_INFO *info = (PFT1000_INFO) netdev_priv(dev);
+       USHORT handshake;
+       ULONG tempx;
+       int loopcnt;
+
+       loopcnt = 0;
+       while (loopcnt < MAX_DSP_WAIT_LOOPS) {
+               if (info->AsicID == ELECTRABUZZ_ID) {
+                       ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
+                                        DWNLD_HANDSHAKE_LOC);
+
+                       handshake = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
+               } else {
+                       tempx =
+                               ntohl(ft1000_read_dpram_mag_32
+                                 (dev, DWNLD_MAG_HANDSHAKE_LOC));
+                       handshake = (USHORT) tempx;
+               }
+
+               if ((handshake == expected_value)
+                       || (handshake == HANDSHAKE_RESET_VALUE)) {
+                       return handshake;
+               } else {
+                       loopcnt++;
+                       mdelay(DSP_WAIT_SLEEP_TIME);
+               }
+
+       }
+
+       return HANDSHAKE_TIMEOUT_VALUE;
+
+}
+
+void put_handshake(struct net_device *dev, USHORT handshake_value)
+{
+       FT1000_INFO *info = (PFT1000_INFO) netdev_priv(dev);
+       ULONG tempx;
+
+       if (info->AsicID == ELECTRABUZZ_ID) {
+               ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
+                                DWNLD_HANDSHAKE_LOC);
+               ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, handshake_value);  /* Handshake */
+       } else {
+               tempx = (ULONG) handshake_value;
+               tempx = ntohl(tempx);
+               ft1000_write_dpram_mag_32(dev, DWNLD_MAG_HANDSHAKE_LOC, tempx); /* Handshake */
+       }
+}
+
+USHORT get_request_type(struct net_device *dev)
+{
+       FT1000_INFO *info = (PFT1000_INFO) netdev_priv(dev);
+       USHORT request_type;
+       ULONG tempx;
+
+       if (info->AsicID == ELECTRABUZZ_ID) {
+               ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, DWNLD_TYPE_LOC);
+               request_type = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
+       } else {
+               tempx = ft1000_read_dpram_mag_32(dev, DWNLD_MAG_TYPE_LOC);
+               tempx = ntohl(tempx);
+               request_type = (USHORT) tempx;
+       }
+
+       return request_type;
+
+}
+
+long get_request_value(struct net_device *dev)
+{
+       FT1000_INFO *info = (PFT1000_INFO) netdev_priv(dev);
+       long value;
+       USHORT w_val;
+
+       if (info->AsicID == ELECTRABUZZ_ID) {
+               ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
+                                DWNLD_SIZE_MSW_LOC);
+
+               w_val = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
+
+               value = (long)(w_val << 16);
+
+               ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
+                                DWNLD_SIZE_LSW_LOC);
+
+               w_val = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
+
+               value = (long)(value | w_val);
+       } else {
+               value = ft1000_read_dpram_mag_32(dev, DWNLD_MAG_SIZE_LOC);
+               value = ntohl(value);
+       }
+
+       return value;
+
+}
+
+void put_request_value(struct net_device *dev, long lvalue)
+{
+       FT1000_INFO *info = (PFT1000_INFO) netdev_priv(dev);
+       USHORT size;
+       ULONG tempx;
+
+       if (info->AsicID == ELECTRABUZZ_ID) {
+               size = (USHORT) (lvalue >> 16);
+
+               ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
+                                DWNLD_SIZE_MSW_LOC);
+
+               ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, size);
+
+               size = (USHORT) (lvalue);
+
+               ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
+                                DWNLD_SIZE_LSW_LOC);
+
+               ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, size);
+       } else {
+               tempx = ntohl(lvalue);
+               ft1000_write_dpram_mag_32(dev, DWNLD_MAG_SIZE_LOC, tempx);      /* Handshake */
+       }
+
+}
+
+USHORT hdr_checksum(PPSEUDO_HDR pHdr)
+{
+       USHORT *usPtr = (USHORT *) pHdr;
+       USHORT chksum;
+
+       chksum = ((((((usPtr[0] ^ usPtr[1]) ^ usPtr[2]) ^ usPtr[3]) ^
+                       usPtr[4]) ^ usPtr[5]) ^ usPtr[6]);
+
+       return chksum;
+}
+
+int card_download(struct net_device *dev, void *pFileStart, UINT FileLength)
+{
+       FT1000_INFO *info = (PFT1000_INFO) netdev_priv(dev);
+       int Status = SUCCESS;
+       USHORT DspWordCnt = 0;
+       UINT uiState;
+       USHORT handshake;
+       PPSEUDO_HDR pHdr;
+       USHORT usHdrLength;
+       PDSP_FILE_HDR pFileHdr;
+       long word_length;
+       USHORT request;
+       USHORT temp;
+       PPROV_RECORD pprov_record;
+       PUCHAR pbuffer;
+       PDSP_FILE_HDR_5 pFileHdr5;
+       PDSP_IMAGE_INFO pDspImageInfo = NULL;
+       PDSP_IMAGE_INFO_V6 pDspImageInfoV6 = NULL;
+       long requested_version;
+       BOOLEAN bGoodVersion = 0;
+       PDRVMSG pMailBoxData;
+       USHORT *pUsData = NULL;
+       USHORT *pUsFile = NULL;
+       UCHAR *pUcFile = NULL;
+       UCHAR *pBootEnd = NULL;
+       UCHAR *pCodeEnd = NULL;
+       int imageN;
+       long file_version;
+       long loader_code_address = 0;
+       long loader_code_size = 0;
+       long run_address = 0;
+       long run_size = 0;
+       unsigned long flags;
+       unsigned long templong;
+       unsigned long image_chksum = 0;
+
+       //
+       // Get version id of file, at first 4 bytes of file, for newer files.
+       //
+       file_version = *(long *)pFileStart;
+
+       uiState = STATE_START_DWNLD;
+
+       pFileHdr = (PDSP_FILE_HDR) pFileStart;
+       pFileHdr5 = (PDSP_FILE_HDR_5) pFileStart;
+
+       switch (file_version) {
+       case 5:
+       case 6:
+               pUsFile =
+                       (USHORT *) ((long)pFileStart + pFileHdr5->loader_offset);
+               pUcFile =
+                       (UCHAR *) ((long)pFileStart + pFileHdr5->loader_offset);
+
+               pBootEnd =
+                       (UCHAR *) ((long)pFileStart + pFileHdr5->loader_code_end);
+
+               loader_code_address = pFileHdr5->loader_code_address;
+               loader_code_size = pFileHdr5->loader_code_size;
+               bGoodVersion = FALSE;
+               break;
+
+       default:
+               Status = FAILURE;
+               break;
+       }
+
+       while ((Status == SUCCESS) && (uiState != STATE_DONE_FILE)) {
+
+               switch (uiState) {
+               case STATE_START_DWNLD:
+
+                       handshake = get_handshake(dev, HANDSHAKE_DSP_BL_READY);
+
+                       if (handshake == HANDSHAKE_DSP_BL_READY) {
+                               put_handshake(dev, HANDSHAKE_DRIVER_READY);
+                       } else {
+                               Status = FAILURE;
+                       }
+
+                       uiState = STATE_BOOT_DWNLD;
+
+                       break;
+
+               case STATE_BOOT_DWNLD:
+                       handshake = get_handshake(dev, HANDSHAKE_REQUEST);
+                       if (handshake == HANDSHAKE_REQUEST) {
+                               /*
+                                * Get type associated with the request.
+                                */
+                               request = get_request_type(dev);
+                               switch (request) {
+                               case REQUEST_RUN_ADDRESS:
+                                       put_request_value(dev,
+                                                         loader_code_address);
+                                       break;
+                               case REQUEST_CODE_LENGTH:
+                                       put_request_value(dev,
+                                                         loader_code_size);
+                                       break;
+                               case REQUEST_DONE_BL:
+                                       /* Reposition ptrs to beginning of code section */
+                                       pUsFile = (USHORT *) ((long)pBootEnd);
+                                       pUcFile = (UCHAR *) ((long)pBootEnd);
+                                       uiState = STATE_CODE_DWNLD;
+                                       break;
+                               case REQUEST_CODE_SEGMENT:
+                                       word_length = get_request_value(dev);
+                                       if (word_length > MAX_LENGTH) {
+                                               Status = FAILURE;
+                                               break;
+                                       }
+                                       if ((word_length * 2 + (long)pUcFile) >
+                                               (long)pBootEnd) {
+                                               /*
+                                                * Error, beyond boot code range.
+                                                */
+                                               Status = FAILURE;
+                                               break;
+                                       }
+                                       // Provide mutual exclusive access while reading ASIC registers.
+                                       spin_lock_irqsave(&info->dpram_lock,
+                                                         flags);
+                                       if (file_version == 5) {
+                                               /*
+                                                * Position ASIC DPRAM auto-increment pointer.
+                                                */
+                                               ft1000_write_reg(dev,
+                                                                FT1000_REG_DPRAM_ADDR,
+                                                                DWNLD_PS_HDR_LOC);
+
+                                               for (; word_length > 0; word_length--) {        /* In words */
+                                                       //temp = *pUsFile;
+                                                       //temp = RtlUshortByteSwap(temp);
+                                                       ft1000_write_reg(dev,
+                                                                        FT1000_REG_DPRAM_DATA,
+                                                                        *pUsFile);
+                                                       pUsFile++;
+                                                       pUcFile += 2;
+                                                       DspWordCnt++;
+                                               }
+                                       } else {
+                                               /*
+                                                * Position ASIC DPRAM auto-increment pointer.
+                                                */
+                                               outw(DWNLD_MAG_PS_HDR_LOC,
+                                                        dev->base_addr +
+                                                        FT1000_REG_DPRAM_ADDR);
+                                               if (word_length & 0x01) {
+                                                       word_length++;
+                                               }
+                                               word_length = word_length / 2;
+
+                                               for (; word_length > 0; word_length--) {        /* In words */
+                                                       templong = *pUsFile++;
+                                                       templong |=
+                                                               (*pUsFile++ << 16);
+                                                       pUcFile += 4;
+                                                       outl(templong,
+                                                                dev->base_addr +
+                                                                FT1000_REG_MAG_DPDATAL);
+                                               }
+                                       }
+                                       spin_unlock_irqrestore(&info->
+                                                                  dpram_lock,
+                                                                  flags);
+                                       break;
+                               default:
+                                       Status = FAILURE;
+                                       break;
+                               }
+                               put_handshake(dev, HANDSHAKE_RESPONSE);
+                       } else {
+                               Status = FAILURE;
+                       }
+
+                       break;
+
+               case STATE_CODE_DWNLD:
+                       handshake = get_handshake(dev, HANDSHAKE_REQUEST);
+                       if (handshake == HANDSHAKE_REQUEST) {
+                               /*
+                                * Get type associated with the request.
+                                */
+                               request = get_request_type(dev);
+                               switch (request) {
+                               case REQUEST_FILE_CHECKSUM:
+                                       DEBUG(0,
+                                                 "ft1000_dnld: REQUEST_FOR_CHECKSUM\n");
+                                       put_request_value(dev, image_chksum);
+                                       break;
+                               case REQUEST_RUN_ADDRESS:
+                                       if (bGoodVersion) {
+                                               put_request_value(dev,
+                                                                 run_address);
+                                       } else {
+                                               Status = FAILURE;
+                                               break;
+                                       }
+                                       break;
+                               case REQUEST_CODE_LENGTH:
+                                       if (bGoodVersion) {
+                                               put_request_value(dev,
+                                                                 run_size);
+                                       } else {
+                                               Status = FAILURE;
+                                               break;
+                                       }
+                                       break;
+                               case REQUEST_DONE_CL:
+                                       /* Reposition ptrs to beginning of provisioning section */
+                                       switch (file_version) {
+                                       case 5:
+                                       case 6:
+                                               pUsFile =
+                                                       (USHORT *) ((long)pFileStart
+                                                               +
+                                                               pFileHdr5->
+                                                               commands_offset);
+                                               pUcFile =
+                                                       (UCHAR *) ((long)pFileStart
+                                                                  +
+                                                                  pFileHdr5->
+                                                                  commands_offset);
+                                               break;
+                                       default:
+                                               Status = FAILURE;
+                                               break;
+                                       }
+                                       uiState = STATE_DONE_DWNLD;
+                                       break;
+                               case REQUEST_CODE_SEGMENT:
+                                       if (!bGoodVersion) {
+                                               Status = FAILURE;
+                                               break;
+                                       }
+                                       word_length = get_request_value(dev);
+                                       if (word_length > MAX_LENGTH) {
+                                               Status = FAILURE;
+                                               break;
+                                       }
+                                       if ((word_length * 2 + (long)pUcFile) >
+                                               (long)pCodeEnd) {
+                                               /*
+                                                * Error, beyond boot code range.
+                                                */
+                                               Status = FAILURE;
+                                               break;
+                                       }
+                                       if (file_version == 5) {
+                                               /*
+                                                * Position ASIC DPRAM auto-increment pointer.
+                                                */
+                                               ft1000_write_reg(dev,
+                                                                FT1000_REG_DPRAM_ADDR,
+                                                                DWNLD_PS_HDR_LOC);
+
+                                               for (; word_length > 0; word_length--) {        /* In words */
+                                                       //temp = *pUsFile;
+                                                       //temp = RtlUshortByteSwap(temp);
+                                                       ft1000_write_reg(dev,
+                                                                        FT1000_REG_DPRAM_DATA,
+                                                                        *pUsFile);
+                                                       pUsFile++;
+                                                       pUcFile += 2;
+                                                       DspWordCnt++;
+                                               }
+                                       } else {
+                                               /*
+                                                * Position ASIC DPRAM auto-increment pointer.
+                                                */
+                                               outw(DWNLD_MAG_PS_HDR_LOC,
+                                                        dev->base_addr +
+                                                        FT1000_REG_DPRAM_ADDR);
+                                               if (word_length & 0x01) {
+                                                       word_length++;
+                                               }
+                                               word_length = word_length / 2;
+
+                                               for (; word_length > 0; word_length--) {        /* In words */
+                                                       templong = *pUsFile++;
+                                                       templong |=
+                                                               (*pUsFile++ << 16);
+                                                       pUcFile += 4;
+                                                       outl(templong,
+                                                                dev->base_addr +
+                                                                FT1000_REG_MAG_DPDATAL);
+                                               }
+                                       }
+                                       break;
+
+                               case REQUEST_MAILBOX_DATA:
+                                       // Convert length from byte count to word count. Make sure we round up.
+                                       word_length =
+                                               (long)(info->DSPInfoBlklen + 1) / 2;
+                                       put_request_value(dev, word_length);
+                                       pMailBoxData =
+                                               (PDRVMSG) & info->DSPInfoBlk[0];
+                                       pUsData =
+                                               (USHORT *) & pMailBoxData->data[0];
+                                       // Provide mutual exclusive access while reading ASIC registers.
+                                       spin_lock_irqsave(&info->dpram_lock,
+                                                         flags);
+                                       if (file_version == 5) {
+                                               /*
+                                                * Position ASIC DPRAM auto-increment pointer.
+                                                */
+                                               ft1000_write_reg(dev,
+                                                                FT1000_REG_DPRAM_ADDR,
+                                                                DWNLD_PS_HDR_LOC);
+
+                                               for (; word_length > 0; word_length--) {        /* In words */
+                                                       temp = ntohs(*pUsData);
+                                                       ft1000_write_reg(dev,
+                                                                        FT1000_REG_DPRAM_DATA,
+                                                                        temp);
+                                                       pUsData++;
+                                               }
+                                       } else {
+                                               /*
+                                                * Position ASIC DPRAM auto-increment pointer.
+                                                */
+                                               outw(DWNLD_MAG_PS_HDR_LOC,
+                                                        dev->base_addr +
+                                                        FT1000_REG_DPRAM_ADDR);
+                                               if (word_length & 0x01) {
+                                                       word_length++;
+                                               }
+                                               word_length = word_length / 2;
+
+                                               for (; word_length > 0; word_length--) {        /* In words */
+                                                       templong = *pUsData++;
+                                                       templong |=
+                                                               (*pUsData++ << 16);
+                                                       outl(templong,
+                                                                dev->base_addr +
+                                                                FT1000_REG_MAG_DPDATAL);
+                                               }
+                                       }
+                                       spin_unlock_irqrestore(&info->
+                                                                  dpram_lock,
+                                                                  flags);
+                                       break;
+
+                               case REQUEST_VERSION_INFO:
+                                       word_length =
+                                               pFileHdr5->version_data_size;
+                                       put_request_value(dev, word_length);
+                                       pUsFile =
+                                               (USHORT *) ((long)pFileStart +
+                                                       pFileHdr5->
+                                                       version_data_offset);
+                                       // Provide mutual exclusive access while reading ASIC registers.
+                                       spin_lock_irqsave(&info->dpram_lock,
+                                                         flags);
+                                       if (file_version == 5) {
+                                               /*
+                                                * Position ASIC DPRAM auto-increment pointer.
+                                                */
+                                               ft1000_write_reg(dev,
+                                                                FT1000_REG_DPRAM_ADDR,
+                                                                DWNLD_PS_HDR_LOC);
+
+                                               for (; word_length > 0; word_length--) {        /* In words */
+                                                       ft1000_write_reg(dev,
+                                                                        FT1000_REG_DPRAM_DATA,
+                                                                        *pUsFile
+                                                                        /*temp */
+                                                               );
+                                                       pUsFile++;
+                                               }
+                                       } else {
+                                               /*
+                                                * Position ASIC DPRAM auto-increment pointer.
+                                                */
+                                               outw(DWNLD_MAG_PS_HDR_LOC,
+                                                        dev->base_addr +
+                                                        FT1000_REG_DPRAM_ADDR);
+                                               if (word_length & 0x01) {
+                                                       word_length++;
+                                               }
+                                               word_length = word_length / 2;
+
+                                               for (; word_length > 0; word_length--) {        /* In words */
+                                                       templong =
+                                                               ntohs(*pUsFile++);
+                                                       temp =
+                                                               ntohs(*pUsFile++);
+                                                       templong |=
+                                                               (temp << 16);
+                                                       outl(templong,
+                                                                dev->base_addr +
+                                                                FT1000_REG_MAG_DPDATAL);
+                                               }
+                                       }
+                                       spin_unlock_irqrestore(&info->
+                                                                  dpram_lock,
+                                                                  flags);
+                                       break;
+
+                               case REQUEST_CODE_BY_VERSION:
+                                       bGoodVersion = FALSE;
+                                       requested_version =
+                                               get_request_value(dev);
+                                       if (file_version == 5) {
+                                               pDspImageInfo =
+                                                       (PDSP_IMAGE_INFO) ((long)
+                                                                          pFileStart
+                                                                          +
+                                                                          sizeof
+                                                                          (DSP_FILE_HDR_5));
+                                               for (imageN = 0;
+                                                        imageN <
+                                                        pFileHdr5->nDspImages;
+                                                        imageN++) {
+                                                       if (pDspImageInfo->
+                                                               version ==
+                                                               requested_version) {
+                                                               bGoodVersion =
+                                                                       TRUE;
+                                                               pUsFile =
+                                                                       (USHORT
+                                                                        *) ((long)
+                                                                        pFileStart
+                                                                        +
+                                                                        pDspImageInfo->
+                                                                        begin_offset);
+                                                               pUcFile =
+                                                                       (UCHAR
+                                                                        *) ((long)
+                                                                        pFileStart
+                                                                        +
+                                                                        pDspImageInfo->
+                                                                        begin_offset);
+                                                               pCodeEnd =
+                                                                       (UCHAR
+                                                                        *) ((long)
+                                                                        pFileStart
+                                                                        +
+                                                                        pDspImageInfo->
+                                                                        end_offset);
+                                                               run_address =
+                                                                       pDspImageInfo->
+                                                                       run_address;
+                                                               run_size =
+                                                                       pDspImageInfo->
+                                                                       image_size;
+                                                               break;
+                                                       }
+                                                       pDspImageInfo++;
+                                               }
+                                       } else {
+                                               pDspImageInfoV6 =
+                                                       (PDSP_IMAGE_INFO_V6) ((long)
+                                                                         pFileStart
+                                                                         +
+                                                                         sizeof
+                                                                         (DSP_FILE_HDR_5));
+                                               for (imageN = 0;
+                                                        imageN <
+                                                        pFileHdr5->nDspImages;
+                                                        imageN++) {
+                                                       temp = (USHORT)
+                                                               (pDspImageInfoV6->
+                                                                version);
+                                                       templong = temp;
+                                                       temp = (USHORT)
+                                                               (pDspImageInfoV6->
+                                                                version >> 16);
+                                                       templong |=
+                                                               (temp << 16);
+                                                       if (templong ==
+                                                               requested_version) {
+                                                               bGoodVersion =
+                                                                       TRUE;
+                                                               pUsFile =
+                                                                       (USHORT
+                                                                        *) ((long)
+                                                                        pFileStart
+                                                                        +
+                                                                        pDspImageInfoV6->
+                                                                        begin_offset);
+                                                               pUcFile =
+                                                                       (UCHAR
+                                                                        *) ((long)
+                                                                        pFileStart
+                                                                        +
+                                                                        pDspImageInfoV6->
+                                                                        begin_offset);
+                                                               pCodeEnd =
+                                                                       (UCHAR
+                                                                        *) ((long)
+                                                                        pFileStart
+                                                                        +
+                                                                        pDspImageInfoV6->
+                                                                        end_offset);
+                                                               run_address =
+                                                                       pDspImageInfoV6->
+                                                                       run_address;
+                                                               run_size =
+                                                                       pDspImageInfoV6->
+                                                                       image_size;
+                                                               image_chksum =
+                                                                       (ULONG)
+                                                                       pDspImageInfoV6->
+                                                                       checksum;
+                                                               DEBUG(0,
+                                                                         "ft1000_dnld: image_chksum = 0x%8x\n",
+                                                                         (unsigned
+                                                                          int)
+                                                                         image_chksum);
+                                                               break;
+                                                       }
+                                                       pDspImageInfoV6++;
+                                               }
+                                       }
+                                       if (!bGoodVersion) {
+                                               /*
+                                                * Error, beyond boot code range.
+                                                */
+                                               Status = FAILURE;
+                                               break;
+                                       }
+                                       break;
+
+                               default:
+                                       Status = FAILURE;
+                                       break;
+                               }
+                               put_handshake(dev, HANDSHAKE_RESPONSE);
+                       } else {
+                               Status = FAILURE;
+                       }
+
+                       break;
+
+               case STATE_DONE_DWNLD:
+                       if (((UINT) (pUcFile) - (UINT) pFileStart) >=
+                               (UINT) FileLength) {
+                               uiState = STATE_DONE_FILE;
+                               break;
+                       }
+
+                       pHdr = (PPSEUDO_HDR) pUsFile;
+
+                       if (pHdr->portdest == 0x80      /* DspOAM */
+                               && (pHdr->portsrc == 0x00       /* Driver */
+                               || pHdr->portsrc == 0x10 /* FMM */ )) {
+                               uiState = STATE_SECTION_PROV;
+                       } else {
+                               DEBUG(1,
+                                         "FT1000:download:Download error: Bad Port IDs in Pseudo Record\n");
+                               DEBUG(1, "\t Port Source = 0x%2.2x\n",
+                                         pHdr->portsrc);
+                               DEBUG(1, "\t Port Destination = 0x%2.2x\n",
+                                         pHdr->portdest);
+                               Status = FAILURE;
+                       }
+
+                       break;
+
+               case STATE_SECTION_PROV:
+
+                       pHdr = (PPSEUDO_HDR) pUcFile;
+
+                       if (pHdr->checksum == hdr_checksum(pHdr)) {
+                               if (pHdr->portdest != 0x80 /* Dsp OAM */ ) {
+                                       uiState = STATE_DONE_PROV;
+                                       break;
+                               }
+                               usHdrLength = ntohs(pHdr->length);      /* Byte length for PROV records */
+
+                               // Get buffer for provisioning data
+                               pbuffer =
+                                       kmalloc((usHdrLength + sizeof(PSEUDO_HDR)),
+                                               GFP_ATOMIC);
+                               if (pbuffer) {
+                                       memcpy(pbuffer, (void *)pUcFile,
+                                                  (UINT) (usHdrLength +
+                                                          sizeof(PSEUDO_HDR)));
+                                       // link provisioning data
+                                       pprov_record =
+                                               kmalloc(sizeof(PROV_RECORD),
+                                                       GFP_ATOMIC);
+                                       if (pprov_record) {
+                                               pprov_record->pprov_data =
+                                                       pbuffer;
+                                               list_add_tail(&pprov_record->
+                                                                 list,
+                                                                 &info->prov_list);
+                                               // Move to next entry if available
+                                               pUcFile =
+                                                       (UCHAR *) ((UINT) pUcFile +
+                                                                  (UINT) ((usHdrLength + 1) & 0xFFFFFFFE) + sizeof(PSEUDO_HDR));
+                                               if ((UINT) (pUcFile) -
+                                                       (UINT) (pFileStart) >=
+                                                       (UINT) FileLength) {
+                                                       uiState =
+                                                               STATE_DONE_FILE;
+                                               }
+                                       } else {
+                                               kfree(pbuffer);
+                                               Status = FAILURE;
+                                       }
+                               } else {
+                                       Status = FAILURE;
+                               }
+                       } else {
+                               /* Checksum did not compute */
+                               Status = FAILURE;
+                       }
+
+                       break;
+
+               case STATE_DONE_PROV:
+                       uiState = STATE_DONE_FILE;
+                       break;
+
+               default:
+                       Status = FAILURE;
+                       break;
+               }               /* End Switch */
+
+       }                       /* End while */
+
+       return Status;
+
+}