2 * Hermes download helper driver.
4 * This could be entirely merged into hermes.c.
6 * I'm keeping it separate to minimise the amount of merging between
7 * kernel upgrades. It also means the memory overhead for drivers that
8 * don't need firmware download low.
11 * - is capable of writing to the volatile area of the hermes device
12 * - is currently not capable of writing to non-volatile areas
13 * - provide helpers to identify and update plugin data
14 * - is not capable of interpreting a fw image directly. That is up to
15 * the main card driver.
16 * - deals with Hermes I devices. It can probably be modified to deal
17 * with Hermes II devices
19 * Copyright (C) 2007, David Kilroy
21 * Plug data code slightly modified from spectrum_cs driver
22 * Copyright (C) 2002-2005 Pavel Roskin <proski@gnu.org>
23 * Portions based on information in wl_lkm_718 Agere driver
24 * COPYRIGHT (C) 2001-2004 by Agere Systems Inc. All Rights Reserved
26 * The contents of this file are subject to the Mozilla Public License
27 * Version 1.1 (the "License"); you may not use this file except in
28 * compliance with the License. You may obtain a copy of the License
29 * at http://www.mozilla.org/MPL/
31 * Software distributed under the License is distributed on an "AS IS"
32 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
33 * the License for the specific language governing rights and
34 * limitations under the License.
36 * Alternatively, the contents of this file may be used under the
37 * terms of the GNU General Public License version 2 (the "GPL"), in
38 * which case the provisions of the GPL are applicable instead of the
39 * above. If you wish to allow the use of your version of this file
40 * only under the terms of the GPL and not to allow others to use your
41 * version of this file under the MPL, indicate your decision by
42 * deleting the provisions above and replace them with the notice and
43 * other provisions required by the GPL. If you do not delete the
44 * provisions above, a recipient may use your version of this file
45 * under either the MPL or the GPL.
48 #include <linux/module.h>
49 #include <linux/delay.h>
51 #include "hermes_dld.h"
53 MODULE_DESCRIPTION("Download helper for Lucent Hermes chipset");
54 MODULE_AUTHOR("David Kilroy <kilroyd@gmail.com>");
55 MODULE_LICENSE("Dual MPL/GPL");
57 #define PFX "hermes_dld: "
60 * AUX port access. To unlock the AUX port write the access keys to the
61 * PARAM0-2 registers, then write HERMES_AUX_ENABLE to the HERMES_CONTROL
62 * register. Then read it and make sure it's HERMES_AUX_ENABLED.
64 #define HERMES_AUX_ENABLE 0x8000 /* Enable auxiliary port access */
65 #define HERMES_AUX_DISABLE 0x4000 /* Disable to auxiliary port access */
66 #define HERMES_AUX_ENABLED 0xC000 /* Auxiliary port is open */
67 #define HERMES_AUX_DISABLED 0x0000 /* Auxiliary port is closed */
69 #define HERMES_AUX_PW0 0xFE01
70 #define HERMES_AUX_PW1 0xDC23
71 #define HERMES_AUX_PW2 0xBA45
73 /* End markers used in dblocks */
74 #define PDI_END 0x00000000 /* End of PDA */
75 #define BLOCK_END 0xFFFFFFFF /* Last image block */
76 #define TEXT_END 0x1A /* End of text header */
79 * PDA == Production Data Area
81 * In principle, the max. size of the PDA is is 4096 words. Currently,
82 * however, only about 500 bytes of this area are used.
84 * Some USB implementations can't handle sizes in excess of 1016. Note
85 * that PDA is not actually used in those USB environments, but may be
86 * retrieved by common code.
88 #define MAX_PDA_SIZE 1000
90 /* Limit the amout we try to download in a single shot.
93 #define MAX_DL_SIZE 1024
94 #define LIMIT_PROGRAM_SIZE 0
97 * The following structures have little-endian fields denoted by
98 * the leading underscore. Don't access them directly - use inline
99 * functions defined below.
103 * The binary image to be downloaded consists of series of data blocks.
104 * Each block has the following structure.
107 __le32 addr; /* adapter address where to write the block */
108 __le16 len; /* length of the data only, in bytes */
109 char data[0]; /* data to be written */
110 } __attribute__ ((packed));
113 * Plug Data References are located in in the image after the last data
114 * block. They refer to areas in the adapter memory where the plug data
115 * items with matching ID should be written.
118 __le32 id; /* record ID */
119 __le32 addr; /* adapter address where to write the data */
120 __le32 len; /* expected length of the data, in bytes */
121 char next[0]; /* next PDR starts here */
122 } __attribute__ ((packed));
125 * Plug Data Items are located in the EEPROM read from the adapter by
126 * primary firmware. They refer to the device-specific data that should
127 * be plugged into the secondary firmware.
130 __le16 len; /* length of ID and data, in words */
131 __le16 id; /* record ID */
132 char data[0]; /* plug data */
133 } __attribute__ ((packed));
135 /*** FW data block access functions ***/
138 dblock_addr(const struct dblock *blk)
140 return le32_to_cpu(blk->addr);
144 dblock_len(const struct dblock *blk)
146 return le16_to_cpu(blk->len);
149 /*** PDR Access functions ***/
152 pdr_id(const struct pdr *pdr)
154 return le32_to_cpu(pdr->id);
158 pdr_addr(const struct pdr *pdr)
160 return le32_to_cpu(pdr->addr);
164 pdr_len(const struct pdr *pdr)
166 return le32_to_cpu(pdr->len);
169 /*** PDI Access functions ***/
172 pdi_id(const struct pdi *pdi)
174 return le16_to_cpu(pdi->id);
177 /* Return length of the data only, in bytes */
179 pdi_len(const struct pdi *pdi)
181 return 2 * (le16_to_cpu(pdi->len) - 1);
184 /*** Hermes AUX control ***/
187 hermes_aux_setaddr(hermes_t *hw, u32 addr)
189 hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7));
190 hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F));
194 hermes_aux_control(hermes_t *hw, int enabled)
196 int desired_state = enabled ? HERMES_AUX_ENABLED : HERMES_AUX_DISABLED;
197 int action = enabled ? HERMES_AUX_ENABLE : HERMES_AUX_DISABLE;
201 if (hermes_read_reg(hw, HERMES_CONTROL) == desired_state)
204 hermes_write_reg(hw, HERMES_PARAM0, HERMES_AUX_PW0);
205 hermes_write_reg(hw, HERMES_PARAM1, HERMES_AUX_PW1);
206 hermes_write_reg(hw, HERMES_PARAM2, HERMES_AUX_PW2);
207 hermes_write_reg(hw, HERMES_CONTROL, action);
209 for (i = 0; i < 20; i++) {
211 if (hermes_read_reg(hw, HERMES_CONTROL) ==
219 /*** Plug Data Functions ***/
222 * Scan PDR for the record with the specified RECORD_ID.
223 * If it's not found, return NULL.
226 hermes_find_pdr(struct pdr *first_pdr, u32 record_id)
228 struct pdr *pdr = first_pdr;
229 void *end = (void *)first_pdr + MAX_PDA_SIZE;
231 while (((void *)pdr < end) &&
232 (pdr_id(pdr) != PDI_END)) {
234 * PDR area is currently not terminated by PDI_END.
235 * It's followed by CRC records, which have the type
236 * field where PDR has length. The type can be 0 or 1.
238 if (pdr_len(pdr) < 2)
241 /* If the record ID matches, we are done */
242 if (pdr_id(pdr) == record_id)
245 pdr = (struct pdr *) pdr->next;
250 /* Process one Plug Data Item - find corresponding PDR and plug it */
252 hermes_plug_pdi(hermes_t *hw, struct pdr *first_pdr, const struct pdi *pdi)
256 /* Find the PDR corresponding to this PDI */
257 pdr = hermes_find_pdr(first_pdr, pdi_id(pdi));
259 /* No match is found, safe to ignore */
263 /* Lengths of the data in PDI and PDR must match */
264 if (pdi_len(pdi) != pdr_len(pdr))
267 /* do the actual plugging */
268 hermes_aux_setaddr(hw, pdr_addr(pdr));
269 hermes_write_bytes(hw, HERMES_AUXDATA, pdi->data, pdi_len(pdi));
274 /* Read PDA from the adapter */
275 int hermes_read_pda(hermes_t *hw,
279 int use_eeprom) /* can we get this into hw? */
283 u16 data_len = pda_len;
287 /* PDA of spectrum symbol is in eeprom */
289 /* Issue command to read EEPROM */
290 ret = hermes_docmd_wait(hw, HERMES_CMD_READMIF, 0, NULL);
295 /* Open auxiliary port */
296 ret = hermes_aux_control(hw, 1);
297 printk(KERN_DEBUG PFX "AUX enable returned %d\n", ret);
301 /* read PDA from EEPROM */
302 hermes_aux_setaddr(hw, pda_addr);
303 hermes_read_words(hw, HERMES_AUXDATA, data, data_len / 2);
306 ret = hermes_aux_control(hw, 0);
307 printk(KERN_DEBUG PFX "AUX disable returned %d\n", ret);
309 /* Check PDA length */
310 pda_size = le16_to_cpu(pda[0]);
311 printk(KERN_DEBUG PFX "Actual PDA length %d, Max allowed %d\n",
313 if (pda_size > pda_len)
318 EXPORT_SYMBOL(hermes_read_pda);
320 /* Parse PDA and write the records into the adapter
322 * Attempt to write every records that is in the specified pda
323 * which also has a valid production data record for the firmware.
325 int hermes_apply_pda(hermes_t *hw,
326 const char *first_pdr,
330 const struct pdi *pdi;
333 pdr = (struct pdr *) first_pdr;
335 /* Go through every PDI and plug them into the adapter */
336 pdi = (const struct pdi *) (pda + 2);
337 while (pdi_id(pdi) != PDI_END) {
338 ret = hermes_plug_pdi(hw, pdr, pdi);
342 /* Increment to the next PDI */
343 pdi = (const struct pdi *) &pdi->data[pdi_len(pdi)];
347 EXPORT_SYMBOL(hermes_apply_pda);
349 /* Identify the total number of bytes in all blocks
350 * including the header data.
353 hermes_blocks_length(const char *first_block)
355 const struct dblock *blk = (const struct dblock *) first_block;
359 /* Skip all blocks to locate Plug Data References
361 while (dblock_addr(blk) != BLOCK_END) {
362 len = dblock_len(blk);
363 total_len += sizeof(*blk) + len;
364 blk = (struct dblock *) &blk->data[len];
369 EXPORT_SYMBOL(hermes_blocks_length);
371 /*** Hermes programming ***/
373 /* Program the data blocks */
374 int hermes_program(hermes_t *hw, const char *first_block, const char *end)
376 const struct dblock *blk;
379 #if LIMIT_PROGRAM_SIZE
384 blk = (const struct dblock *) first_block;
386 if ((const char *) blk > (end - sizeof(*blk)))
389 blkaddr = dblock_addr(blk);
390 blklen = dblock_len(blk);
392 while ((blkaddr != BLOCK_END) &&
393 (((const char *) blk + blklen) <= end)) {
394 printk(KERN_DEBUG PFX
395 "Programming block of length %d to address 0x%08x\n",
398 #if !LIMIT_PROGRAM_SIZE
399 /* wl_lkm driver splits this into writes of 2000 bytes */
400 hermes_aux_setaddr(hw, blkaddr);
401 hermes_write_bytes(hw, HERMES_AUXDATA, blk->data,
404 len = (blklen < MAX_DL_SIZE) ? blklen : MAX_DL_SIZE;
407 while (addr < (blkaddr + blklen)) {
408 printk(KERN_DEBUG PFX
409 "Programming subblock of length %d "
410 "to address 0x%08x. Data @ %p\n",
411 len, addr, &blk->data[addr - blkaddr]);
413 hermes_aux_setaddr(hw, addr);
414 hermes_write_bytes(hw, HERMES_AUXDATA,
415 &blk->data[addr - blkaddr],
419 len = ((blkaddr + blklen - addr) < MAX_DL_SIZE) ?
420 (blkaddr + blklen - addr) : MAX_DL_SIZE;
423 blk = (const struct dblock *) &blk->data[blklen];
425 if ((const char *) blk > (end - sizeof(*blk)))
428 blkaddr = dblock_addr(blk);
429 blklen = dblock_len(blk);
433 EXPORT_SYMBOL(hermes_program);
435 static int __init init_hermes_dld(void)
440 static void __exit exit_hermes_dld(void)
444 module_init(init_hermes_dld);
445 module_exit(exit_hermes_dld);