blob: 83683e9a145137cf625549d0926426e0a4b8d229 [file] [log] [blame]
Marek Beliskof7c1be02010-09-22 07:56:27 +02001/*---------------------------------------------------------------------------
Joe Perchesecdd21c2014-11-03 16:25:43 -08002 FT1000 driver for Flarion Flash OFDM NIC Device
Greg Kroah-Hartmanbf3146c2010-09-22 08:34:49 -07003
Joe Perchesecdd21c2014-11-03 16:25:43 -08004 Copyright (C) 2002 Flarion Technologies, All rights reserved.
Greg Kroah-Hartmanbf3146c2010-09-22 08:34:49 -07005
Joe Perchesecdd21c2014-11-03 16:25:43 -08006 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.
Marek Beliskof7c1be02010-09-22 07:56:27 +020016 --------------------------------------------------------------------------
17
Joe Perchesecdd21c2014-11-03 16:25:43 -080018 Description: This module will handshake with the DSP bootloader to
19 download the DSP runtime image.
Greg Kroah-Hartmanbf3146c2010-09-22 08:34:49 -070020
Joe Perchesecdd21c2014-11-03 16:25:43 -080021 ---------------------------------------------------------------------------*/
Marek Beliskof7c1be02010-09-22 07:56:27 +020022
Joe Perchesb5d82042014-11-03 16:25:44 -080023#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
24
Marek Beliskof7c1be02010-09-22 07:56:27 +020025#define __KERNEL_SYSCALLS__
26
27#include <linux/module.h>
28#include <linux/fs.h>
29#include <linux/mm.h>
30#include <linux/slab.h>
31#include <linux/unistd.h>
32#include <linux/netdevice.h>
33#include <linux/timer.h>
34#include <linux/delay.h>
Gulsah Kose56a28392014-09-26 22:55:59 +030035#include <linux/io.h>
36#include <linux/uaccess.h>
Marek Beliskof7c1be02010-09-22 07:56:27 +020037#include <linux/vmalloc.h>
38
Marek Beliskof7c1be02010-09-22 07:56:27 +020039#include "ft1000.h"
40#include "boot.h"
41
Marek Beliskof7c1be02010-09-22 07:56:27 +020042#define MAX_DSP_WAIT_LOOPS 100
43#define DSP_WAIT_SLEEP_TIME 1 /* 1 millisecond */
44
45#define MAX_LENGTH 0x7f0
46
47#define DWNLD_MAG_HANDSHAKE_LOC 0x00
48#define DWNLD_MAG_TYPE_LOC 0x01
49#define DWNLD_MAG_SIZE_LOC 0x02
50#define DWNLD_MAG_PS_HDR_LOC 0x03
51
52#define DWNLD_HANDSHAKE_LOC 0x02
53#define DWNLD_TYPE_LOC 0x04
54#define DWNLD_SIZE_MSW_LOC 0x06
55#define DWNLD_SIZE_LSW_LOC 0x08
56#define DWNLD_PS_HDR_LOC 0x0A
57
58#define HANDSHAKE_TIMEOUT_VALUE 0xF1F1
59#define HANDSHAKE_RESET_VALUE 0xFEFE /* When DSP requests startover */
60#define HANDSHAKE_DSP_BL_READY 0xFEFE /* At start DSP writes this when bootloader ready */
61#define HANDSHAKE_DRIVER_READY 0xFFFF /* Driver writes after receiving 0xFEFE */
62#define HANDSHAKE_SEND_DATA 0x0000 /* DSP writes this when ready for more data */
63
64#define HANDSHAKE_REQUEST 0x0001 /* Request from DSP */
65#define HANDSHAKE_RESPONSE 0x0000 /* Satisfied DSP request */
66
67#define REQUEST_CODE_LENGTH 0x0000
68#define REQUEST_RUN_ADDRESS 0x0001
69#define REQUEST_CODE_SEGMENT 0x0002 /* In WORD count */
70#define REQUEST_DONE_BL 0x0003
71#define REQUEST_DONE_CL 0x0004
72#define REQUEST_VERSION_INFO 0x0005
73#define REQUEST_CODE_BY_VERSION 0x0006
74#define REQUEST_MAILBOX_DATA 0x0007
75#define REQUEST_FILE_CHECKSUM 0x0008
76
77#define STATE_START_DWNLD 0x01
78#define STATE_BOOT_DWNLD 0x02
79#define STATE_CODE_DWNLD 0x03
80#define STATE_DONE_DWNLD 0x04
81#define STATE_SECTION_PROV 0x05
82#define STATE_DONE_PROV 0x06
83#define STATE_DONE_FILE 0x07
84
Ondrej Zaryca145272011-07-01 00:03:47 +020085u16 get_handshake(struct net_device *dev, u16 expected_value);
86void put_handshake(struct net_device *dev, u16 handshake_value);
87u16 get_request_type(struct net_device *dev);
Marek Beliskof7c1be02010-09-22 07:56:27 +020088long get_request_value(struct net_device *dev);
89void put_request_value(struct net_device *dev, long lvalue);
Ondrej Zaryca145272011-07-01 00:03:47 +020090u16 hdr_checksum(struct pseudo_hdr *pHdr);
Marek Beliskof7c1be02010-09-22 07:56:27 +020091
Ondrej Zary8e0fd2c2011-07-01 00:03:45 +020092struct dsp_file_hdr {
Devendra Nagaad139632012-09-16 13:49:40 -040093 u32 version_id; /* Version ID of this image format. */
94 u32 package_id; /* Package ID of code release. */
95 u32 build_date; /* Date/time stamp when file was built. */
96 u32 commands_offset; /* Offset to attached commands in Pseudo Hdr format. */
97 u32 loader_offset; /* Offset to bootloader code. */
98 u32 loader_code_address; /* Start address of bootloader. */
99 u32 loader_code_end; /* Where bootloader code ends. */
pixo14e5d8e2011-03-23 08:08:08 +0100100 u32 loader_code_size;
Devendra Nagaad139632012-09-16 13:49:40 -0400101 u32 version_data_offset; /* Offset were scrambled version data begins. */
102 u32 version_data_size; /* Size, in words, of scrambled version data. */
103 u32 nDspImages; /* Number of DSP images in file. */
Geoff Darst0a005d52014-11-29 13:17:22 -0700104} __packed;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200105
Ondrej Zary8e0fd2c2011-07-01 00:03:45 +0200106struct dsp_image_info {
Devendra Nagaad139632012-09-16 13:49:40 -0400107 u32 coff_date; /* Date/time when DSP Coff image was built. */
108 u32 begin_offset; /* Offset in file where image begins. */
109 u32 end_offset; /* Offset in file where image begins. */
110 u32 run_address; /* On chip Start address of DSP code. */
111 u32 image_size; /* Size of image. */
112 u32 version; /* Embedded version # of DSP code. */
113 unsigned short checksum; /* Dsp File checksum */
Marek Beliskof7c1be02010-09-22 07:56:27 +0200114 unsigned short pad1;
Geoff Darst0a005d52014-11-29 13:17:22 -0700115} __packed;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200116
117void card_bootload(struct net_device *dev)
118{
Julia Lawalla4820ac2015-03-29 14:54:14 +0200119 struct ft1000_info *info = netdev_priv(dev);
Marek Beliskof7c1be02010-09-22 07:56:27 +0200120 unsigned long flags;
Ondrej Zaryca145272011-07-01 00:03:47 +0200121 u32 *pdata;
122 u32 size;
123 u32 i;
124 u32 templong;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200125
Catalina Mocanu8f3a2e02014-09-22 15:14:18 -0700126 netdev_dbg(dev, "card_bootload is called\n");
Marek Beliskof7c1be02010-09-22 07:56:27 +0200127
Joe Perchesecdd21c2014-11-03 16:25:43 -0800128 pdata = (u32 *)bootimage;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200129 size = sizeof(bootimage);
130
Dylan Socolobsky86714382013-05-13 19:47:33 -0300131 /* check for odd word */
Dylan Socolobsky6fb3c2b2013-05-13 19:42:28 -0300132 if (size & 0x0003)
Marek Beliskof7c1be02010-09-22 07:56:27 +0200133 size += 4;
Dylan Socolobsky6fb3c2b2013-05-13 19:42:28 -0300134
Dylan Socolobsky86714382013-05-13 19:47:33 -0300135 /* Provide mutual exclusive access while reading ASIC registers. */
Marek Beliskof7c1be02010-09-22 07:56:27 +0200136 spin_lock_irqsave(&info->dpram_lock, flags);
137
Dylan Socolobsky86714382013-05-13 19:47:33 -0300138 /* need to set i/o base address initially and hardware will autoincrement */
Marek Beliskof7c1be02010-09-22 07:56:27 +0200139 ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, FT1000_DPRAM_BASE);
Dylan Socolobsky86714382013-05-13 19:47:33 -0300140 /* write bytes */
Marek Beliskof7c1be02010-09-22 07:56:27 +0200141 for (i = 0; i < (size >> 2); i++) {
142 templong = *pdata++;
143 outl(templong, dev->base_addr + FT1000_REG_MAG_DPDATA);
144 }
145
146 spin_unlock_irqrestore(&info->dpram_lock, flags);
147}
148
Ondrej Zaryca145272011-07-01 00:03:47 +0200149u16 get_handshake(struct net_device *dev, u16 expected_value)
Marek Beliskof7c1be02010-09-22 07:56:27 +0200150{
Julia Lawalla4820ac2015-03-29 14:54:14 +0200151 struct ft1000_info *info = netdev_priv(dev);
Ondrej Zaryca145272011-07-01 00:03:47 +0200152 u16 handshake;
153 u32 tempx;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200154 int loopcnt;
155
156 loopcnt = 0;
157 while (loopcnt < MAX_DSP_WAIT_LOOPS) {
158 if (info->AsicID == ELECTRABUZZ_ID) {
159 ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
160 DWNLD_HANDSHAKE_LOC);
161
162 handshake = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
163 } else {
164 tempx =
165 ntohl(ft1000_read_dpram_mag_32
Joe Perchesecdd21c2014-11-03 16:25:43 -0800166 (dev, DWNLD_MAG_HANDSHAKE_LOC));
167 handshake = (u16)tempx;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200168 }
169
170 if ((handshake == expected_value)
Joe Perchesecdd21c2014-11-03 16:25:43 -0800171 || (handshake == HANDSHAKE_RESET_VALUE)) {
Marek Beliskof7c1be02010-09-22 07:56:27 +0200172 return handshake;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200173 }
Gulsah Kose96bcbea2014-09-26 22:49:26 +0300174 loopcnt++;
175 mdelay(DSP_WAIT_SLEEP_TIME);
Marek Beliskof7c1be02010-09-22 07:56:27 +0200176
177 }
178
179 return HANDSHAKE_TIMEOUT_VALUE;
180
181}
182
Ondrej Zaryca145272011-07-01 00:03:47 +0200183void put_handshake(struct net_device *dev, u16 handshake_value)
Marek Beliskof7c1be02010-09-22 07:56:27 +0200184{
Julia Lawalla4820ac2015-03-29 14:54:14 +0200185 struct ft1000_info *info = netdev_priv(dev);
Ondrej Zaryca145272011-07-01 00:03:47 +0200186 u32 tempx;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200187
188 if (info->AsicID == ELECTRABUZZ_ID) {
189 ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
190 DWNLD_HANDSHAKE_LOC);
191 ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, handshake_value); /* Handshake */
192 } else {
Joe Perchesecdd21c2014-11-03 16:25:43 -0800193 tempx = (u32)handshake_value;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200194 tempx = ntohl(tempx);
195 ft1000_write_dpram_mag_32(dev, DWNLD_MAG_HANDSHAKE_LOC, tempx); /* Handshake */
196 }
197}
198
Ondrej Zaryca145272011-07-01 00:03:47 +0200199u16 get_request_type(struct net_device *dev)
Marek Beliskof7c1be02010-09-22 07:56:27 +0200200{
Julia Lawalla4820ac2015-03-29 14:54:14 +0200201 struct ft1000_info *info = netdev_priv(dev);
Ondrej Zaryca145272011-07-01 00:03:47 +0200202 u16 request_type;
203 u32 tempx;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200204
205 if (info->AsicID == ELECTRABUZZ_ID) {
206 ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, DWNLD_TYPE_LOC);
207 request_type = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
208 } else {
209 tempx = ft1000_read_dpram_mag_32(dev, DWNLD_MAG_TYPE_LOC);
210 tempx = ntohl(tempx);
Joe Perchesecdd21c2014-11-03 16:25:43 -0800211 request_type = (u16)tempx;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200212 }
213
214 return request_type;
215
216}
217
218long get_request_value(struct net_device *dev)
219{
Julia Lawalla4820ac2015-03-29 14:54:14 +0200220 struct ft1000_info *info = netdev_priv(dev);
Marek Beliskof7c1be02010-09-22 07:56:27 +0200221 long value;
Ondrej Zaryca145272011-07-01 00:03:47 +0200222 u16 w_val;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200223
224 if (info->AsicID == ELECTRABUZZ_ID) {
225 ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
226 DWNLD_SIZE_MSW_LOC);
227
228 w_val = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
229
230 value = (long)(w_val << 16);
231
232 ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
233 DWNLD_SIZE_LSW_LOC);
234
235 w_val = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
236
237 value = (long)(value | w_val);
238 } else {
239 value = ft1000_read_dpram_mag_32(dev, DWNLD_MAG_SIZE_LOC);
240 value = ntohl(value);
241 }
242
243 return value;
244
245}
246
247void put_request_value(struct net_device *dev, long lvalue)
248{
Julia Lawalla4820ac2015-03-29 14:54:14 +0200249 struct ft1000_info *info = netdev_priv(dev);
Ondrej Zaryca145272011-07-01 00:03:47 +0200250 u16 size;
251 u32 tempx;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200252
253 if (info->AsicID == ELECTRABUZZ_ID) {
Ondrej Zaryca145272011-07-01 00:03:47 +0200254 size = (u16) (lvalue >> 16);
Marek Beliskof7c1be02010-09-22 07:56:27 +0200255
256 ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
257 DWNLD_SIZE_MSW_LOC);
258
259 ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, size);
260
Ondrej Zaryca145272011-07-01 00:03:47 +0200261 size = (u16) (lvalue);
Marek Beliskof7c1be02010-09-22 07:56:27 +0200262
263 ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
264 DWNLD_SIZE_LSW_LOC);
265
266 ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, size);
267 } else {
268 tempx = ntohl(lvalue);
269 ft1000_write_dpram_mag_32(dev, DWNLD_MAG_SIZE_LOC, tempx); /* Handshake */
270 }
271
272}
273
Ondrej Zaryca145272011-07-01 00:03:47 +0200274u16 hdr_checksum(struct pseudo_hdr *pHdr)
Marek Beliskof7c1be02010-09-22 07:56:27 +0200275{
Joe Perchesecdd21c2014-11-03 16:25:43 -0800276 u16 *usPtr = (u16 *)pHdr;
Ondrej Zaryca145272011-07-01 00:03:47 +0200277 u16 chksum;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200278
Haneen Mohammed964308a2015-03-13 20:51:17 +0300279 chksum = (((((usPtr[0] ^ usPtr[1]) ^ usPtr[2]) ^ usPtr[3]) ^
280 usPtr[4]) ^ usPtr[5]) ^ usPtr[6];
Marek Beliskof7c1be02010-09-22 07:56:27 +0200281
282 return chksum;
283}
284
Randy Dunlapeb93ca42011-08-08 11:31:48 -0700285int card_download(struct net_device *dev, const u8 *pFileStart,
286 size_t FileLength)
Marek Beliskof7c1be02010-09-22 07:56:27 +0200287{
Julia Lawalla4820ac2015-03-29 14:54:14 +0200288 struct ft1000_info *info = netdev_priv(dev);
Marek Beliskof7c1be02010-09-22 07:56:27 +0200289 int Status = SUCCESS;
Ondrej Zaryca145272011-07-01 00:03:47 +0200290 u32 uiState;
291 u16 handshake;
Ondrej Zary2c9bf832011-07-01 00:03:34 +0200292 struct pseudo_hdr *pHdr;
Ondrej Zaryca145272011-07-01 00:03:47 +0200293 u16 usHdrLength;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200294 long word_length;
Ondrej Zaryca145272011-07-01 00:03:47 +0200295 u16 request;
296 u16 temp;
Ondrej Zary3aaf8072011-07-01 00:03:41 +0200297 struct prov_record *pprov_record;
Ondrej Zaryca145272011-07-01 00:03:47 +0200298 u8 *pbuffer;
Ondrej Zarye161a442011-07-01 00:03:57 +0200299 struct dsp_file_hdr *pFileHdr5;
300 struct dsp_image_info *pDspImageInfoV6 = NULL;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200301 long requested_version;
Rashika Kheria93b2a6c2013-10-27 19:53:36 +0530302 bool bGoodVersion = false;
Ondrej Zary8bc0d6f2011-07-01 00:03:35 +0200303 struct drv_msg *pMailBoxData;
Ondrej Zaryca145272011-07-01 00:03:47 +0200304 u16 *pUsData = NULL;
305 u16 *pUsFile = NULL;
306 u8 *pUcFile = NULL;
307 u8 *pBootEnd = NULL;
308 u8 *pCodeEnd = NULL;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200309 int imageN;
310 long file_version;
311 long loader_code_address = 0;
312 long loader_code_size = 0;
313 long run_address = 0;
314 long run_size = 0;
315 unsigned long flags;
316 unsigned long templong;
317 unsigned long image_chksum = 0;
318
Marek Beliskof7c1be02010-09-22 07:56:27 +0200319 file_version = *(long *)pFileStart;
Ondrej Zarye161a442011-07-01 00:03:57 +0200320 if (file_version != 6) {
Joe Perchesb5d82042014-11-03 16:25:44 -0800321 pr_err("unsupported firmware version %ld\n", file_version);
Ondrej Zarye161a442011-07-01 00:03:57 +0200322 Status = FAILURE;
323 }
Marek Beliskof7c1be02010-09-22 07:56:27 +0200324
325 uiState = STATE_START_DWNLD;
326
Joe Perchesecdd21c2014-11-03 16:25:43 -0800327 pFileHdr5 = (struct dsp_file_hdr *)pFileStart;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200328
Ondrej Zarye161a442011-07-01 00:03:57 +0200329 pUsFile = (u16 *) ((long)pFileStart + pFileHdr5->loader_offset);
330 pUcFile = (u8 *) ((long)pFileStart + pFileHdr5->loader_offset);
331 pBootEnd = (u8 *) ((long)pFileStart + pFileHdr5->loader_code_end);
332 loader_code_address = pFileHdr5->loader_code_address;
333 loader_code_size = pFileHdr5->loader_code_size;
334 bGoodVersion = false;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200335
336 while ((Status == SUCCESS) && (uiState != STATE_DONE_FILE)) {
337
338 switch (uiState) {
339 case STATE_START_DWNLD:
340
341 handshake = get_handshake(dev, HANDSHAKE_DSP_BL_READY);
342
Dylan Socolobsky6fb3c2b2013-05-13 19:42:28 -0300343 if (handshake == HANDSHAKE_DSP_BL_READY)
Marek Beliskof7c1be02010-09-22 07:56:27 +0200344 put_handshake(dev, HANDSHAKE_DRIVER_READY);
Dylan Socolobsky6fb3c2b2013-05-13 19:42:28 -0300345 else
Marek Beliskof7c1be02010-09-22 07:56:27 +0200346 Status = FAILURE;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200347
348 uiState = STATE_BOOT_DWNLD;
349
350 break;
351
352 case STATE_BOOT_DWNLD:
353 handshake = get_handshake(dev, HANDSHAKE_REQUEST);
354 if (handshake == HANDSHAKE_REQUEST) {
355 /*
356 * Get type associated with the request.
357 */
358 request = get_request_type(dev);
359 switch (request) {
360 case REQUEST_RUN_ADDRESS:
361 put_request_value(dev,
362 loader_code_address);
363 break;
364 case REQUEST_CODE_LENGTH:
365 put_request_value(dev,
366 loader_code_size);
367 break;
368 case REQUEST_DONE_BL:
369 /* Reposition ptrs to beginning of code section */
Ondrej Zaryca145272011-07-01 00:03:47 +0200370 pUsFile = (u16 *) ((long)pBootEnd);
371 pUcFile = (u8 *) ((long)pBootEnd);
Marek Beliskof7c1be02010-09-22 07:56:27 +0200372 uiState = STATE_CODE_DWNLD;
373 break;
374 case REQUEST_CODE_SEGMENT:
375 word_length = get_request_value(dev);
376 if (word_length > MAX_LENGTH) {
377 Status = FAILURE;
378 break;
379 }
380 if ((word_length * 2 + (long)pUcFile) >
Joe Perchesecdd21c2014-11-03 16:25:43 -0800381 (long)pBootEnd) {
Marek Beliskof7c1be02010-09-22 07:56:27 +0200382 /*
383 * Error, beyond boot code range.
384 */
385 Status = FAILURE;
386 break;
387 }
Dylan Socolobsky86714382013-05-13 19:47:33 -0300388 /* Provide mutual exclusive access while reading ASIC registers. */
Marek Beliskof7c1be02010-09-22 07:56:27 +0200389 spin_lock_irqsave(&info->dpram_lock,
390 flags);
Ondrej Zarye161a442011-07-01 00:03:57 +0200391 /*
392 * Position ASIC DPRAM auto-increment pointer.
393 */
394 outw(DWNLD_MAG_PS_HDR_LOC,
Joe Perchesecdd21c2014-11-03 16:25:43 -0800395 dev->base_addr +
396 FT1000_REG_DPRAM_ADDR);
Ondrej Zarye161a442011-07-01 00:03:57 +0200397 if (word_length & 0x01)
398 word_length++;
399 word_length = word_length / 2;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200400
Ondrej Zarye161a442011-07-01 00:03:57 +0200401 for (; word_length > 0; word_length--) { /* In words */
402 templong = *pUsFile++;
403 templong |=
404 (*pUsFile++ << 16);
405 pUcFile += 4;
406 outl(templong,
Joe Perchesecdd21c2014-11-03 16:25:43 -0800407 dev->base_addr +
408 FT1000_REG_MAG_DPDATAL);
Marek Beliskof7c1be02010-09-22 07:56:27 +0200409 }
410 spin_unlock_irqrestore(&info->
Joe Perchesecdd21c2014-11-03 16:25:43 -0800411 dpram_lock,
412 flags);
Marek Beliskof7c1be02010-09-22 07:56:27 +0200413 break;
414 default:
415 Status = FAILURE;
416 break;
417 }
418 put_handshake(dev, HANDSHAKE_RESPONSE);
419 } else {
420 Status = FAILURE;
421 }
422
423 break;
424
425 case STATE_CODE_DWNLD:
426 handshake = get_handshake(dev, HANDSHAKE_REQUEST);
427 if (handshake == HANDSHAKE_REQUEST) {
428 /*
429 * Get type associated with the request.
430 */
431 request = get_request_type(dev);
432 switch (request) {
433 case REQUEST_FILE_CHECKSUM:
Catalina Mocanu8f3a2e02014-09-22 15:14:18 -0700434 netdev_dbg(dev,
Joe Perchesecdd21c2014-11-03 16:25:43 -0800435 "ft1000_dnld: REQUEST_FOR_CHECKSUM\n");
Marek Beliskof7c1be02010-09-22 07:56:27 +0200436 put_request_value(dev, image_chksum);
437 break;
438 case REQUEST_RUN_ADDRESS:
439 if (bGoodVersion) {
440 put_request_value(dev,
441 run_address);
442 } else {
443 Status = FAILURE;
444 break;
445 }
446 break;
447 case REQUEST_CODE_LENGTH:
448 if (bGoodVersion) {
449 put_request_value(dev,
450 run_size);
451 } else {
452 Status = FAILURE;
453 break;
454 }
455 break;
456 case REQUEST_DONE_CL:
457 /* Reposition ptrs to beginning of provisioning section */
Ondrej Zarye161a442011-07-01 00:03:57 +0200458 pUsFile = (u16 *) ((long)pFileStart + pFileHdr5->commands_offset);
459 pUcFile = (u8 *) ((long)pFileStart + pFileHdr5->commands_offset);
Marek Beliskof7c1be02010-09-22 07:56:27 +0200460 uiState = STATE_DONE_DWNLD;
461 break;
462 case REQUEST_CODE_SEGMENT:
463 if (!bGoodVersion) {
464 Status = FAILURE;
465 break;
466 }
467 word_length = get_request_value(dev);
468 if (word_length > MAX_LENGTH) {
469 Status = FAILURE;
470 break;
471 }
472 if ((word_length * 2 + (long)pUcFile) >
Joe Perchesecdd21c2014-11-03 16:25:43 -0800473 (long)pCodeEnd) {
Marek Beliskof7c1be02010-09-22 07:56:27 +0200474 /*
475 * Error, beyond boot code range.
476 */
477 Status = FAILURE;
478 break;
479 }
Ondrej Zarye161a442011-07-01 00:03:57 +0200480 /*
481 * Position ASIC DPRAM auto-increment pointer.
482 */
483 outw(DWNLD_MAG_PS_HDR_LOC,
Joe Perchesecdd21c2014-11-03 16:25:43 -0800484 dev->base_addr +
485 FT1000_REG_DPRAM_ADDR);
Ondrej Zarye161a442011-07-01 00:03:57 +0200486 if (word_length & 0x01)
487 word_length++;
488 word_length = word_length / 2;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200489
Ondrej Zarye161a442011-07-01 00:03:57 +0200490 for (; word_length > 0; word_length--) { /* In words */
491 templong = *pUsFile++;
492 templong |=
493 (*pUsFile++ << 16);
494 pUcFile += 4;
495 outl(templong,
Joe Perchesecdd21c2014-11-03 16:25:43 -0800496 dev->base_addr +
497 FT1000_REG_MAG_DPDATAL);
Marek Beliskof7c1be02010-09-22 07:56:27 +0200498 }
499 break;
500
501 case REQUEST_MAILBOX_DATA:
Dylan Socolobsky86714382013-05-13 19:47:33 -0300502 /* Convert length from byte count to word count. Make sure we round up. */
Marek Beliskof7c1be02010-09-22 07:56:27 +0200503 word_length =
504 (long)(info->DSPInfoBlklen + 1) / 2;
505 put_request_value(dev, word_length);
506 pMailBoxData =
Joe Perchesecdd21c2014-11-03 16:25:43 -0800507 (struct drv_msg *)&info->DSPInfoBlk[0];
Marek Beliskof7c1be02010-09-22 07:56:27 +0200508 pUsData =
Joe Perchesecdd21c2014-11-03 16:25:43 -0800509 (u16 *)&pMailBoxData->data[0];
Dylan Socolobsky86714382013-05-13 19:47:33 -0300510 /* Provide mutual exclusive access while reading ASIC registers. */
Marek Beliskof7c1be02010-09-22 07:56:27 +0200511 spin_lock_irqsave(&info->dpram_lock,
512 flags);
513 if (file_version == 5) {
514 /*
515 * Position ASIC DPRAM auto-increment pointer.
516 */
517 ft1000_write_reg(dev,
518 FT1000_REG_DPRAM_ADDR,
519 DWNLD_PS_HDR_LOC);
520
521 for (; word_length > 0; word_length--) { /* In words */
522 temp = ntohs(*pUsData);
523 ft1000_write_reg(dev,
524 FT1000_REG_DPRAM_DATA,
525 temp);
526 pUsData++;
527 }
528 } else {
529 /*
530 * Position ASIC DPRAM auto-increment pointer.
531 */
532 outw(DWNLD_MAG_PS_HDR_LOC,
Joe Perchesecdd21c2014-11-03 16:25:43 -0800533 dev->base_addr +
534 FT1000_REG_DPRAM_ADDR);
Dylan Socolobsky6fb3c2b2013-05-13 19:42:28 -0300535 if (word_length & 0x01)
Marek Beliskof7c1be02010-09-22 07:56:27 +0200536 word_length++;
Dylan Socolobsky6fb3c2b2013-05-13 19:42:28 -0300537
Marek Beliskof7c1be02010-09-22 07:56:27 +0200538 word_length = word_length / 2;
539
540 for (; word_length > 0; word_length--) { /* In words */
541 templong = *pUsData++;
542 templong |=
543 (*pUsData++ << 16);
544 outl(templong,
Joe Perchesecdd21c2014-11-03 16:25:43 -0800545 dev->base_addr +
546 FT1000_REG_MAG_DPDATAL);
Marek Beliskof7c1be02010-09-22 07:56:27 +0200547 }
548 }
549 spin_unlock_irqrestore(&info->
Joe Perchesecdd21c2014-11-03 16:25:43 -0800550 dpram_lock,
551 flags);
Marek Beliskof7c1be02010-09-22 07:56:27 +0200552 break;
553
554 case REQUEST_VERSION_INFO:
555 word_length =
556 pFileHdr5->version_data_size;
557 put_request_value(dev, word_length);
558 pUsFile =
Ondrej Zaryca145272011-07-01 00:03:47 +0200559 (u16 *) ((long)pFileStart +
Joe Perchesecdd21c2014-11-03 16:25:43 -0800560 pFileHdr5->
561 version_data_offset);
Dylan Socolobsky86714382013-05-13 19:47:33 -0300562 /* Provide mutual exclusive access while reading ASIC registers. */
Marek Beliskof7c1be02010-09-22 07:56:27 +0200563 spin_lock_irqsave(&info->dpram_lock,
564 flags);
Ondrej Zarye161a442011-07-01 00:03:57 +0200565 /*
566 * Position ASIC DPRAM auto-increment pointer.
567 */
568 outw(DWNLD_MAG_PS_HDR_LOC,
Joe Perchesecdd21c2014-11-03 16:25:43 -0800569 dev->base_addr +
570 FT1000_REG_DPRAM_ADDR);
Ondrej Zarye161a442011-07-01 00:03:57 +0200571 if (word_length & 0x01)
572 word_length++;
573 word_length = word_length / 2;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200574
Ondrej Zarye161a442011-07-01 00:03:57 +0200575 for (; word_length > 0; word_length--) { /* In words */
576 templong =
577 ntohs(*pUsFile++);
578 temp =
579 ntohs(*pUsFile++);
580 templong |=
581 (temp << 16);
582 outl(templong,
Joe Perchesecdd21c2014-11-03 16:25:43 -0800583 dev->base_addr +
584 FT1000_REG_MAG_DPDATAL);
Marek Beliskof7c1be02010-09-22 07:56:27 +0200585 }
586 spin_unlock_irqrestore(&info->
Joe Perchesecdd21c2014-11-03 16:25:43 -0800587 dpram_lock,
588 flags);
Marek Beliskof7c1be02010-09-22 07:56:27 +0200589 break;
590
591 case REQUEST_CODE_BY_VERSION:
Ondrej Zaryca145272011-07-01 00:03:47 +0200592 bGoodVersion = false;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200593 requested_version =
594 get_request_value(dev);
Ondrej Zarye161a442011-07-01 00:03:57 +0200595 pDspImageInfoV6 =
596 (struct dsp_image_info *) ((long)
Joe Perchesecdd21c2014-11-03 16:25:43 -0800597 pFileStart
598 +
599 sizeof
600 (struct dsp_file_hdr));
Ondrej Zarye161a442011-07-01 00:03:57 +0200601 for (imageN = 0;
Joe Perchesecdd21c2014-11-03 16:25:43 -0800602 imageN <
603 pFileHdr5->nDspImages;
604 imageN++) {
Ondrej Zarye161a442011-07-01 00:03:57 +0200605 temp = (u16)
606 (pDspImageInfoV6->
607 version);
608 templong = temp;
609 temp = (u16)
610 (pDspImageInfoV6->
611 version >> 16);
612 templong |=
613 (temp << 16);
614 if (templong ==
Joe Perchesecdd21c2014-11-03 16:25:43 -0800615 requested_version) {
Ondrej Zarye161a442011-07-01 00:03:57 +0200616 bGoodVersion =
617 true;
618 pUsFile =
619 (u16
620 *) ((long)
Joe Perchesecdd21c2014-11-03 16:25:43 -0800621 pFileStart
622 +
623 pDspImageInfoV6->
624 begin_offset);
Ondrej Zarye161a442011-07-01 00:03:57 +0200625 pUcFile =
626 (u8
627 *) ((long)
Joe Perchesecdd21c2014-11-03 16:25:43 -0800628 pFileStart
629 +
630 pDspImageInfoV6->
631 begin_offset);
Ondrej Zarye161a442011-07-01 00:03:57 +0200632 pCodeEnd =
633 (u8
634 *) ((long)
Joe Perchesecdd21c2014-11-03 16:25:43 -0800635 pFileStart
636 +
637 pDspImageInfoV6->
638 end_offset);
Ondrej Zarye161a442011-07-01 00:03:57 +0200639 run_address =
640 pDspImageInfoV6->
641 run_address;
642 run_size =
643 pDspImageInfoV6->
644 image_size;
645 image_chksum =
646 (u32)
647 pDspImageInfoV6->
648 checksum;
Catalina Mocanu8f3a2e02014-09-22 15:14:18 -0700649 netdev_dbg(dev,
Joe Perchesecdd21c2014-11-03 16:25:43 -0800650 "ft1000_dnld: image_chksum = 0x%8x\n",
651 (unsigned
652 int)
653 image_chksum);
Ondrej Zarye161a442011-07-01 00:03:57 +0200654 break;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200655 }
Ondrej Zarye161a442011-07-01 00:03:57 +0200656 pDspImageInfoV6++;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200657 }
658 if (!bGoodVersion) {
659 /*
660 * Error, beyond boot code range.
661 */
662 Status = FAILURE;
663 break;
664 }
665 break;
666
667 default:
668 Status = FAILURE;
669 break;
670 }
671 put_handshake(dev, HANDSHAKE_RESPONSE);
672 } else {
673 Status = FAILURE;
674 }
675
676 break;
677
678 case STATE_DONE_DWNLD:
Joe Perchesecdd21c2014-11-03 16:25:43 -0800679 if (((unsigned long)(pUcFile) - (unsigned long) pFileStart) >=
680 (unsigned long)FileLength) {
Marek Beliskof7c1be02010-09-22 07:56:27 +0200681 uiState = STATE_DONE_FILE;
682 break;
683 }
684
Joe Perchesecdd21c2014-11-03 16:25:43 -0800685 pHdr = (struct pseudo_hdr *)pUsFile;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200686
687 if (pHdr->portdest == 0x80 /* DspOAM */
Joe Perchesecdd21c2014-11-03 16:25:43 -0800688 && (pHdr->portsrc == 0x00 /* Driver */
Aldo Iljazi2dad6e42013-06-06 22:52:12 +0300689 || pHdr->portsrc == 0x10 /* FMM */)) {
Marek Beliskof7c1be02010-09-22 07:56:27 +0200690 uiState = STATE_SECTION_PROV;
691 } else {
Catalina Mocanu8f3a2e02014-09-22 15:14:18 -0700692 netdev_dbg(dev,
Joe Perchesb5d82042014-11-03 16:25:44 -0800693 "Download error: Bad Port IDs in Pseudo Record\n");
Catalina Mocanu8f3a2e02014-09-22 15:14:18 -0700694 netdev_dbg(dev, "\t Port Source = 0x%2.2x\n",
Joe Perchesecdd21c2014-11-03 16:25:43 -0800695 pHdr->portsrc);
Catalina Mocanu8f3a2e02014-09-22 15:14:18 -0700696 netdev_dbg(dev, "\t Port Destination = 0x%2.2x\n",
Joe Perchesecdd21c2014-11-03 16:25:43 -0800697 pHdr->portdest);
Marek Beliskof7c1be02010-09-22 07:56:27 +0200698 Status = FAILURE;
699 }
700
701 break;
702
703 case STATE_SECTION_PROV:
704
Joe Perchesecdd21c2014-11-03 16:25:43 -0800705 pHdr = (struct pseudo_hdr *)pUcFile;
Marek Beliskof7c1be02010-09-22 07:56:27 +0200706
707 if (pHdr->checksum == hdr_checksum(pHdr)) {
Aldo Iljazi2dad6e42013-06-06 22:52:12 +0300708 if (pHdr->portdest != 0x80 /* Dsp OAM */) {
Marek Beliskof7c1be02010-09-22 07:56:27 +0200709 uiState = STATE_DONE_PROV;
710 break;
711 }
712 usHdrLength = ntohs(pHdr->length); /* Byte length for PROV records */
713
Dylan Socolobsky86714382013-05-13 19:47:33 -0300714 /* Get buffer for provisioning data */
Marek Beliskof7c1be02010-09-22 07:56:27 +0200715 pbuffer =
Aya Mahfouz08a02cb2015-03-10 19:00:53 +0200716 kmalloc(usHdrLength + sizeof(struct pseudo_hdr),
Marek Beliskof7c1be02010-09-22 07:56:27 +0200717 GFP_ATOMIC);
718 if (pbuffer) {
Aya Mahfouz08a02cb2015-03-10 19:00:53 +0200719 memcpy(pbuffer, pUcFile,
Joe Perchesecdd21c2014-11-03 16:25:43 -0800720 (u32) (usHdrLength +
721 sizeof(struct pseudo_hdr)));
Dylan Socolobsky86714382013-05-13 19:47:33 -0300722 /* link provisioning data */
Marek Beliskof7c1be02010-09-22 07:56:27 +0200723 pprov_record =
Ondrej Zary3aaf8072011-07-01 00:03:41 +0200724 kmalloc(sizeof(struct prov_record),
Marek Beliskof7c1be02010-09-22 07:56:27 +0200725 GFP_ATOMIC);
726 if (pprov_record) {
727 pprov_record->pprov_data =
728 pbuffer;
729 list_add_tail(&pprov_record->
Joe Perchesecdd21c2014-11-03 16:25:43 -0800730 list,
731 &info->prov_list);
Dylan Socolobsky86714382013-05-13 19:47:33 -0300732 /* Move to next entry if available */
Marek Beliskof7c1be02010-09-22 07:56:27 +0200733 pUcFile =
Joe Perchesecdd21c2014-11-03 16:25:43 -0800734 (u8 *)((unsigned long) pUcFile +
735 (unsigned long) ((usHdrLength + 1) & 0xFFFFFFFE) + sizeof(struct pseudo_hdr));
pixo14e5d8e2011-03-23 08:08:08 +0100736 if ((unsigned long) (pUcFile) -
Joe Perchesecdd21c2014-11-03 16:25:43 -0800737 (unsigned long) (pFileStart) >=
738 (unsigned long)FileLength) {
Marek Beliskof7c1be02010-09-22 07:56:27 +0200739 uiState =
740 STATE_DONE_FILE;
741 }
742 } else {
743 kfree(pbuffer);
744 Status = FAILURE;
745 }
746 } else {
747 Status = FAILURE;
748 }
749 } else {
750 /* Checksum did not compute */
751 Status = FAILURE;
752 }
753
754 break;
755
756 case STATE_DONE_PROV:
757 uiState = STATE_DONE_FILE;
758 break;
759
760 default:
761 Status = FAILURE;
762 break;
763 } /* End Switch */
764
765 } /* End while */
766
767 return Status;
768
769}