blob: a9ba195cdada70963503c6b6bbd0f41ddd4c5905 [file] [log] [blame]
David Kilroyf482eb72008-08-21 23:27:51 +01001/*
David Kilroyf90d8d42009-02-04 23:05:57 +00002 * Hermes download helper.
David Kilroyf482eb72008-08-21 23:27:51 +01003 *
David Kilroyf90d8d42009-02-04 23:05:57 +00004 * This helper:
David Kilroyf482eb72008-08-21 23:27:51 +01005 * - 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
12 *
13 * Copyright (C) 2007, David Kilroy
14 *
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
19 *
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/
24 *
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.
29 *
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.
40 */
41
42#include <linux/module.h>
43#include <linux/delay.h>
44#include "hermes.h"
45#include "hermes_dld.h"
46
David Kilroyf482eb72008-08-21 23:27:51 +010047#define PFX "hermes_dld: "
48
49/*
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.
53 */
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 */
David Kilroye2334182008-08-21 23:27:52 +010057#define HERMES_AUX_DISABLED 0x0000 /* Auxiliary port is closed */
David Kilroyf482eb72008-08-21 23:27:51 +010058
59#define HERMES_AUX_PW0 0xFE01
60#define HERMES_AUX_PW1 0xDC23
61#define HERMES_AUX_PW2 0xBA45
62
David Kilroy8f5ae732008-08-21 23:27:53 +010063/* 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)
68
David Kilroye2334182008-08-21 23:27:52 +010069/* End markers used in dblocks */
David Kilroyf482eb72008-08-21 23:27:51 +010070#define PDI_END 0x00000000 /* End of PDA */
71#define BLOCK_END 0xFFFFFFFF /* Last image block */
David Kilroye2334182008-08-21 23:27:52 +010072#define TEXT_END 0x1A /* End of text header */
73
David Kilroye2334182008-08-21 23:27:52 +010074/* Limit the amout we try to download in a single shot.
75 * Size is in bytes.
76 */
77#define MAX_DL_SIZE 1024
78#define LIMIT_PROGRAM_SIZE 0
David Kilroyf482eb72008-08-21 23:27:51 +010079
80/*
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.
84 */
85
86/*
87 * The binary image to be downloaded consists of series of data blocks.
88 * Each block has the following structure.
89 */
90struct dblock {
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));
95
96/*
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.
100 */
101struct pdr {
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));
107
108/*
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.
112 */
113struct pdi {
114 __le16 len; /* length of ID and data, in words */
115 __le16 id; /* record ID */
116 char data[0]; /* plug data */
117} __attribute__ ((packed));
118
David Kilroye2334182008-08-21 23:27:52 +0100119/*** FW data block access functions ***/
120
David Kilroyf482eb72008-08-21 23:27:51 +0100121static inline u32
122dblock_addr(const struct dblock *blk)
123{
124 return le32_to_cpu(blk->addr);
125}
126
127static inline u32
128dblock_len(const struct dblock *blk)
129{
130 return le16_to_cpu(blk->len);
131}
132
David Kilroye2334182008-08-21 23:27:52 +0100133/*** PDR Access functions ***/
134
David Kilroyf482eb72008-08-21 23:27:51 +0100135static inline u32
136pdr_id(const struct pdr *pdr)
137{
138 return le32_to_cpu(pdr->id);
139}
140
141static inline u32
142pdr_addr(const struct pdr *pdr)
143{
144 return le32_to_cpu(pdr->addr);
145}
146
147static inline u32
148pdr_len(const struct pdr *pdr)
149{
150 return le32_to_cpu(pdr->len);
151}
152
David Kilroye2334182008-08-21 23:27:52 +0100153/*** PDI Access functions ***/
154
David Kilroyf482eb72008-08-21 23:27:51 +0100155static inline u32
156pdi_id(const struct pdi *pdi)
157{
158 return le16_to_cpu(pdi->id);
159}
160
161/* Return length of the data only, in bytes */
162static inline u32
163pdi_len(const struct pdi *pdi)
164{
165 return 2 * (le16_to_cpu(pdi->len) - 1);
166}
167
David Kilroye2334182008-08-21 23:27:52 +0100168/*** Hermes AUX control ***/
169
David Kilroyf482eb72008-08-21 23:27:51 +0100170static inline void
David Kilroye2334182008-08-21 23:27:52 +0100171hermes_aux_setaddr(hermes_t *hw, u32 addr)
David Kilroyf482eb72008-08-21 23:27:51 +0100172{
173 hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7));
174 hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F));
175}
176
David Kilroye2334182008-08-21 23:27:52 +0100177static inline int
178hermes_aux_control(hermes_t *hw, int enabled)
David Kilroyf482eb72008-08-21 23:27:51 +0100179{
David Kilroye2334182008-08-21 23:27:52 +0100180 int desired_state = enabled ? HERMES_AUX_ENABLED : HERMES_AUX_DISABLED;
181 int action = enabled ? HERMES_AUX_ENABLE : HERMES_AUX_DISABLE;
David Kilroyf482eb72008-08-21 23:27:51 +0100182 int i;
183
184 /* Already open? */
David Kilroye2334182008-08-21 23:27:52 +0100185 if (hermes_read_reg(hw, HERMES_CONTROL) == desired_state)
David Kilroyf482eb72008-08-21 23:27:51 +0100186 return 0;
187
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);
David Kilroye2334182008-08-21 23:27:52 +0100191 hermes_write_reg(hw, HERMES_CONTROL, action);
David Kilroyf482eb72008-08-21 23:27:51 +0100192
193 for (i = 0; i < 20; i++) {
194 udelay(10);
195 if (hermes_read_reg(hw, HERMES_CONTROL) ==
David Kilroye2334182008-08-21 23:27:52 +0100196 desired_state)
David Kilroyf482eb72008-08-21 23:27:51 +0100197 return 0;
198 }
199
200 return -EBUSY;
201}
202
David Kilroye2334182008-08-21 23:27:52 +0100203/*** Plug Data Functions ***/
204
David Kilroyf482eb72008-08-21 23:27:51 +0100205/*
206 * Scan PDR for the record with the specified RECORD_ID.
207 * If it's not found, return NULL.
208 */
David Kilroy3faa19c2009-02-21 16:52:54 +0000209static const struct pdr *
210hermes_find_pdr(const struct pdr *first_pdr, u32 record_id, const void *end)
David Kilroyf482eb72008-08-21 23:27:51 +0100211{
David Kilroy3faa19c2009-02-21 16:52:54 +0000212 const struct pdr *pdr = first_pdr;
David Kilroyf482eb72008-08-21 23:27:51 +0100213
David Kilroy3faa19c2009-02-21 16:52:54 +0000214 end -= sizeof(struct pdr);
215
216 while (((void *) pdr <= end) &&
David Kilroye2334182008-08-21 23:27:52 +0100217 (pdr_id(pdr) != PDI_END)) {
David Kilroyf482eb72008-08-21 23:27:51 +0100218 /*
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.
222 */
223 if (pdr_len(pdr) < 2)
224 return NULL;
225
226 /* If the record ID matches, we are done */
227 if (pdr_id(pdr) == record_id)
228 return pdr;
229
230 pdr = (struct pdr *) pdr->next;
231 }
232 return NULL;
233}
234
David Kilroy8f5ae732008-08-21 23:27:53 +0100235/* Scan production data items for a particular entry */
David Kilroy3faa19c2009-02-21 16:52:54 +0000236static const struct pdi *
237hermes_find_pdi(const struct pdi *first_pdi, u32 record_id, const void *end)
David Kilroy8f5ae732008-08-21 23:27:53 +0100238{
David Kilroy3faa19c2009-02-21 16:52:54 +0000239 const struct pdi *pdi = first_pdi;
David Kilroy8f5ae732008-08-21 23:27:53 +0100240
David Kilroy3faa19c2009-02-21 16:52:54 +0000241 end -= sizeof(struct pdi);
242
243 while (((void *) pdi <= end) &&
244 (pdi_id(pdi) != PDI_END)) {
David Kilroy8f5ae732008-08-21 23:27:53 +0100245
246 /* If the record ID matches, we are done */
247 if (pdi_id(pdi) == record_id)
248 return pdi;
249
250 pdi = (struct pdi *) &pdi->data[pdi_len(pdi)];
251 }
252 return NULL;
253}
254
David Kilroyf482eb72008-08-21 23:27:51 +0100255/* Process one Plug Data Item - find corresponding PDR and plug it */
256static int
David Kilroy3faa19c2009-02-21 16:52:54 +0000257hermes_plug_pdi(hermes_t *hw, const struct pdr *first_pdr,
258 const struct pdi *pdi, const void *pdr_end)
David Kilroyf482eb72008-08-21 23:27:51 +0100259{
David Kilroy3faa19c2009-02-21 16:52:54 +0000260 const struct pdr *pdr;
David Kilroyf482eb72008-08-21 23:27:51 +0100261
David Kilroye2334182008-08-21 23:27:52 +0100262 /* Find the PDR corresponding to this PDI */
David Kilroy3faa19c2009-02-21 16:52:54 +0000263 pdr = hermes_find_pdr(first_pdr, pdi_id(pdi), pdr_end);
David Kilroyf482eb72008-08-21 23:27:51 +0100264
265 /* No match is found, safe to ignore */
266 if (!pdr)
267 return 0;
268
269 /* Lengths of the data in PDI and PDR must match */
270 if (pdi_len(pdi) != pdr_len(pdr))
271 return -EINVAL;
272
273 /* do the actual plugging */
David Kilroye2334182008-08-21 23:27:52 +0100274 hermes_aux_setaddr(hw, pdr_addr(pdr));
David Kilroyf482eb72008-08-21 23:27:51 +0100275 hermes_write_bytes(hw, HERMES_AUXDATA, pdi->data, pdi_len(pdi));
276
277 return 0;
278}
279
280/* Read PDA from the adapter */
David Kilroye2334182008-08-21 23:27:52 +0100281int hermes_read_pda(hermes_t *hw,
282 __le16 *pda,
283 u32 pda_addr,
284 u16 pda_len,
285 int use_eeprom) /* can we get this into hw? */
David Kilroyf482eb72008-08-21 23:27:51 +0100286{
287 int ret;
David Kilroye2334182008-08-21 23:27:52 +0100288 u16 pda_size;
289 u16 data_len = pda_len;
290 __le16 *data = pda;
David Kilroyf482eb72008-08-21 23:27:51 +0100291
David Kilroye2334182008-08-21 23:27:52 +0100292 if (use_eeprom) {
293 /* PDA of spectrum symbol is in eeprom */
294
295 /* Issue command to read EEPROM */
296 ret = hermes_docmd_wait(hw, HERMES_CMD_READMIF, 0, NULL);
297 if (ret)
298 return ret;
David Kilroy8f5ae732008-08-21 23:27:53 +0100299 } else {
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;
307 data = pda + 2;
David Kilroye2334182008-08-21 23:27:52 +0100308 }
David Kilroyf482eb72008-08-21 23:27:51 +0100309
310 /* Open auxiliary port */
David Kilroye2334182008-08-21 23:27:52 +0100311 ret = hermes_aux_control(hw, 1);
312 printk(KERN_DEBUG PFX "AUX enable returned %d\n", ret);
David Kilroyf482eb72008-08-21 23:27:51 +0100313 if (ret)
314 return ret;
315
316 /* read PDA from EEPROM */
David Kilroye2334182008-08-21 23:27:52 +0100317 hermes_aux_setaddr(hw, pda_addr);
318 hermes_read_words(hw, HERMES_AUXDATA, data, data_len / 2);
319
320 /* Close aux port */
321 ret = hermes_aux_control(hw, 0);
322 printk(KERN_DEBUG PFX "AUX disable returned %d\n", ret);
David Kilroyf482eb72008-08-21 23:27:51 +0100323
324 /* Check PDA length */
325 pda_size = le16_to_cpu(pda[0]);
David Kilroye2334182008-08-21 23:27:52 +0100326 printk(KERN_DEBUG PFX "Actual PDA length %d, Max allowed %d\n",
327 pda_size, pda_len);
David Kilroyf482eb72008-08-21 23:27:51 +0100328 if (pda_size > pda_len)
329 return -EINVAL;
330
331 return 0;
332}
David Kilroyf482eb72008-08-21 23:27:51 +0100333
David Kilroye2334182008-08-21 23:27:52 +0100334/* Parse PDA and write the records into the adapter
335 *
336 * Attempt to write every records that is in the specified pda
337 * which also has a valid production data record for the firmware.
338 */
339int hermes_apply_pda(hermes_t *hw,
340 const char *first_pdr,
David Kilroy3faa19c2009-02-21 16:52:54 +0000341 const void *pdr_end,
342 const __le16 *pda,
343 const void *pda_end)
David Kilroyf482eb72008-08-21 23:27:51 +0100344{
345 int ret;
David Kilroye2334182008-08-21 23:27:52 +0100346 const struct pdi *pdi;
David Kilroy3faa19c2009-02-21 16:52:54 +0000347 const struct pdr *pdr;
David Kilroyf482eb72008-08-21 23:27:51 +0100348
David Kilroy3faa19c2009-02-21 16:52:54 +0000349 pdr = (const struct pdr *) first_pdr;
350 pda_end -= sizeof(struct pdi);
David Kilroyf482eb72008-08-21 23:27:51 +0100351
352 /* Go through every PDI and plug them into the adapter */
David Kilroye2334182008-08-21 23:27:52 +0100353 pdi = (const struct pdi *) (pda + 2);
David Kilroy3faa19c2009-02-21 16:52:54 +0000354 while (((void *) pdi <= pda_end) &&
355 (pdi_id(pdi) != PDI_END)) {
356 ret = hermes_plug_pdi(hw, pdr, pdi, pdr_end);
David Kilroyf482eb72008-08-21 23:27:51 +0100357 if (ret)
358 return ret;
359
360 /* Increment to the next PDI */
David Kilroye2334182008-08-21 23:27:52 +0100361 pdi = (const struct pdi *) &pdi->data[pdi_len(pdi)];
David Kilroyf482eb72008-08-21 23:27:51 +0100362 }
363 return 0;
364}
David Kilroyf482eb72008-08-21 23:27:51 +0100365
David Kilroye2334182008-08-21 23:27:52 +0100366/* Identify the total number of bytes in all blocks
367 * including the header data.
368 */
369size_t
David Kilroy3faa19c2009-02-21 16:52:54 +0000370hermes_blocks_length(const char *first_block, const void *end)
David Kilroye2334182008-08-21 23:27:52 +0100371{
372 const struct dblock *blk = (const struct dblock *) first_block;
373 int total_len = 0;
374 int len;
375
David Kilroy3faa19c2009-02-21 16:52:54 +0000376 end -= sizeof(*blk);
377
David Kilroye2334182008-08-21 23:27:52 +0100378 /* Skip all blocks to locate Plug Data References
379 * (Spectrum CS) */
David Kilroy3faa19c2009-02-21 16:52:54 +0000380 while (((void *) blk <= end) &&
381 (dblock_addr(blk) != BLOCK_END)) {
David Kilroye2334182008-08-21 23:27:52 +0100382 len = dblock_len(blk);
383 total_len += sizeof(*blk) + len;
384 blk = (struct dblock *) &blk->data[len];
385 }
386
387 return total_len;
388}
David Kilroye2334182008-08-21 23:27:52 +0100389
390/*** Hermes programming ***/
391
David Kilroy8f5ae732008-08-21 23:27:53 +0100392/* About to start programming data (Hermes I)
393 * offset is the entry point
394 *
395 * Spectrum_cs' Symbol fw does not require this
396 * wl_lkm Agere fw does
397 * Don't know about intersil
398 */
399int hermesi_program_init(hermes_t *hw, u32 offset)
400{
401 int err;
402
403 /* Disable interrupts?*/
404 /*hw->inten = 0x0;*/
405 /*hermes_write_regn(hw, INTEN, 0);*/
406 /*hermes_set_irqmask(hw, 0);*/
407
408 /* Acknowledge any outstanding command */
409 hermes_write_regn(hw, EVACK, 0xFFFF);
410
411 /* Using doicmd_wait rather than docmd_wait */
412 err = hermes_doicmd_wait(hw,
413 0x0100 | HERMES_CMD_INIT,
414 0, 0, 0, NULL);
415 if (err)
416 return err;
417
418 err = hermes_doicmd_wait(hw,
419 0x0000 | HERMES_CMD_INIT,
420 0, 0, 0, NULL);
421 if (err)
422 return err;
423
424 err = hermes_aux_control(hw, 1);
425 printk(KERN_DEBUG PFX "AUX enable returned %d\n", err);
426
427 if (err)
428 return err;
429
430 printk(KERN_DEBUG PFX "Enabling volatile, EP 0x%08x\n", offset);
431 err = hermes_doicmd_wait(hw,
432 HERMES_PROGRAM_ENABLE_VOLATILE,
433 offset & 0xFFFFu,
434 offset >> 16,
435 0,
436 NULL);
437 printk(KERN_DEBUG PFX "PROGRAM_ENABLE returned %d\n",
438 err);
439
440 return err;
441}
David Kilroy8f5ae732008-08-21 23:27:53 +0100442
443/* Done programming data (Hermes I)
444 *
445 * Spectrum_cs' Symbol fw does not require this
446 * wl_lkm Agere fw does
447 * Don't know about intersil
448 */
449int hermesi_program_end(hermes_t *hw)
450{
451 struct hermes_response resp;
452 int rc = 0;
453 int err;
454
455 rc = hermes_docmd_wait(hw, HERMES_PROGRAM_DISABLE, 0, &resp);
456
457 printk(KERN_DEBUG PFX "PROGRAM_DISABLE returned %d, "
458 "r0 0x%04x, r1 0x%04x, r2 0x%04x\n",
459 rc, resp.resp0, resp.resp1, resp.resp2);
460
461 if ((rc == 0) &&
462 ((resp.status & HERMES_STATUS_CMDCODE) != HERMES_CMD_DOWNLD))
463 rc = -EIO;
464
465 err = hermes_aux_control(hw, 0);
466 printk(KERN_DEBUG PFX "AUX disable returned %d\n", err);
467
468 /* Acknowledge any outstanding command */
469 hermes_write_regn(hw, EVACK, 0xFFFF);
470
471 /* Reinitialise, ignoring return */
472 (void) hermes_doicmd_wait(hw, 0x0000 | HERMES_CMD_INIT,
473 0, 0, 0, NULL);
474
475 return rc ? rc : err;
476}
David Kilroy8f5ae732008-08-21 23:27:53 +0100477
David Kilroye2334182008-08-21 23:27:52 +0100478/* Program the data blocks */
David Kilroy3faa19c2009-02-21 16:52:54 +0000479int hermes_program(hermes_t *hw, const char *first_block, const void *end)
David Kilroyf482eb72008-08-21 23:27:51 +0100480{
481 const struct dblock *blk;
482 u32 blkaddr;
483 u32 blklen;
David Kilroye2334182008-08-21 23:27:52 +0100484#if LIMIT_PROGRAM_SIZE
485 u32 addr;
486 u32 len;
487#endif
David Kilroyf482eb72008-08-21 23:27:51 +0100488
David Kilroye2334182008-08-21 23:27:52 +0100489 blk = (const struct dblock *) first_block;
490
David Kilroy3faa19c2009-02-21 16:52:54 +0000491 if ((void *) blk > (end - sizeof(*blk)))
David Kilroye2334182008-08-21 23:27:52 +0100492 return -EIO;
493
David Kilroyf482eb72008-08-21 23:27:51 +0100494 blkaddr = dblock_addr(blk);
495 blklen = dblock_len(blk);
496
David Kilroye2334182008-08-21 23:27:52 +0100497 while ((blkaddr != BLOCK_END) &&
David Kilroy3faa19c2009-02-21 16:52:54 +0000498 (((void *) blk + blklen) <= end)) {
David Kilroye2334182008-08-21 23:27:52 +0100499 printk(KERN_DEBUG PFX
500 "Programming block of length %d to address 0x%08x\n",
501 blklen, blkaddr);
502
503#if !LIMIT_PROGRAM_SIZE
504 /* wl_lkm driver splits this into writes of 2000 bytes */
505 hermes_aux_setaddr(hw, blkaddr);
David Kilroyf482eb72008-08-21 23:27:51 +0100506 hermes_write_bytes(hw, HERMES_AUXDATA, blk->data,
507 blklen);
David Kilroye2334182008-08-21 23:27:52 +0100508#else
509 len = (blklen < MAX_DL_SIZE) ? blklen : MAX_DL_SIZE;
510 addr = blkaddr;
David Kilroyf482eb72008-08-21 23:27:51 +0100511
David Kilroye2334182008-08-21 23:27:52 +0100512 while (addr < (blkaddr + blklen)) {
513 printk(KERN_DEBUG PFX
514 "Programming subblock of length %d "
515 "to address 0x%08x. Data @ %p\n",
516 len, addr, &blk->data[addr - blkaddr]);
517
518 hermes_aux_setaddr(hw, addr);
519 hermes_write_bytes(hw, HERMES_AUXDATA,
520 &blk->data[addr - blkaddr],
521 len);
522
523 addr += len;
524 len = ((blkaddr + blklen - addr) < MAX_DL_SIZE) ?
525 (blkaddr + blklen - addr) : MAX_DL_SIZE;
526 }
527#endif
528 blk = (const struct dblock *) &blk->data[blklen];
529
David Kilroy3faa19c2009-02-21 16:52:54 +0000530 if ((void *) blk > (end - sizeof(*blk)))
David Kilroye2334182008-08-21 23:27:52 +0100531 return -EIO;
532
David Kilroyf482eb72008-08-21 23:27:51 +0100533 blkaddr = dblock_addr(blk);
534 blklen = dblock_len(blk);
535 }
536 return 0;
537}
David Kilroy8f5ae732008-08-21 23:27:53 +0100538
539/*** Default plugging data for Hermes I ***/
540/* Values from wl_lkm_718/hcf/dhf.c */
541
542#define DEFINE_DEFAULT_PDR(pid, length, data) \
543static const struct { \
544 __le16 len; \
545 __le16 id; \
546 u8 val[length]; \
547} __attribute__ ((packed)) default_pdr_data_##pid = { \
David Kilroy3faa19c2009-02-21 16:52:54 +0000548 cpu_to_le16((sizeof(default_pdr_data_##pid)/ \
David Kilroy8f5ae732008-08-21 23:27:53 +0100549 sizeof(__le16)) - 1), \
David Kilroy3faa19c2009-02-21 16:52:54 +0000550 cpu_to_le16(pid), \
David Kilroy8f5ae732008-08-21 23:27:53 +0100551 data \
552}
553
554#define DEFAULT_PDR(pid) default_pdr_data_##pid
555
556/* HWIF Compatiblity */
557DEFINE_DEFAULT_PDR(0x0005, 10, "\x00\x00\x06\x00\x01\x00\x01\x00\x01\x00");
558
559/* PPPPSign */
560DEFINE_DEFAULT_PDR(0x0108, 4, "\x00\x00\x00\x00");
561
562/* PPPPProf */
563DEFINE_DEFAULT_PDR(0x0109, 10, "\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00");
564
565/* Antenna diversity */
566DEFINE_DEFAULT_PDR(0x0150, 2, "\x00\x3F");
567
568/* Modem VCO band Set-up */
569DEFINE_DEFAULT_PDR(0x0160, 28,
570 "\x00\x00\x00\x00\x00\x00\x00\x00"
571 "\x00\x00\x00\x00\x00\x00\x00\x00"
572 "\x00\x00\x00\x00\x00\x00\x00\x00"
573 "\x00\x00\x00\x00");
574
575/* Modem Rx Gain Table Values */
576DEFINE_DEFAULT_PDR(0x0161, 256,
577 "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
578 "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
579 "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
580 "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
581 "\x3F\x01\x3E\01\x3E\x01\x3D\x01"
582 "\x3D\x01\x3C\01\x3C\x01\x3B\x01"
583 "\x3B\x01\x3A\01\x3A\x01\x39\x01"
584 "\x39\x01\x38\01\x38\x01\x37\x01"
585 "\x37\x01\x36\01\x36\x01\x35\x01"
586 "\x35\x01\x34\01\x34\x01\x33\x01"
587 "\x33\x01\x32\x01\x32\x01\x31\x01"
588 "\x31\x01\x30\x01\x30\x01\x7B\x01"
589 "\x7B\x01\x7A\x01\x7A\x01\x79\x01"
590 "\x79\x01\x78\x01\x78\x01\x77\x01"
591 "\x77\x01\x76\x01\x76\x01\x75\x01"
592 "\x75\x01\x74\x01\x74\x01\x73\x01"
593 "\x73\x01\x72\x01\x72\x01\x71\x01"
594 "\x71\x01\x70\x01\x70\x01\x68\x01"
595 "\x68\x01\x67\x01\x67\x01\x66\x01"
596 "\x66\x01\x65\x01\x65\x01\x57\x01"
597 "\x57\x01\x56\x01\x56\x01\x55\x01"
598 "\x55\x01\x54\x01\x54\x01\x53\x01"
599 "\x53\x01\x52\x01\x52\x01\x51\x01"
600 "\x51\x01\x50\x01\x50\x01\x48\x01"
601 "\x48\x01\x47\x01\x47\x01\x46\x01"
602 "\x46\x01\x45\x01\x45\x01\x44\x01"
603 "\x44\x01\x43\x01\x43\x01\x42\x01"
604 "\x42\x01\x41\x01\x41\x01\x40\x01"
605 "\x40\x01\x40\x01\x40\x01\x40\x01"
606 "\x40\x01\x40\x01\x40\x01\x40\x01"
607 "\x40\x01\x40\x01\x40\x01\x40\x01"
608 "\x40\x01\x40\x01\x40\x01\x40\x01");
609
610/* Write PDA according to certain rules.
611 *
612 * For every production data record, look for a previous setting in
613 * the pda, and use that.
614 *
615 * For certain records, use defaults if they are not found in pda.
616 */
617int hermes_apply_pda_with_defaults(hermes_t *hw,
618 const char *first_pdr,
David Kilroy3faa19c2009-02-21 16:52:54 +0000619 const void *pdr_end,
620 const __le16 *pda,
621 const void *pda_end)
David Kilroy8f5ae732008-08-21 23:27:53 +0100622{
623 const struct pdr *pdr = (const struct pdr *) first_pdr;
David Kilroy3faa19c2009-02-21 16:52:54 +0000624 const struct pdi *first_pdi = (const struct pdi *) &pda[2];
625 const struct pdi *pdi;
626 const struct pdi *default_pdi = NULL;
627 const struct pdi *outdoor_pdi;
David Kilroy8f5ae732008-08-21 23:27:53 +0100628 int record_id;
629
David Kilroy3faa19c2009-02-21 16:52:54 +0000630 pdr_end -= sizeof(struct pdr);
631
632 while (((void *) pdr <= pdr_end) &&
David Kilroy8f5ae732008-08-21 23:27:53 +0100633 (pdr_id(pdr) != PDI_END)) {
634 /*
635 * For spectrum_cs firmwares,
636 * PDR area is currently not terminated by PDI_END.
637 * It's followed by CRC records, which have the type
638 * field where PDR has length. The type can be 0 or 1.
639 */
640 if (pdr_len(pdr) < 2)
641 break;
642 record_id = pdr_id(pdr);
643
David Kilroy3faa19c2009-02-21 16:52:54 +0000644 pdi = hermes_find_pdi(first_pdi, record_id, pda_end);
David Kilroy8f5ae732008-08-21 23:27:53 +0100645 if (pdi)
646 printk(KERN_DEBUG PFX "Found record 0x%04x at %p\n",
647 record_id, pdi);
648
649 switch (record_id) {
650 case 0x110: /* Modem REFDAC values */
651 case 0x120: /* Modem VGDAC values */
David Kilroy3faa19c2009-02-21 16:52:54 +0000652 outdoor_pdi = hermes_find_pdi(first_pdi, record_id + 1,
653 pda_end);
David Kilroy8f5ae732008-08-21 23:27:53 +0100654 default_pdi = NULL;
655 if (outdoor_pdi) {
656 pdi = outdoor_pdi;
657 printk(KERN_DEBUG PFX
658 "Using outdoor record 0x%04x at %p\n",
659 record_id + 1, pdi);
660 }
661 break;
662 case 0x5: /* HWIF Compatiblity */
663 default_pdi = (struct pdi *) &DEFAULT_PDR(0x0005);
664 break;
665 case 0x108: /* PPPPSign */
666 default_pdi = (struct pdi *) &DEFAULT_PDR(0x0108);
667 break;
668 case 0x109: /* PPPPProf */
669 default_pdi = (struct pdi *) &DEFAULT_PDR(0x0109);
670 break;
671 case 0x150: /* Antenna diversity */
672 default_pdi = (struct pdi *) &DEFAULT_PDR(0x0150);
673 break;
674 case 0x160: /* Modem VCO band Set-up */
675 default_pdi = (struct pdi *) &DEFAULT_PDR(0x0160);
676 break;
677 case 0x161: /* Modem Rx Gain Table Values */
678 default_pdi = (struct pdi *) &DEFAULT_PDR(0x0161);
679 break;
680 default:
681 default_pdi = NULL;
682 break;
683 }
684 if (!pdi && default_pdi) {
685 /* Use default */
686 pdi = default_pdi;
687 printk(KERN_DEBUG PFX
688 "Using default record 0x%04x at %p\n",
689 record_id, pdi);
690 }
691
692 if (pdi) {
693 /* Lengths of the data in PDI and PDR must match */
David Kilroy3faa19c2009-02-21 16:52:54 +0000694 if ((pdi_len(pdi) == pdr_len(pdr)) &&
695 ((void *) pdi->data + pdi_len(pdi) < pda_end)) {
David Kilroy8f5ae732008-08-21 23:27:53 +0100696 /* do the actual plugging */
697 hermes_aux_setaddr(hw, pdr_addr(pdr));
698 hermes_write_bytes(hw, HERMES_AUXDATA,
699 pdi->data, pdi_len(pdi));
700 }
701 }
702
703 pdr++;
704 }
705 return 0;
706}