Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ft1000_dnld.c
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------
2  FT1000 driver for Flarion Flash OFDM NIC Device
3 
4  Copyright (C) 2002 Flarion Technologies, All rights reserved.
5 
6  This program is free software; you can redistribute it and/or modify it
7  under the terms of the GNU General Public License as published by the Free
8  Software Foundation; either version 2 of the License, or (at your option) any
9  later version. This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12  more details. You should have received a copy of the GNU General Public
13  License along with this program; if not, write to the
14  Free Software Foundation, Inc., 59 Temple Place -
15  Suite 330, Boston, MA 02111-1307, USA.
16  --------------------------------------------------------------------------
17 
18  Description: This module will handshake with the DSP bootloader to
19  download the DSP runtime image.
20 
21 ---------------------------------------------------------------------------*/
22 
23 #define __KERNEL_SYSCALLS__
24 
25 #include <linux/module.h>
26 #include <linux/fs.h>
27 #include <linux/mm.h>
28 #include <linux/slab.h>
29 #include <linux/unistd.h>
30 #include <linux/netdevice.h>
31 #include <linux/timer.h>
32 #include <linux/delay.h>
33 #include <asm/io.h>
34 #include <asm/uaccess.h>
35 #include <linux/vmalloc.h>
36 
37 #include "ft1000.h"
38 #include "boot.h"
39 
40 #ifdef FT_DEBUG
41 #define DEBUG(n, args...) printk(KERN_DEBUG args);
42 #else
43 #define DEBUG(n, args...)
44 #endif
45 
46 #define MAX_DSP_WAIT_LOOPS 100
47 #define DSP_WAIT_SLEEP_TIME 1 /* 1 millisecond */
48 
49 #define MAX_LENGTH 0x7f0
50 
51 #define DWNLD_MAG_HANDSHAKE_LOC 0x00
52 #define DWNLD_MAG_TYPE_LOC 0x01
53 #define DWNLD_MAG_SIZE_LOC 0x02
54 #define DWNLD_MAG_PS_HDR_LOC 0x03
55 
56 #define DWNLD_HANDSHAKE_LOC 0x02
57 #define DWNLD_TYPE_LOC 0x04
58 #define DWNLD_SIZE_MSW_LOC 0x06
59 #define DWNLD_SIZE_LSW_LOC 0x08
60 #define DWNLD_PS_HDR_LOC 0x0A
61 
62 #define HANDSHAKE_TIMEOUT_VALUE 0xF1F1
63 #define HANDSHAKE_RESET_VALUE 0xFEFE /* When DSP requests startover */
64 #define HANDSHAKE_DSP_BL_READY 0xFEFE /* At start DSP writes this when bootloader ready */
65 #define HANDSHAKE_DRIVER_READY 0xFFFF /* Driver writes after receiving 0xFEFE */
66 #define HANDSHAKE_SEND_DATA 0x0000 /* DSP writes this when ready for more data */
67 
68 #define HANDSHAKE_REQUEST 0x0001 /* Request from DSP */
69 #define HANDSHAKE_RESPONSE 0x0000 /* Satisfied DSP request */
70 
71 #define REQUEST_CODE_LENGTH 0x0000
72 #define REQUEST_RUN_ADDRESS 0x0001
73 #define REQUEST_CODE_SEGMENT 0x0002 /* In WORD count */
74 #define REQUEST_DONE_BL 0x0003
75 #define REQUEST_DONE_CL 0x0004
76 #define REQUEST_VERSION_INFO 0x0005
77 #define REQUEST_CODE_BY_VERSION 0x0006
78 #define REQUEST_MAILBOX_DATA 0x0007
79 #define REQUEST_FILE_CHECKSUM 0x0008
80 
81 #define STATE_START_DWNLD 0x01
82 #define STATE_BOOT_DWNLD 0x02
83 #define STATE_CODE_DWNLD 0x03
84 #define STATE_DONE_DWNLD 0x04
85 #define STATE_SECTION_PROV 0x05
86 #define STATE_DONE_PROV 0x06
87 #define STATE_DONE_FILE 0x07
88 
89 u16 get_handshake(struct net_device *dev, u16 expected_value);
90 void put_handshake(struct net_device *dev, u16 handshake_value);
92 long get_request_value(struct net_device *dev);
93 void put_request_value(struct net_device *dev, long lvalue);
94 u16 hdr_checksum(struct pseudo_hdr *pHdr);
95 
96 struct dsp_file_hdr {
97  u32 version_id; /* Version ID of this image format. */
98  u32 package_id; /* Package ID of code release. */
99  u32 build_date; /* Date/time stamp when file was built. */
100  u32 commands_offset; /* Offset to attached commands in Pseudo Hdr format. */
101  u32 loader_offset; /* Offset to bootloader code. */
102  u32 loader_code_address; /* Start address of bootloader. */
103  u32 loader_code_end; /* Where bootloader code ends. */
105  u32 version_data_offset; /* Offset were scrambled version data begins. */
106  u32 version_data_size; /* Size, in words, of scrambled version data. */
107  u32 nDspImages; /* Number of DSP images in file. */
108 } __attribute__ ((packed));
111  u32 coff_date; /* Date/time when DSP Coff image was built. */
112  u32 begin_offset; /* Offset in file where image begins. */
113  u32 end_offset; /* Offset in file where image begins. */
114  u32 run_address; /* On chip Start address of DSP code. */
115  u32 image_size; /* Size of image. */
116  u32 version; /* Embedded version # of DSP code. */
117  unsigned short checksum; /* Dsp File checksum */
118  unsigned short pad1;
119 } __attribute__ ((packed));
122 {
123  struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev);
124  unsigned long flags;
128  u32 templong;
129 
130  DEBUG(0, "card_bootload is called\n");
131 
132  pdata = (u32 *) bootimage;
133  size = sizeof(bootimage);
134 
135  // check for odd word
136  if (size & 0x0003) {
137  size += 4;
138  }
139  // Provide mutual exclusive access while reading ASIC registers.
140  spin_lock_irqsave(&info->dpram_lock, flags);
141 
142  // need to set i/o base address initially and hardware will autoincrement
143  ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, FT1000_DPRAM_BASE);
144  // write bytes
145  for (i = 0; i < (size >> 2); i++) {
146  templong = *pdata++;
147  outl(templong, dev->base_addr + FT1000_REG_MAG_DPDATA);
148  }
149 
150  spin_unlock_irqrestore(&info->dpram_lock, flags);
151 }
152 
153 u16 get_handshake(struct net_device *dev, u16 expected_value)
154 {
155  struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev);
156  u16 handshake;
157  u32 tempx;
158  int loopcnt;
159 
160  loopcnt = 0;
161  while (loopcnt < MAX_DSP_WAIT_LOOPS) {
162  if (info->AsicID == ELECTRABUZZ_ID) {
163  ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
165 
166  handshake = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
167  } else {
168  tempx =
170  (dev, DWNLD_MAG_HANDSHAKE_LOC));
171  handshake = (u16) tempx;
172  }
173 
174  if ((handshake == expected_value)
175  || (handshake == HANDSHAKE_RESET_VALUE)) {
176  return handshake;
177  } else {
178  loopcnt++;
180  }
181 
182  }
183 
185 
186 }
187 
188 void put_handshake(struct net_device *dev, u16 handshake_value)
189 {
190  struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev);
191  u32 tempx;
192 
193  if (info->AsicID == ELECTRABUZZ_ID) {
194  ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
196  ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, handshake_value); /* Handshake */
197  } else {
198  tempx = (u32) handshake_value;
199  tempx = ntohl(tempx);
200  ft1000_write_dpram_mag_32(dev, DWNLD_MAG_HANDSHAKE_LOC, tempx); /* Handshake */
201  }
202 }
203 
205 {
206  struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev);
208  u32 tempx;
209 
210  if (info->AsicID == ELECTRABUZZ_ID) {
211  ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR, DWNLD_TYPE_LOC);
212  request_type = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
213  } else {
215  tempx = ntohl(tempx);
216  request_type = (u16) tempx;
217  }
218 
219  return request_type;
220 
221 }
222 
223 long get_request_value(struct net_device *dev)
224 {
225  struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev);
226  long value;
227  u16 w_val;
228 
229  if (info->AsicID == ELECTRABUZZ_ID) {
230  ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
232 
233  w_val = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
234 
235  value = (long)(w_val << 16);
236 
237  ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
239 
240  w_val = ft1000_read_reg(dev, FT1000_REG_DPRAM_DATA);
241 
242  value = (long)(value | w_val);
243  } else {
245  value = ntohl(value);
246  }
247 
248  return value;
249 
250 }
251 
252 void put_request_value(struct net_device *dev, long lvalue)
253 {
254  struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev);
255  u16 size;
256  u32 tempx;
257 
258  if (info->AsicID == ELECTRABUZZ_ID) {
259  size = (u16) (lvalue >> 16);
260 
261  ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
263 
264  ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, size);
265 
266  size = (u16) (lvalue);
267 
268  ft1000_write_reg(dev, FT1000_REG_DPRAM_ADDR,
270 
271  ft1000_write_reg(dev, FT1000_REG_DPRAM_DATA, size);
272  } else {
273  tempx = ntohl(lvalue);
274  ft1000_write_dpram_mag_32(dev, DWNLD_MAG_SIZE_LOC, tempx); /* Handshake */
275  }
276 
277 }
278 
280 {
281  u16 *usPtr = (u16 *) pHdr;
282  u16 chksum;
283 
284  chksum = ((((((usPtr[0] ^ usPtr[1]) ^ usPtr[2]) ^ usPtr[3]) ^
285  usPtr[4]) ^ usPtr[5]) ^ usPtr[6]);
286 
287  return chksum;
288 }
289 
290 int card_download(struct net_device *dev, const u8 *pFileStart,
291  size_t FileLength)
292 {
293  struct ft1000_info *info = (struct ft1000_info *) netdev_priv(dev);
294  int Status = SUCCESS;
295  u32 uiState;
296  u16 handshake;
297  struct pseudo_hdr *pHdr;
298  u16 usHdrLength;
299  long word_length;
300  u16 request;
301  u16 temp;
302  struct prov_record *pprov_record;
303  u8 *pbuffer;
304  struct dsp_file_hdr *pFileHdr5;
305  struct dsp_image_info *pDspImageInfoV6 = NULL;
306  long requested_version;
307  bool bGoodVersion = 0;
308  struct drv_msg *pMailBoxData;
309  u16 *pUsData = NULL;
310  u16 *pUsFile = NULL;
311  u8 *pUcFile = NULL;
312  u8 *pBootEnd = NULL;
313  u8 *pCodeEnd = NULL;
314  int imageN;
315  long file_version;
316  long loader_code_address = 0;
317  long loader_code_size = 0;
318  long run_address = 0;
319  long run_size = 0;
320  unsigned long flags;
321  unsigned long templong;
322  unsigned long image_chksum = 0;
323 
324  file_version = *(long *)pFileStart;
325  if (file_version != 6) {
326  printk(KERN_ERR "ft1000: unsupported firmware version %ld\n", file_version);
327  Status = FAILURE;
328  }
329 
330  uiState = STATE_START_DWNLD;
331 
332  pFileHdr5 = (struct dsp_file_hdr *) pFileStart;
333 
334  pUsFile = (u16 *) ((long)pFileStart + pFileHdr5->loader_offset);
335  pUcFile = (u8 *) ((long)pFileStart + pFileHdr5->loader_offset);
336  pBootEnd = (u8 *) ((long)pFileStart + pFileHdr5->loader_code_end);
337  loader_code_address = pFileHdr5->loader_code_address;
338  loader_code_size = pFileHdr5->loader_code_size;
339  bGoodVersion = false;
340 
341  while ((Status == SUCCESS) && (uiState != STATE_DONE_FILE)) {
342 
343  switch (uiState) {
344  case STATE_START_DWNLD:
345 
346  handshake = get_handshake(dev, HANDSHAKE_DSP_BL_READY);
347 
348  if (handshake == HANDSHAKE_DSP_BL_READY) {
350  } else {
351  Status = FAILURE;
352  }
353 
354  uiState = STATE_BOOT_DWNLD;
355 
356  break;
357 
358  case STATE_BOOT_DWNLD:
359  handshake = get_handshake(dev, HANDSHAKE_REQUEST);
360  if (handshake == HANDSHAKE_REQUEST) {
361  /*
362  * Get type associated with the request.
363  */
364  request = get_request_type(dev);
365  switch (request) {
366  case REQUEST_RUN_ADDRESS:
367  put_request_value(dev,
368  loader_code_address);
369  break;
370  case REQUEST_CODE_LENGTH:
371  put_request_value(dev,
372  loader_code_size);
373  break;
374  case REQUEST_DONE_BL:
375  /* Reposition ptrs to beginning of code section */
376  pUsFile = (u16 *) ((long)pBootEnd);
377  pUcFile = (u8 *) ((long)pBootEnd);
378  uiState = STATE_CODE_DWNLD;
379  break;
381  word_length = get_request_value(dev);
382  if (word_length > MAX_LENGTH) {
383  Status = FAILURE;
384  break;
385  }
386  if ((word_length * 2 + (long)pUcFile) >
387  (long)pBootEnd) {
388  /*
389  * Error, beyond boot code range.
390  */
391  Status = FAILURE;
392  break;
393  }
394  // Provide mutual exclusive access while reading ASIC registers.
396  flags);
397  /*
398  * Position ASIC DPRAM auto-increment pointer.
399  */
401  dev->base_addr +
403  if (word_length & 0x01)
404  word_length++;
405  word_length = word_length / 2;
406 
407  for (; word_length > 0; word_length--) { /* In words */
408  templong = *pUsFile++;
409  templong |=
410  (*pUsFile++ << 16);
411  pUcFile += 4;
412  outl(templong,
413  dev->base_addr +
415  }
416  spin_unlock_irqrestore(&info->
417  dpram_lock,
418  flags);
419  break;
420  default:
421  Status = FAILURE;
422  break;
423  }
425  } else {
426  Status = FAILURE;
427  }
428 
429  break;
430 
431  case STATE_CODE_DWNLD:
432  handshake = get_handshake(dev, HANDSHAKE_REQUEST);
433  if (handshake == HANDSHAKE_REQUEST) {
434  /*
435  * Get type associated with the request.
436  */
437  request = get_request_type(dev);
438  switch (request) {
440  DEBUG(0,
441  "ft1000_dnld: REQUEST_FOR_CHECKSUM\n");
442  put_request_value(dev, image_chksum);
443  break;
444  case REQUEST_RUN_ADDRESS:
445  if (bGoodVersion) {
446  put_request_value(dev,
447  run_address);
448  } else {
449  Status = FAILURE;
450  break;
451  }
452  break;
453  case REQUEST_CODE_LENGTH:
454  if (bGoodVersion) {
455  put_request_value(dev,
456  run_size);
457  } else {
458  Status = FAILURE;
459  break;
460  }
461  break;
462  case REQUEST_DONE_CL:
463  /* Reposition ptrs to beginning of provisioning section */
464  pUsFile = (u16 *) ((long)pFileStart + pFileHdr5->commands_offset);
465  pUcFile = (u8 *) ((long)pFileStart + pFileHdr5->commands_offset);
466  uiState = STATE_DONE_DWNLD;
467  break;
469  if (!bGoodVersion) {
470  Status = FAILURE;
471  break;
472  }
473  word_length = get_request_value(dev);
474  if (word_length > MAX_LENGTH) {
475  Status = FAILURE;
476  break;
477  }
478  if ((word_length * 2 + (long)pUcFile) >
479  (long)pCodeEnd) {
480  /*
481  * Error, beyond boot code range.
482  */
483  Status = FAILURE;
484  break;
485  }
486  /*
487  * Position ASIC DPRAM auto-increment pointer.
488  */
490  dev->base_addr +
492  if (word_length & 0x01)
493  word_length++;
494  word_length = word_length / 2;
495 
496  for (; word_length > 0; word_length--) { /* In words */
497  templong = *pUsFile++;
498  templong |=
499  (*pUsFile++ << 16);
500  pUcFile += 4;
501  outl(templong,
502  dev->base_addr +
504  }
505  break;
506 
508  // Convert length from byte count to word count. Make sure we round up.
509  word_length =
510  (long)(info->DSPInfoBlklen + 1) / 2;
511  put_request_value(dev, word_length);
512  pMailBoxData =
513  (struct drv_msg *) & info->DSPInfoBlk[0];
514  pUsData =
515  (u16 *) & pMailBoxData->data[0];
516  // Provide mutual exclusive access while reading ASIC registers.
518  flags);
519  if (file_version == 5) {
520  /*
521  * Position ASIC DPRAM auto-increment pointer.
522  */
523  ft1000_write_reg(dev,
526 
527  for (; word_length > 0; word_length--) { /* In words */
528  temp = ntohs(*pUsData);
529  ft1000_write_reg(dev,
531  temp);
532  pUsData++;
533  }
534  } else {
535  /*
536  * Position ASIC DPRAM auto-increment pointer.
537  */
539  dev->base_addr +
541  if (word_length & 0x01) {
542  word_length++;
543  }
544  word_length = word_length / 2;
545 
546  for (; word_length > 0; word_length--) { /* In words */
547  templong = *pUsData++;
548  templong |=
549  (*pUsData++ << 16);
550  outl(templong,
551  dev->base_addr +
553  }
554  }
555  spin_unlock_irqrestore(&info->
556  dpram_lock,
557  flags);
558  break;
559 
561  word_length =
562  pFileHdr5->version_data_size;
563  put_request_value(dev, word_length);
564  pUsFile =
565  (u16 *) ((long)pFileStart +
566  pFileHdr5->
568  // Provide mutual exclusive access while reading ASIC registers.
570  flags);
571  /*
572  * Position ASIC DPRAM auto-increment pointer.
573  */
575  dev->base_addr +
577  if (word_length & 0x01)
578  word_length++;
579  word_length = word_length / 2;
580 
581  for (; word_length > 0; word_length--) { /* In words */
582  templong =
583  ntohs(*pUsFile++);
584  temp =
585  ntohs(*pUsFile++);
586  templong |=
587  (temp << 16);
588  outl(templong,
589  dev->base_addr +
591  }
592  spin_unlock_irqrestore(&info->
593  dpram_lock,
594  flags);
595  break;
596 
598  bGoodVersion = false;
599  requested_version =
600  get_request_value(dev);
601  pDspImageInfoV6 =
602  (struct dsp_image_info *) ((long)
603  pFileStart
604  +
605  sizeof
606  (struct dsp_file_hdr));
607  for (imageN = 0;
608  imageN <
609  pFileHdr5->nDspImages;
610  imageN++) {
611  temp = (u16)
612  (pDspImageInfoV6->
613  version);
614  templong = temp;
615  temp = (u16)
616  (pDspImageInfoV6->
617  version >> 16);
618  templong |=
619  (temp << 16);
620  if (templong ==
621  requested_version) {
622  bGoodVersion =
623  true;
624  pUsFile =
625  (u16
626  *) ((long)
627  pFileStart
628  +
629  pDspImageInfoV6->
630  begin_offset);
631  pUcFile =
632  (u8
633  *) ((long)
634  pFileStart
635  +
636  pDspImageInfoV6->
637  begin_offset);
638  pCodeEnd =
639  (u8
640  *) ((long)
641  pFileStart
642  +
643  pDspImageInfoV6->
644  end_offset);
645  run_address =
646  pDspImageInfoV6->
647  run_address;
648  run_size =
649  pDspImageInfoV6->
650  image_size;
651  image_chksum =
652  (u32)
653  pDspImageInfoV6->
654  checksum;
655  DEBUG(0,
656  "ft1000_dnld: image_chksum = 0x%8x\n",
657  (unsigned
658  int)
659  image_chksum);
660  break;
661  }
662  pDspImageInfoV6++;
663  }
664  if (!bGoodVersion) {
665  /*
666  * Error, beyond boot code range.
667  */
668  Status = FAILURE;
669  break;
670  }
671  break;
672 
673  default:
674  Status = FAILURE;
675  break;
676  }
678  } else {
679  Status = FAILURE;
680  }
681 
682  break;
683 
684  case STATE_DONE_DWNLD:
685  if (((unsigned long) (pUcFile) - (unsigned long) pFileStart) >=
686  (unsigned long) FileLength) {
687  uiState = STATE_DONE_FILE;
688  break;
689  }
690 
691  pHdr = (struct pseudo_hdr *) pUsFile;
692 
693  if (pHdr->portdest == 0x80 /* DspOAM */
694  && (pHdr->portsrc == 0x00 /* Driver */
695  || pHdr->portsrc == 0x10 /* FMM */ )) {
696  uiState = STATE_SECTION_PROV;
697  } else {
698  DEBUG(1,
699  "FT1000:download:Download error: Bad Port IDs in Pseudo Record\n");
700  DEBUG(1, "\t Port Source = 0x%2.2x\n",
701  pHdr->portsrc);
702  DEBUG(1, "\t Port Destination = 0x%2.2x\n",
703  pHdr->portdest);
704  Status = FAILURE;
705  }
706 
707  break;
708 
709  case STATE_SECTION_PROV:
710 
711  pHdr = (struct pseudo_hdr *) pUcFile;
712 
713  if (pHdr->checksum == hdr_checksum(pHdr)) {
714  if (pHdr->portdest != 0x80 /* Dsp OAM */ ) {
715  uiState = STATE_DONE_PROV;
716  break;
717  }
718  usHdrLength = ntohs(pHdr->length); /* Byte length for PROV records */
719 
720  // Get buffer for provisioning data
721  pbuffer =
722  kmalloc((usHdrLength + sizeof(struct pseudo_hdr)),
723  GFP_ATOMIC);
724  if (pbuffer) {
725  memcpy(pbuffer, (void *)pUcFile,
726  (u32) (usHdrLength +
727  sizeof(struct pseudo_hdr)));
728  // link provisioning data
729  pprov_record =
730  kmalloc(sizeof(struct prov_record),
731  GFP_ATOMIC);
732  if (pprov_record) {
733  pprov_record->pprov_data =
734  pbuffer;
735  list_add_tail(&pprov_record->
736  list,
737  &info->prov_list);
738  // Move to next entry if available
739  pUcFile =
740  (u8 *) ((unsigned long) pUcFile +
741  (unsigned long) ((usHdrLength + 1) & 0xFFFFFFFE) + sizeof(struct pseudo_hdr));
742  if ((unsigned long) (pUcFile) -
743  (unsigned long) (pFileStart) >=
744  (unsigned long) FileLength) {
745  uiState =
747  }
748  } else {
749  kfree(pbuffer);
750  Status = FAILURE;
751  }
752  } else {
753  Status = FAILURE;
754  }
755  } else {
756  /* Checksum did not compute */
757  Status = FAILURE;
758  }
759 
760  break;
761 
762  case STATE_DONE_PROV:
763  uiState = STATE_DONE_FILE;
764  break;
765 
766  default:
767  Status = FAILURE;
768  break;
769  } /* End Switch */
770 
771  } /* End while */
772 
773  return Status;
774 
775 }