blob: d8c626e61a3ad603d6e18bf902806ffb3aa62052 [file] [log] [blame]
David Kilroyf482eb72008-08-21 23:27:51 +01001/*
2 * Hermes download helper driver.
3 *
4 * This could be entirely merged into hermes.c.
5 *
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.
9 *
10 * This driver:
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
18 *
19 * Copyright (C) 2007, David Kilroy
20 *
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
25 *
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/
30 *
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.
35 *
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.
46 */
47
48#include <linux/module.h>
49#include <linux/delay.h>
50#include "hermes.h"
51#include "hermes_dld.h"
52
53MODULE_DESCRIPTION("Download helper for Lucent Hermes chipset");
54MODULE_AUTHOR("David Kilroy <kilroyd@gmail.com>");
55MODULE_LICENSE("Dual MPL/GPL");
56
57#define PFX "hermes_dld: "
58
59/*
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.
63 */
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 */
David Kilroye2334182008-08-21 23:27:52 +010067#define HERMES_AUX_DISABLED 0x0000 /* Auxiliary port is closed */
David Kilroyf482eb72008-08-21 23:27:51 +010068
69#define HERMES_AUX_PW0 0xFE01
70#define HERMES_AUX_PW1 0xDC23
71#define HERMES_AUX_PW2 0xBA45
72
David Kilroy8f5ae732008-08-21 23:27:53 +010073/* HERMES_CMD_DOWNLD */
74#define HERMES_PROGRAM_DISABLE (0x0000 | HERMES_CMD_DOWNLD)
75#define HERMES_PROGRAM_ENABLE_VOLATILE (0x0100 | HERMES_CMD_DOWNLD)
76#define HERMES_PROGRAM_ENABLE_NON_VOLATILE (0x0200 | HERMES_CMD_DOWNLD)
77#define HERMES_PROGRAM_NON_VOLATILE (0x0300 | HERMES_CMD_DOWNLD)
78
David Kilroye2334182008-08-21 23:27:52 +010079/* End markers used in dblocks */
David Kilroyf482eb72008-08-21 23:27:51 +010080#define PDI_END 0x00000000 /* End of PDA */
81#define BLOCK_END 0xFFFFFFFF /* Last image block */
David Kilroye2334182008-08-21 23:27:52 +010082#define TEXT_END 0x1A /* End of text header */
83
84/*
85 * PDA == Production Data Area
86 *
87 * In principle, the max. size of the PDA is is 4096 words. Currently,
88 * however, only about 500 bytes of this area are used.
89 *
90 * Some USB implementations can't handle sizes in excess of 1016. Note
91 * that PDA is not actually used in those USB environments, but may be
92 * retrieved by common code.
93 */
94#define MAX_PDA_SIZE 1000
95
96/* Limit the amout we try to download in a single shot.
97 * Size is in bytes.
98 */
99#define MAX_DL_SIZE 1024
100#define LIMIT_PROGRAM_SIZE 0
David Kilroyf482eb72008-08-21 23:27:51 +0100101
102/*
103 * The following structures have little-endian fields denoted by
104 * the leading underscore. Don't access them directly - use inline
105 * functions defined below.
106 */
107
108/*
109 * The binary image to be downloaded consists of series of data blocks.
110 * Each block has the following structure.
111 */
112struct dblock {
113 __le32 addr; /* adapter address where to write the block */
114 __le16 len; /* length of the data only, in bytes */
115 char data[0]; /* data to be written */
116} __attribute__ ((packed));
117
118/*
119 * Plug Data References are located in in the image after the last data
120 * block. They refer to areas in the adapter memory where the plug data
121 * items with matching ID should be written.
122 */
123struct pdr {
124 __le32 id; /* record ID */
125 __le32 addr; /* adapter address where to write the data */
126 __le32 len; /* expected length of the data, in bytes */
127 char next[0]; /* next PDR starts here */
128} __attribute__ ((packed));
129
130/*
131 * Plug Data Items are located in the EEPROM read from the adapter by
132 * primary firmware. They refer to the device-specific data that should
133 * be plugged into the secondary firmware.
134 */
135struct pdi {
136 __le16 len; /* length of ID and data, in words */
137 __le16 id; /* record ID */
138 char data[0]; /* plug data */
139} __attribute__ ((packed));
140
David Kilroye2334182008-08-21 23:27:52 +0100141/*** FW data block access functions ***/
142
David Kilroyf482eb72008-08-21 23:27:51 +0100143static inline u32
144dblock_addr(const struct dblock *blk)
145{
146 return le32_to_cpu(blk->addr);
147}
148
149static inline u32
150dblock_len(const struct dblock *blk)
151{
152 return le16_to_cpu(blk->len);
153}
154
David Kilroye2334182008-08-21 23:27:52 +0100155/*** PDR Access functions ***/
156
David Kilroyf482eb72008-08-21 23:27:51 +0100157static inline u32
158pdr_id(const struct pdr *pdr)
159{
160 return le32_to_cpu(pdr->id);
161}
162
163static inline u32
164pdr_addr(const struct pdr *pdr)
165{
166 return le32_to_cpu(pdr->addr);
167}
168
169static inline u32
170pdr_len(const struct pdr *pdr)
171{
172 return le32_to_cpu(pdr->len);
173}
174
David Kilroye2334182008-08-21 23:27:52 +0100175/*** PDI Access functions ***/
176
David Kilroyf482eb72008-08-21 23:27:51 +0100177static inline u32
178pdi_id(const struct pdi *pdi)
179{
180 return le16_to_cpu(pdi->id);
181}
182
183/* Return length of the data only, in bytes */
184static inline u32
185pdi_len(const struct pdi *pdi)
186{
187 return 2 * (le16_to_cpu(pdi->len) - 1);
188}
189
David Kilroye2334182008-08-21 23:27:52 +0100190/*** Hermes AUX control ***/
191
David Kilroyf482eb72008-08-21 23:27:51 +0100192static inline void
David Kilroye2334182008-08-21 23:27:52 +0100193hermes_aux_setaddr(hermes_t *hw, u32 addr)
David Kilroyf482eb72008-08-21 23:27:51 +0100194{
195 hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7));
196 hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F));
197}
198
David Kilroye2334182008-08-21 23:27:52 +0100199static inline int
200hermes_aux_control(hermes_t *hw, int enabled)
David Kilroyf482eb72008-08-21 23:27:51 +0100201{
David Kilroye2334182008-08-21 23:27:52 +0100202 int desired_state = enabled ? HERMES_AUX_ENABLED : HERMES_AUX_DISABLED;
203 int action = enabled ? HERMES_AUX_ENABLE : HERMES_AUX_DISABLE;
David Kilroyf482eb72008-08-21 23:27:51 +0100204 int i;
205
206 /* Already open? */
David Kilroye2334182008-08-21 23:27:52 +0100207 if (hermes_read_reg(hw, HERMES_CONTROL) == desired_state)
David Kilroyf482eb72008-08-21 23:27:51 +0100208 return 0;
209
210 hermes_write_reg(hw, HERMES_PARAM0, HERMES_AUX_PW0);
211 hermes_write_reg(hw, HERMES_PARAM1, HERMES_AUX_PW1);
212 hermes_write_reg(hw, HERMES_PARAM2, HERMES_AUX_PW2);
David Kilroye2334182008-08-21 23:27:52 +0100213 hermes_write_reg(hw, HERMES_CONTROL, action);
David Kilroyf482eb72008-08-21 23:27:51 +0100214
215 for (i = 0; i < 20; i++) {
216 udelay(10);
217 if (hermes_read_reg(hw, HERMES_CONTROL) ==
David Kilroye2334182008-08-21 23:27:52 +0100218 desired_state)
David Kilroyf482eb72008-08-21 23:27:51 +0100219 return 0;
220 }
221
222 return -EBUSY;
223}
224
David Kilroye2334182008-08-21 23:27:52 +0100225/*** Plug Data Functions ***/
226
David Kilroyf482eb72008-08-21 23:27:51 +0100227/*
228 * Scan PDR for the record with the specified RECORD_ID.
229 * If it's not found, return NULL.
230 */
231static struct pdr *
David Kilroye2334182008-08-21 23:27:52 +0100232hermes_find_pdr(struct pdr *first_pdr, u32 record_id)
David Kilroyf482eb72008-08-21 23:27:51 +0100233{
234 struct pdr *pdr = first_pdr;
David Kilroye2334182008-08-21 23:27:52 +0100235 void *end = (void *)first_pdr + MAX_PDA_SIZE;
David Kilroyf482eb72008-08-21 23:27:51 +0100236
David Kilroye2334182008-08-21 23:27:52 +0100237 while (((void *)pdr < end) &&
238 (pdr_id(pdr) != PDI_END)) {
David Kilroyf482eb72008-08-21 23:27:51 +0100239 /*
240 * PDR area is currently not terminated by PDI_END.
241 * It's followed by CRC records, which have the type
242 * field where PDR has length. The type can be 0 or 1.
243 */
244 if (pdr_len(pdr) < 2)
245 return NULL;
246
247 /* If the record ID matches, we are done */
248 if (pdr_id(pdr) == record_id)
249 return pdr;
250
251 pdr = (struct pdr *) pdr->next;
252 }
253 return NULL;
254}
255
David Kilroy8f5ae732008-08-21 23:27:53 +0100256/* Scan production data items for a particular entry */
257static struct pdi *
258hermes_find_pdi(struct pdi *first_pdi, u32 record_id)
259{
260 struct pdi *pdi = first_pdi;
261
262 while (pdi_id(pdi) != PDI_END) {
263
264 /* If the record ID matches, we are done */
265 if (pdi_id(pdi) == record_id)
266 return pdi;
267
268 pdi = (struct pdi *) &pdi->data[pdi_len(pdi)];
269 }
270 return NULL;
271}
272
David Kilroyf482eb72008-08-21 23:27:51 +0100273/* Process one Plug Data Item - find corresponding PDR and plug it */
274static int
David Kilroye2334182008-08-21 23:27:52 +0100275hermes_plug_pdi(hermes_t *hw, struct pdr *first_pdr, const struct pdi *pdi)
David Kilroyf482eb72008-08-21 23:27:51 +0100276{
277 struct pdr *pdr;
278
David Kilroye2334182008-08-21 23:27:52 +0100279 /* Find the PDR corresponding to this PDI */
280 pdr = hermes_find_pdr(first_pdr, pdi_id(pdi));
David Kilroyf482eb72008-08-21 23:27:51 +0100281
282 /* No match is found, safe to ignore */
283 if (!pdr)
284 return 0;
285
286 /* Lengths of the data in PDI and PDR must match */
287 if (pdi_len(pdi) != pdr_len(pdr))
288 return -EINVAL;
289
290 /* do the actual plugging */
David Kilroye2334182008-08-21 23:27:52 +0100291 hermes_aux_setaddr(hw, pdr_addr(pdr));
David Kilroyf482eb72008-08-21 23:27:51 +0100292 hermes_write_bytes(hw, HERMES_AUXDATA, pdi->data, pdi_len(pdi));
293
294 return 0;
295}
296
297/* Read PDA from the adapter */
David Kilroye2334182008-08-21 23:27:52 +0100298int hermes_read_pda(hermes_t *hw,
299 __le16 *pda,
300 u32 pda_addr,
301 u16 pda_len,
302 int use_eeprom) /* can we get this into hw? */
David Kilroyf482eb72008-08-21 23:27:51 +0100303{
304 int ret;
David Kilroye2334182008-08-21 23:27:52 +0100305 u16 pda_size;
306 u16 data_len = pda_len;
307 __le16 *data = pda;
David Kilroyf482eb72008-08-21 23:27:51 +0100308
David Kilroye2334182008-08-21 23:27:52 +0100309 if (use_eeprom) {
310 /* PDA of spectrum symbol is in eeprom */
311
312 /* Issue command to read EEPROM */
313 ret = hermes_docmd_wait(hw, HERMES_CMD_READMIF, 0, NULL);
314 if (ret)
315 return ret;
David Kilroy8f5ae732008-08-21 23:27:53 +0100316 } else {
317 /* wl_lkm does not include PDA size in the PDA area.
318 * We will pad the information into pda, so other routines
319 * don't have to be modified */
320 pda[0] = cpu_to_le16(pda_len - 2);
321 /* Includes CFG_PROD_DATA but not itself */
322 pda[1] = cpu_to_le16(0x0800); /* CFG_PROD_DATA */
323 data_len = pda_len - 4;
324 data = pda + 2;
David Kilroye2334182008-08-21 23:27:52 +0100325 }
David Kilroyf482eb72008-08-21 23:27:51 +0100326
327 /* Open auxiliary port */
David Kilroye2334182008-08-21 23:27:52 +0100328 ret = hermes_aux_control(hw, 1);
329 printk(KERN_DEBUG PFX "AUX enable returned %d\n", ret);
David Kilroyf482eb72008-08-21 23:27:51 +0100330 if (ret)
331 return ret;
332
333 /* read PDA from EEPROM */
David Kilroye2334182008-08-21 23:27:52 +0100334 hermes_aux_setaddr(hw, pda_addr);
335 hermes_read_words(hw, HERMES_AUXDATA, data, data_len / 2);
336
337 /* Close aux port */
338 ret = hermes_aux_control(hw, 0);
339 printk(KERN_DEBUG PFX "AUX disable returned %d\n", ret);
David Kilroyf482eb72008-08-21 23:27:51 +0100340
341 /* Check PDA length */
342 pda_size = le16_to_cpu(pda[0]);
David Kilroye2334182008-08-21 23:27:52 +0100343 printk(KERN_DEBUG PFX "Actual PDA length %d, Max allowed %d\n",
344 pda_size, pda_len);
David Kilroyf482eb72008-08-21 23:27:51 +0100345 if (pda_size > pda_len)
346 return -EINVAL;
347
348 return 0;
349}
David Kilroye2334182008-08-21 23:27:52 +0100350EXPORT_SYMBOL(hermes_read_pda);
David Kilroyf482eb72008-08-21 23:27:51 +0100351
David Kilroye2334182008-08-21 23:27:52 +0100352/* Parse PDA and write the records into the adapter
353 *
354 * Attempt to write every records that is in the specified pda
355 * which also has a valid production data record for the firmware.
356 */
357int hermes_apply_pda(hermes_t *hw,
358 const char *first_pdr,
359 const __le16 *pda)
David Kilroyf482eb72008-08-21 23:27:51 +0100360{
361 int ret;
David Kilroye2334182008-08-21 23:27:52 +0100362 const struct pdi *pdi;
363 struct pdr *pdr;
David Kilroyf482eb72008-08-21 23:27:51 +0100364
David Kilroye2334182008-08-21 23:27:52 +0100365 pdr = (struct pdr *) first_pdr;
David Kilroyf482eb72008-08-21 23:27:51 +0100366
367 /* Go through every PDI and plug them into the adapter */
David Kilroye2334182008-08-21 23:27:52 +0100368 pdi = (const struct pdi *) (pda + 2);
David Kilroyf482eb72008-08-21 23:27:51 +0100369 while (pdi_id(pdi) != PDI_END) {
David Kilroye2334182008-08-21 23:27:52 +0100370 ret = hermes_plug_pdi(hw, pdr, pdi);
David Kilroyf482eb72008-08-21 23:27:51 +0100371 if (ret)
372 return ret;
373
374 /* Increment to the next PDI */
David Kilroye2334182008-08-21 23:27:52 +0100375 pdi = (const struct pdi *) &pdi->data[pdi_len(pdi)];
David Kilroyf482eb72008-08-21 23:27:51 +0100376 }
377 return 0;
378}
David Kilroye2334182008-08-21 23:27:52 +0100379EXPORT_SYMBOL(hermes_apply_pda);
David Kilroyf482eb72008-08-21 23:27:51 +0100380
David Kilroye2334182008-08-21 23:27:52 +0100381/* Identify the total number of bytes in all blocks
382 * including the header data.
383 */
384size_t
385hermes_blocks_length(const char *first_block)
386{
387 const struct dblock *blk = (const struct dblock *) first_block;
388 int total_len = 0;
389 int len;
390
391 /* Skip all blocks to locate Plug Data References
392 * (Spectrum CS) */
393 while (dblock_addr(blk) != BLOCK_END) {
394 len = dblock_len(blk);
395 total_len += sizeof(*blk) + len;
396 blk = (struct dblock *) &blk->data[len];
397 }
398
399 return total_len;
400}
401EXPORT_SYMBOL(hermes_blocks_length);
402
403/*** Hermes programming ***/
404
David Kilroy8f5ae732008-08-21 23:27:53 +0100405/* About to start programming data (Hermes I)
406 * offset is the entry point
407 *
408 * Spectrum_cs' Symbol fw does not require this
409 * wl_lkm Agere fw does
410 * Don't know about intersil
411 */
412int hermesi_program_init(hermes_t *hw, u32 offset)
413{
414 int err;
415
416 /* Disable interrupts?*/
417 /*hw->inten = 0x0;*/
418 /*hermes_write_regn(hw, INTEN, 0);*/
419 /*hermes_set_irqmask(hw, 0);*/
420
421 /* Acknowledge any outstanding command */
422 hermes_write_regn(hw, EVACK, 0xFFFF);
423
424 /* Using doicmd_wait rather than docmd_wait */
425 err = hermes_doicmd_wait(hw,
426 0x0100 | HERMES_CMD_INIT,
427 0, 0, 0, NULL);
428 if (err)
429 return err;
430
431 err = hermes_doicmd_wait(hw,
432 0x0000 | HERMES_CMD_INIT,
433 0, 0, 0, NULL);
434 if (err)
435 return err;
436
437 err = hermes_aux_control(hw, 1);
438 printk(KERN_DEBUG PFX "AUX enable returned %d\n", err);
439
440 if (err)
441 return err;
442
443 printk(KERN_DEBUG PFX "Enabling volatile, EP 0x%08x\n", offset);
444 err = hermes_doicmd_wait(hw,
445 HERMES_PROGRAM_ENABLE_VOLATILE,
446 offset & 0xFFFFu,
447 offset >> 16,
448 0,
449 NULL);
450 printk(KERN_DEBUG PFX "PROGRAM_ENABLE returned %d\n",
451 err);
452
453 return err;
454}
455EXPORT_SYMBOL(hermesi_program_init);
456
457/* Done programming data (Hermes I)
458 *
459 * Spectrum_cs' Symbol fw does not require this
460 * wl_lkm Agere fw does
461 * Don't know about intersil
462 */
463int hermesi_program_end(hermes_t *hw)
464{
465 struct hermes_response resp;
466 int rc = 0;
467 int err;
468
469 rc = hermes_docmd_wait(hw, HERMES_PROGRAM_DISABLE, 0, &resp);
470
471 printk(KERN_DEBUG PFX "PROGRAM_DISABLE returned %d, "
472 "r0 0x%04x, r1 0x%04x, r2 0x%04x\n",
473 rc, resp.resp0, resp.resp1, resp.resp2);
474
475 if ((rc == 0) &&
476 ((resp.status & HERMES_STATUS_CMDCODE) != HERMES_CMD_DOWNLD))
477 rc = -EIO;
478
479 err = hermes_aux_control(hw, 0);
480 printk(KERN_DEBUG PFX "AUX disable returned %d\n", err);
481
482 /* Acknowledge any outstanding command */
483 hermes_write_regn(hw, EVACK, 0xFFFF);
484
485 /* Reinitialise, ignoring return */
486 (void) hermes_doicmd_wait(hw, 0x0000 | HERMES_CMD_INIT,
487 0, 0, 0, NULL);
488
489 return rc ? rc : err;
490}
491EXPORT_SYMBOL(hermesi_program_end);
492
David Kilroye2334182008-08-21 23:27:52 +0100493/* Program the data blocks */
494int hermes_program(hermes_t *hw, const char *first_block, const char *end)
David Kilroyf482eb72008-08-21 23:27:51 +0100495{
496 const struct dblock *blk;
497 u32 blkaddr;
498 u32 blklen;
David Kilroye2334182008-08-21 23:27:52 +0100499#if LIMIT_PROGRAM_SIZE
500 u32 addr;
501 u32 len;
502#endif
David Kilroyf482eb72008-08-21 23:27:51 +0100503
David Kilroye2334182008-08-21 23:27:52 +0100504 blk = (const struct dblock *) first_block;
505
506 if ((const char *) blk > (end - sizeof(*blk)))
507 return -EIO;
508
David Kilroyf482eb72008-08-21 23:27:51 +0100509 blkaddr = dblock_addr(blk);
510 blklen = dblock_len(blk);
511
David Kilroye2334182008-08-21 23:27:52 +0100512 while ((blkaddr != BLOCK_END) &&
513 (((const char *) blk + blklen) <= end)) {
514 printk(KERN_DEBUG PFX
515 "Programming block of length %d to address 0x%08x\n",
516 blklen, blkaddr);
517
518#if !LIMIT_PROGRAM_SIZE
519 /* wl_lkm driver splits this into writes of 2000 bytes */
520 hermes_aux_setaddr(hw, blkaddr);
David Kilroyf482eb72008-08-21 23:27:51 +0100521 hermes_write_bytes(hw, HERMES_AUXDATA, blk->data,
522 blklen);
David Kilroye2334182008-08-21 23:27:52 +0100523#else
524 len = (blklen < MAX_DL_SIZE) ? blklen : MAX_DL_SIZE;
525 addr = blkaddr;
David Kilroyf482eb72008-08-21 23:27:51 +0100526
David Kilroye2334182008-08-21 23:27:52 +0100527 while (addr < (blkaddr + blklen)) {
528 printk(KERN_DEBUG PFX
529 "Programming subblock of length %d "
530 "to address 0x%08x. Data @ %p\n",
531 len, addr, &blk->data[addr - blkaddr]);
532
533 hermes_aux_setaddr(hw, addr);
534 hermes_write_bytes(hw, HERMES_AUXDATA,
535 &blk->data[addr - blkaddr],
536 len);
537
538 addr += len;
539 len = ((blkaddr + blklen - addr) < MAX_DL_SIZE) ?
540 (blkaddr + blklen - addr) : MAX_DL_SIZE;
541 }
542#endif
543 blk = (const struct dblock *) &blk->data[blklen];
544
545 if ((const char *) blk > (end - sizeof(*blk)))
546 return -EIO;
547
David Kilroyf482eb72008-08-21 23:27:51 +0100548 blkaddr = dblock_addr(blk);
549 blklen = dblock_len(blk);
550 }
551 return 0;
552}
David Kilroye2334182008-08-21 23:27:52 +0100553EXPORT_SYMBOL(hermes_program);
David Kilroyf482eb72008-08-21 23:27:51 +0100554
555static int __init init_hermes_dld(void)
556{
557 return 0;
558}
559
560static void __exit exit_hermes_dld(void)
561{
562}
563
564module_init(init_hermes_dld);
565module_exit(exit_hermes_dld);
David Kilroy8f5ae732008-08-21 23:27:53 +0100566
567/*** Default plugging data for Hermes I ***/
568/* Values from wl_lkm_718/hcf/dhf.c */
569
570#define DEFINE_DEFAULT_PDR(pid, length, data) \
571static const struct { \
572 __le16 len; \
573 __le16 id; \
574 u8 val[length]; \
575} __attribute__ ((packed)) default_pdr_data_##pid = { \
576 __constant_cpu_to_le16((sizeof(default_pdr_data_##pid)/ \
577 sizeof(__le16)) - 1), \
578 __constant_cpu_to_le16(pid), \
579 data \
580}
581
582#define DEFAULT_PDR(pid) default_pdr_data_##pid
583
584/* HWIF Compatiblity */
585DEFINE_DEFAULT_PDR(0x0005, 10, "\x00\x00\x06\x00\x01\x00\x01\x00\x01\x00");
586
587/* PPPPSign */
588DEFINE_DEFAULT_PDR(0x0108, 4, "\x00\x00\x00\x00");
589
590/* PPPPProf */
591DEFINE_DEFAULT_PDR(0x0109, 10, "\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00");
592
593/* Antenna diversity */
594DEFINE_DEFAULT_PDR(0x0150, 2, "\x00\x3F");
595
596/* Modem VCO band Set-up */
597DEFINE_DEFAULT_PDR(0x0160, 28,
598 "\x00\x00\x00\x00\x00\x00\x00\x00"
599 "\x00\x00\x00\x00\x00\x00\x00\x00"
600 "\x00\x00\x00\x00\x00\x00\x00\x00"
601 "\x00\x00\x00\x00");
602
603/* Modem Rx Gain Table Values */
604DEFINE_DEFAULT_PDR(0x0161, 256,
605 "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
606 "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
607 "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
608 "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
609 "\x3F\x01\x3E\01\x3E\x01\x3D\x01"
610 "\x3D\x01\x3C\01\x3C\x01\x3B\x01"
611 "\x3B\x01\x3A\01\x3A\x01\x39\x01"
612 "\x39\x01\x38\01\x38\x01\x37\x01"
613 "\x37\x01\x36\01\x36\x01\x35\x01"
614 "\x35\x01\x34\01\x34\x01\x33\x01"
615 "\x33\x01\x32\x01\x32\x01\x31\x01"
616 "\x31\x01\x30\x01\x30\x01\x7B\x01"
617 "\x7B\x01\x7A\x01\x7A\x01\x79\x01"
618 "\x79\x01\x78\x01\x78\x01\x77\x01"
619 "\x77\x01\x76\x01\x76\x01\x75\x01"
620 "\x75\x01\x74\x01\x74\x01\x73\x01"
621 "\x73\x01\x72\x01\x72\x01\x71\x01"
622 "\x71\x01\x70\x01\x70\x01\x68\x01"
623 "\x68\x01\x67\x01\x67\x01\x66\x01"
624 "\x66\x01\x65\x01\x65\x01\x57\x01"
625 "\x57\x01\x56\x01\x56\x01\x55\x01"
626 "\x55\x01\x54\x01\x54\x01\x53\x01"
627 "\x53\x01\x52\x01\x52\x01\x51\x01"
628 "\x51\x01\x50\x01\x50\x01\x48\x01"
629 "\x48\x01\x47\x01\x47\x01\x46\x01"
630 "\x46\x01\x45\x01\x45\x01\x44\x01"
631 "\x44\x01\x43\x01\x43\x01\x42\x01"
632 "\x42\x01\x41\x01\x41\x01\x40\x01"
633 "\x40\x01\x40\x01\x40\x01\x40\x01"
634 "\x40\x01\x40\x01\x40\x01\x40\x01"
635 "\x40\x01\x40\x01\x40\x01\x40\x01"
636 "\x40\x01\x40\x01\x40\x01\x40\x01");
637
638/* Write PDA according to certain rules.
639 *
640 * For every production data record, look for a previous setting in
641 * the pda, and use that.
642 *
643 * For certain records, use defaults if they are not found in pda.
644 */
645int hermes_apply_pda_with_defaults(hermes_t *hw,
646 const char *first_pdr,
647 const __le16 *pda)
648{
649 const struct pdr *pdr = (const struct pdr *) first_pdr;
650 struct pdi *first_pdi = (struct pdi *) &pda[2];
651 struct pdi *pdi;
652 struct pdi *default_pdi = NULL;
653 struct pdi *outdoor_pdi;
654 void *end = (void *)first_pdr + MAX_PDA_SIZE;
655 int record_id;
656
657 while (((void *)pdr < end) &&
658 (pdr_id(pdr) != PDI_END)) {
659 /*
660 * For spectrum_cs firmwares,
661 * PDR area is currently not terminated by PDI_END.
662 * It's followed by CRC records, which have the type
663 * field where PDR has length. The type can be 0 or 1.
664 */
665 if (pdr_len(pdr) < 2)
666 break;
667 record_id = pdr_id(pdr);
668
669 pdi = hermes_find_pdi(first_pdi, record_id);
670 if (pdi)
671 printk(KERN_DEBUG PFX "Found record 0x%04x at %p\n",
672 record_id, pdi);
673
674 switch (record_id) {
675 case 0x110: /* Modem REFDAC values */
676 case 0x120: /* Modem VGDAC values */
677 outdoor_pdi = hermes_find_pdi(first_pdi, record_id + 1);
678 default_pdi = NULL;
679 if (outdoor_pdi) {
680 pdi = outdoor_pdi;
681 printk(KERN_DEBUG PFX
682 "Using outdoor record 0x%04x at %p\n",
683 record_id + 1, pdi);
684 }
685 break;
686 case 0x5: /* HWIF Compatiblity */
687 default_pdi = (struct pdi *) &DEFAULT_PDR(0x0005);
688 break;
689 case 0x108: /* PPPPSign */
690 default_pdi = (struct pdi *) &DEFAULT_PDR(0x0108);
691 break;
692 case 0x109: /* PPPPProf */
693 default_pdi = (struct pdi *) &DEFAULT_PDR(0x0109);
694 break;
695 case 0x150: /* Antenna diversity */
696 default_pdi = (struct pdi *) &DEFAULT_PDR(0x0150);
697 break;
698 case 0x160: /* Modem VCO band Set-up */
699 default_pdi = (struct pdi *) &DEFAULT_PDR(0x0160);
700 break;
701 case 0x161: /* Modem Rx Gain Table Values */
702 default_pdi = (struct pdi *) &DEFAULT_PDR(0x0161);
703 break;
704 default:
705 default_pdi = NULL;
706 break;
707 }
708 if (!pdi && default_pdi) {
709 /* Use default */
710 pdi = default_pdi;
711 printk(KERN_DEBUG PFX
712 "Using default record 0x%04x at %p\n",
713 record_id, pdi);
714 }
715
716 if (pdi) {
717 /* Lengths of the data in PDI and PDR must match */
718 if (pdi_len(pdi) == pdr_len(pdr)) {
719 /* do the actual plugging */
720 hermes_aux_setaddr(hw, pdr_addr(pdr));
721 hermes_write_bytes(hw, HERMES_AUXDATA,
722 pdi->data, pdi_len(pdi));
723 }
724 }
725
726 pdr++;
727 }
728 return 0;
729}
730EXPORT_SYMBOL(hermes_apply_pda_with_defaults);