2 * Hermes download helper.
5 * - is capable of writing to the volatile area of the hermes device
6 * - is currently not capable of writing to non-volatile areas
7 * - provide helpers to identify and update plugin data
8 * - is not capable of interpreting a fw image directly. That is up to
9 * the main card driver.
10 * - deals with Hermes I devices. It can probably be modified to deal
11 * with Hermes II devices
13 * Copyright (C) 2007, David Kilroy
15 * Plug data code slightly modified from spectrum_cs driver
16 * Copyright (C) 2002-2005 Pavel Roskin <proski@gnu.org>
17 * Portions based on information in wl_lkm_718 Agere driver
18 * COPYRIGHT (C) 2001-2004 by Agere Systems Inc. All Rights Reserved
20 * The contents of this file are subject to the Mozilla Public License
21 * Version 1.1 (the "License"); you may not use this file except in
22 * compliance with the License. You may obtain a copy of the License
23 * at http://www.mozilla.org/MPL/
25 * Software distributed under the License is distributed on an "AS IS"
26 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
27 * the License for the specific language governing rights and
28 * limitations under the License.
30 * Alternatively, the contents of this file may be used under the
31 * terms of the GNU General Public License version 2 (the "GPL"), in
32 * which case the provisions of the GPL are applicable instead of the
33 * above. If you wish to allow the use of your version of this file
34 * only under the terms of the GPL and not to allow others to use your
35 * version of this file under the MPL, indicate your decision by
36 * deleting the provisions above and replace them with the notice and
37 * other provisions required by the GPL. If you do not delete the
38 * provisions above, a recipient may use your version of this file
39 * under either the MPL or the GPL.
42 #include <linux/module.h>
43 #include <linux/delay.h>
45 #include "hermes_dld.h"
47 #define PFX "hermes_dld: "
50 * AUX port access. To unlock the AUX port write the access keys to the
51 * PARAM0-2 registers, then write HERMES_AUX_ENABLE to the HERMES_CONTROL
52 * register. Then read it and make sure it's HERMES_AUX_ENABLED.
54 #define HERMES_AUX_ENABLE 0x8000 /* Enable auxiliary port access */
55 #define HERMES_AUX_DISABLE 0x4000 /* Disable to auxiliary port access */
56 #define HERMES_AUX_ENABLED 0xC000 /* Auxiliary port is open */
57 #define HERMES_AUX_DISABLED 0x0000 /* Auxiliary port is closed */
59 #define HERMES_AUX_PW0 0xFE01
60 #define HERMES_AUX_PW1 0xDC23
61 #define HERMES_AUX_PW2 0xBA45
63 /* HERMES_CMD_DOWNLD */
64 #define HERMES_PROGRAM_DISABLE (0x0000 | HERMES_CMD_DOWNLD)
65 #define HERMES_PROGRAM_ENABLE_VOLATILE (0x0100 | HERMES_CMD_DOWNLD)
66 #define HERMES_PROGRAM_ENABLE_NON_VOLATILE (0x0200 | HERMES_CMD_DOWNLD)
67 #define HERMES_PROGRAM_NON_VOLATILE (0x0300 | HERMES_CMD_DOWNLD)
69 /* End markers used in dblocks */
70 #define PDI_END 0x00000000 /* End of PDA */
71 #define BLOCK_END 0xFFFFFFFF /* Last image block */
72 #define TEXT_END 0x1A /* End of text header */
74 /* Limit the amout we try to download in a single shot.
77 #define MAX_DL_SIZE 1024
78 #define LIMIT_PROGRAM_SIZE 0
81 * The following structures have little-endian fields denoted by
82 * the leading underscore. Don't access them directly - use inline
83 * functions defined below.
87 * The binary image to be downloaded consists of series of data blocks.
88 * Each block has the following structure.
91 __le32 addr
; /* adapter address where to write the block */
92 __le16 len
; /* length of the data only, in bytes */
93 char data
[0]; /* data to be written */
94 } __attribute__ ((packed
));
97 * Plug Data References are located in in the image after the last data
98 * block. They refer to areas in the adapter memory where the plug data
99 * items with matching ID should be written.
102 __le32 id
; /* record ID */
103 __le32 addr
; /* adapter address where to write the data */
104 __le32 len
; /* expected length of the data, in bytes */
105 char next
[0]; /* next PDR starts here */
106 } __attribute__ ((packed
));
109 * Plug Data Items are located in the EEPROM read from the adapter by
110 * primary firmware. They refer to the device-specific data that should
111 * be plugged into the secondary firmware.
114 __le16 len
; /* length of ID and data, in words */
115 __le16 id
; /* record ID */
116 char data
[0]; /* plug data */
117 } __attribute__ ((packed
));
119 /*** FW data block access functions ***/
122 dblock_addr(const struct dblock
*blk
)
124 return le32_to_cpu(blk
->addr
);
128 dblock_len(const struct dblock
*blk
)
130 return le16_to_cpu(blk
->len
);
133 /*** PDR Access functions ***/
136 pdr_id(const struct pdr
*pdr
)
138 return le32_to_cpu(pdr
->id
);
142 pdr_addr(const struct pdr
*pdr
)
144 return le32_to_cpu(pdr
->addr
);
148 pdr_len(const struct pdr
*pdr
)
150 return le32_to_cpu(pdr
->len
);
153 /*** PDI Access functions ***/
156 pdi_id(const struct pdi
*pdi
)
158 return le16_to_cpu(pdi
->id
);
161 /* Return length of the data only, in bytes */
163 pdi_len(const struct pdi
*pdi
)
165 return 2 * (le16_to_cpu(pdi
->len
) - 1);
168 /*** Hermes AUX control ***/
171 hermes_aux_setaddr(hermes_t
*hw
, u32 addr
)
173 hermes_write_reg(hw
, HERMES_AUXPAGE
, (u16
) (addr
>> 7));
174 hermes_write_reg(hw
, HERMES_AUXOFFSET
, (u16
) (addr
& 0x7F));
178 hermes_aux_control(hermes_t
*hw
, int enabled
)
180 int desired_state
= enabled
? HERMES_AUX_ENABLED
: HERMES_AUX_DISABLED
;
181 int action
= enabled
? HERMES_AUX_ENABLE
: HERMES_AUX_DISABLE
;
185 if (hermes_read_reg(hw
, HERMES_CONTROL
) == desired_state
)
188 hermes_write_reg(hw
, HERMES_PARAM0
, HERMES_AUX_PW0
);
189 hermes_write_reg(hw
, HERMES_PARAM1
, HERMES_AUX_PW1
);
190 hermes_write_reg(hw
, HERMES_PARAM2
, HERMES_AUX_PW2
);
191 hermes_write_reg(hw
, HERMES_CONTROL
, action
);
193 for (i
= 0; i
< 20; i
++) {
195 if (hermes_read_reg(hw
, HERMES_CONTROL
) ==
203 /*** Plug Data Functions ***/
206 * Scan PDR for the record with the specified RECORD_ID.
207 * If it's not found, return NULL.
209 static const struct pdr
*
210 hermes_find_pdr(const struct pdr
*first_pdr
, u32 record_id
, const void *end
)
212 const struct pdr
*pdr
= first_pdr
;
214 end
-= sizeof(struct pdr
);
216 while (((void *) pdr
<= end
) &&
217 (pdr_id(pdr
) != PDI_END
)) {
219 * PDR area is currently not terminated by PDI_END.
220 * It's followed by CRC records, which have the type
221 * field where PDR has length. The type can be 0 or 1.
223 if (pdr_len(pdr
) < 2)
226 /* If the record ID matches, we are done */
227 if (pdr_id(pdr
) == record_id
)
230 pdr
= (struct pdr
*) pdr
->next
;
235 /* Scan production data items for a particular entry */
236 static const struct pdi
*
237 hermes_find_pdi(const struct pdi
*first_pdi
, u32 record_id
, const void *end
)
239 const struct pdi
*pdi
= first_pdi
;
241 end
-= sizeof(struct pdi
);
243 while (((void *) pdi
<= end
) &&
244 (pdi_id(pdi
) != PDI_END
)) {
246 /* If the record ID matches, we are done */
247 if (pdi_id(pdi
) == record_id
)
250 pdi
= (struct pdi
*) &pdi
->data
[pdi_len(pdi
)];
255 /* Process one Plug Data Item - find corresponding PDR and plug it */
257 hermes_plug_pdi(hermes_t
*hw
, const struct pdr
*first_pdr
,
258 const struct pdi
*pdi
, const void *pdr_end
)
260 const struct pdr
*pdr
;
262 /* Find the PDR corresponding to this PDI */
263 pdr
= hermes_find_pdr(first_pdr
, pdi_id(pdi
), pdr_end
);
265 /* No match is found, safe to ignore */
269 /* Lengths of the data in PDI and PDR must match */
270 if (pdi_len(pdi
) != pdr_len(pdr
))
273 /* do the actual plugging */
274 hermes_aux_setaddr(hw
, pdr_addr(pdr
));
275 hermes_write_bytes(hw
, HERMES_AUXDATA
, pdi
->data
, pdi_len(pdi
));
280 /* Read PDA from the adapter */
281 int hermes_read_pda(hermes_t
*hw
,
285 int use_eeprom
) /* can we get this into hw? */
289 u16 data_len
= pda_len
;
293 /* PDA of spectrum symbol is in eeprom */
295 /* Issue command to read EEPROM */
296 ret
= hermes_docmd_wait(hw
, HERMES_CMD_READMIF
, 0, NULL
);
300 /* wl_lkm does not include PDA size in the PDA area.
301 * We will pad the information into pda, so other routines
302 * don't have to be modified */
303 pda
[0] = cpu_to_le16(pda_len
- 2);
304 /* Includes CFG_PROD_DATA but not itself */
305 pda
[1] = cpu_to_le16(0x0800); /* CFG_PROD_DATA */
306 data_len
= pda_len
- 4;
310 /* Open auxiliary port */
311 ret
= hermes_aux_control(hw
, 1);
312 pr_debug(PFX
"AUX enable returned %d\n", ret
);
316 /* read PDA from EEPROM */
317 hermes_aux_setaddr(hw
, pda_addr
);
318 hermes_read_words(hw
, HERMES_AUXDATA
, data
, data_len
/ 2);
321 ret
= hermes_aux_control(hw
, 0);
322 pr_debug(PFX
"AUX disable returned %d\n", ret
);
324 /* Check PDA length */
325 pda_size
= le16_to_cpu(pda
[0]);
326 pr_debug(PFX
"Actual PDA length %d, Max allowed %d\n",
328 if (pda_size
> pda_len
)
334 /* Parse PDA and write the records into the adapter
336 * Attempt to write every records that is in the specified pda
337 * which also has a valid production data record for the firmware.
339 int hermes_apply_pda(hermes_t
*hw
,
340 const char *first_pdr
,
346 const struct pdi
*pdi
;
347 const struct pdr
*pdr
;
349 pdr
= (const struct pdr
*) first_pdr
;
350 pda_end
-= sizeof(struct pdi
);
352 /* Go through every PDI and plug them into the adapter */
353 pdi
= (const struct pdi
*) (pda
+ 2);
354 while (((void *) pdi
<= pda_end
) &&
355 (pdi_id(pdi
) != PDI_END
)) {
356 ret
= hermes_plug_pdi(hw
, pdr
, pdi
, pdr_end
);
360 /* Increment to the next PDI */
361 pdi
= (const struct pdi
*) &pdi
->data
[pdi_len(pdi
)];
366 /* Identify the total number of bytes in all blocks
367 * including the header data.
370 hermes_blocks_length(const char *first_block
, const void *end
)
372 const struct dblock
*blk
= (const struct dblock
*) first_block
;
378 /* Skip all blocks to locate Plug Data References
380 while (((void *) blk
<= end
) &&
381 (dblock_addr(blk
) != BLOCK_END
)) {
382 len
= dblock_len(blk
);
383 total_len
+= sizeof(*blk
) + len
;
384 blk
= (struct dblock
*) &blk
->data
[len
];
390 /*** Hermes programming ***/
392 /* About to start programming data (Hermes I)
393 * offset is the entry point
395 * Spectrum_cs' Symbol fw does not require this
396 * wl_lkm Agere fw does
397 * Don't know about intersil
399 int hermesi_program_init(hermes_t
*hw
, u32 offset
)
403 /* Disable interrupts?*/
405 /*hermes_write_regn(hw, INTEN, 0);*/
406 /*hermes_set_irqmask(hw, 0);*/
408 /* Acknowledge any outstanding command */
409 hermes_write_regn(hw
, EVACK
, 0xFFFF);
411 /* Using doicmd_wait rather than docmd_wait */
412 err
= hermes_doicmd_wait(hw
,
413 0x0100 | HERMES_CMD_INIT
,
418 err
= hermes_doicmd_wait(hw
,
419 0x0000 | HERMES_CMD_INIT
,
424 err
= hermes_aux_control(hw
, 1);
425 pr_debug(PFX
"AUX enable returned %d\n", err
);
430 pr_debug(KERN_DEBUG PFX
"Enabling volatile, EP 0x%08x\n", offset
);
431 err
= hermes_doicmd_wait(hw
,
432 HERMES_PROGRAM_ENABLE_VOLATILE
,
437 pr_debug(PFX
"PROGRAM_ENABLE returned %d\n", err
);
442 /* Done programming data (Hermes I)
444 * Spectrum_cs' Symbol fw does not require this
445 * wl_lkm Agere fw does
446 * Don't know about intersil
448 int hermesi_program_end(hermes_t
*hw
)
450 struct hermes_response resp
;
454 rc
= hermes_docmd_wait(hw
, HERMES_PROGRAM_DISABLE
, 0, &resp
);
456 pr_debug(PFX
"PROGRAM_DISABLE returned %d, "
457 "r0 0x%04x, r1 0x%04x, r2 0x%04x\n",
458 rc
, resp
.resp0
, resp
.resp1
, resp
.resp2
);
461 ((resp
.status
& HERMES_STATUS_CMDCODE
) != HERMES_CMD_DOWNLD
))
464 err
= hermes_aux_control(hw
, 0);
465 pr_debug(PFX
"AUX disable returned %d\n", err
);
467 /* Acknowledge any outstanding command */
468 hermes_write_regn(hw
, EVACK
, 0xFFFF);
470 /* Reinitialise, ignoring return */
471 (void) hermes_doicmd_wait(hw
, 0x0000 | HERMES_CMD_INIT
,
474 return rc
? rc
: err
;
477 /* Program the data blocks */
478 int hermes_program(hermes_t
*hw
, const char *first_block
, const void *end
)
480 const struct dblock
*blk
;
483 #if LIMIT_PROGRAM_SIZE
488 blk
= (const struct dblock
*) first_block
;
490 if ((void *) blk
> (end
- sizeof(*blk
)))
493 blkaddr
= dblock_addr(blk
);
494 blklen
= dblock_len(blk
);
496 while ((blkaddr
!= BLOCK_END
) &&
497 (((void *) blk
+ blklen
) <= end
)) {
498 pr_debug(PFX
"Programming block of length %d "
499 "to address 0x%08x\n", blklen
, blkaddr
);
501 #if !LIMIT_PROGRAM_SIZE
502 /* wl_lkm driver splits this into writes of 2000 bytes */
503 hermes_aux_setaddr(hw
, blkaddr
);
504 hermes_write_bytes(hw
, HERMES_AUXDATA
, blk
->data
,
507 len
= (blklen
< MAX_DL_SIZE
) ? blklen
: MAX_DL_SIZE
;
510 while (addr
< (blkaddr
+ blklen
)) {
511 pr_debug(PFX
"Programming subblock of length %d "
512 "to address 0x%08x. Data @ %p\n",
513 len
, addr
, &blk
->data
[addr
- blkaddr
]);
515 hermes_aux_setaddr(hw
, addr
);
516 hermes_write_bytes(hw
, HERMES_AUXDATA
,
517 &blk
->data
[addr
- blkaddr
],
521 len
= ((blkaddr
+ blklen
- addr
) < MAX_DL_SIZE
) ?
522 (blkaddr
+ blklen
- addr
) : MAX_DL_SIZE
;
525 blk
= (const struct dblock
*) &blk
->data
[blklen
];
527 if ((void *) blk
> (end
- sizeof(*blk
)))
530 blkaddr
= dblock_addr(blk
);
531 blklen
= dblock_len(blk
);
536 /*** Default plugging data for Hermes I ***/
537 /* Values from wl_lkm_718/hcf/dhf.c */
539 #define DEFINE_DEFAULT_PDR(pid, length, data) \
540 static const struct { \
544 } __attribute__ ((packed)) default_pdr_data_##pid = { \
545 cpu_to_le16((sizeof(default_pdr_data_##pid)/ \
546 sizeof(__le16)) - 1), \
551 #define DEFAULT_PDR(pid) default_pdr_data_##pid
553 /* HWIF Compatiblity */
554 DEFINE_DEFAULT_PDR(0x0005, 10, "\x00\x00\x06\x00\x01\x00\x01\x00\x01\x00");
557 DEFINE_DEFAULT_PDR(0x0108, 4, "\x00\x00\x00\x00");
560 DEFINE_DEFAULT_PDR(0x0109, 10, "\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00");
562 /* Antenna diversity */
563 DEFINE_DEFAULT_PDR(0x0150, 2, "\x00\x3F");
565 /* Modem VCO band Set-up */
566 DEFINE_DEFAULT_PDR(0x0160, 28,
567 "\x00\x00\x00\x00\x00\x00\x00\x00"
568 "\x00\x00\x00\x00\x00\x00\x00\x00"
569 "\x00\x00\x00\x00\x00\x00\x00\x00"
572 /* Modem Rx Gain Table Values */
573 DEFINE_DEFAULT_PDR(0x0161, 256,
574 "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
575 "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
576 "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
577 "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
578 "\x3F\x01\x3E\01\x3E\x01\x3D\x01"
579 "\x3D\x01\x3C\01\x3C\x01\x3B\x01"
580 "\x3B\x01\x3A\01\x3A\x01\x39\x01"
581 "\x39\x01\x38\01\x38\x01\x37\x01"
582 "\x37\x01\x36\01\x36\x01\x35\x01"
583 "\x35\x01\x34\01\x34\x01\x33\x01"
584 "\x33\x01\x32\x01\x32\x01\x31\x01"
585 "\x31\x01\x30\x01\x30\x01\x7B\x01"
586 "\x7B\x01\x7A\x01\x7A\x01\x79\x01"
587 "\x79\x01\x78\x01\x78\x01\x77\x01"
588 "\x77\x01\x76\x01\x76\x01\x75\x01"
589 "\x75\x01\x74\x01\x74\x01\x73\x01"
590 "\x73\x01\x72\x01\x72\x01\x71\x01"
591 "\x71\x01\x70\x01\x70\x01\x68\x01"
592 "\x68\x01\x67\x01\x67\x01\x66\x01"
593 "\x66\x01\x65\x01\x65\x01\x57\x01"
594 "\x57\x01\x56\x01\x56\x01\x55\x01"
595 "\x55\x01\x54\x01\x54\x01\x53\x01"
596 "\x53\x01\x52\x01\x52\x01\x51\x01"
597 "\x51\x01\x50\x01\x50\x01\x48\x01"
598 "\x48\x01\x47\x01\x47\x01\x46\x01"
599 "\x46\x01\x45\x01\x45\x01\x44\x01"
600 "\x44\x01\x43\x01\x43\x01\x42\x01"
601 "\x42\x01\x41\x01\x41\x01\x40\x01"
602 "\x40\x01\x40\x01\x40\x01\x40\x01"
603 "\x40\x01\x40\x01\x40\x01\x40\x01"
604 "\x40\x01\x40\x01\x40\x01\x40\x01"
605 "\x40\x01\x40\x01\x40\x01\x40\x01");
607 /* Write PDA according to certain rules.
609 * For every production data record, look for a previous setting in
610 * the pda, and use that.
612 * For certain records, use defaults if they are not found in pda.
614 int hermes_apply_pda_with_defaults(hermes_t
*hw
,
615 const char *first_pdr
,
620 const struct pdr
*pdr
= (const struct pdr
*) first_pdr
;
621 const struct pdi
*first_pdi
= (const struct pdi
*) &pda
[2];
622 const struct pdi
*pdi
;
623 const struct pdi
*default_pdi
= NULL
;
624 const struct pdi
*outdoor_pdi
;
627 pdr_end
-= sizeof(struct pdr
);
629 while (((void *) pdr
<= pdr_end
) &&
630 (pdr_id(pdr
) != PDI_END
)) {
632 * For spectrum_cs firmwares,
633 * PDR area is currently not terminated by PDI_END.
634 * It's followed by CRC records, which have the type
635 * field where PDR has length. The type can be 0 or 1.
637 if (pdr_len(pdr
) < 2)
639 record_id
= pdr_id(pdr
);
641 pdi
= hermes_find_pdi(first_pdi
, record_id
, pda_end
);
643 pr_debug(PFX
"Found record 0x%04x at %p\n",
647 case 0x110: /* Modem REFDAC values */
648 case 0x120: /* Modem VGDAC values */
649 outdoor_pdi
= hermes_find_pdi(first_pdi
, record_id
+ 1,
655 "Using outdoor record 0x%04x at %p\n",
659 case 0x5: /* HWIF Compatiblity */
660 default_pdi
= (struct pdi
*) &DEFAULT_PDR(0x0005);
662 case 0x108: /* PPPPSign */
663 default_pdi
= (struct pdi
*) &DEFAULT_PDR(0x0108);
665 case 0x109: /* PPPPProf */
666 default_pdi
= (struct pdi
*) &DEFAULT_PDR(0x0109);
668 case 0x150: /* Antenna diversity */
669 default_pdi
= (struct pdi
*) &DEFAULT_PDR(0x0150);
671 case 0x160: /* Modem VCO band Set-up */
672 default_pdi
= (struct pdi
*) &DEFAULT_PDR(0x0160);
674 case 0x161: /* Modem Rx Gain Table Values */
675 default_pdi
= (struct pdi
*) &DEFAULT_PDR(0x0161);
681 if (!pdi
&& default_pdi
) {
684 pr_debug(PFX
"Using default record 0x%04x at %p\n",
689 /* Lengths of the data in PDI and PDR must match */
690 if ((pdi_len(pdi
) == pdr_len(pdr
)) &&
691 ((void *) pdi
->data
+ pdi_len(pdi
) < pda_end
)) {
692 /* do the actual plugging */
693 hermes_aux_setaddr(hw
, pdr_addr(pdr
));
694 hermes_write_bytes(hw
, HERMES_AUXDATA
,
695 pdi
->data
, pdi_len(pdi
));