diff options
author | David Kilroy <kilroyd@googlemail.com> | 2010-05-01 09:05:43 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-05-03 14:53:09 -0400 |
commit | 07cefe7ac983374ee4c369f1d4aee3093bf3b44f (patch) | |
tree | c4db80ac5a3f28bda1a5c93fb9b88b7db728898e /drivers/net/wireless/orinoco/hermes_dld.c | |
parent | fc97431a50962e66c052ec6909d4b2582efd3554 (diff) |
orinoco_usb: implement fw download
This involves some refactorring of the common fw download code to
substitute ezusb versions of various functions.
Note that WPA-enabled firmwares (9.xx series) will not work fully with
orinoco_usb yet.
Signed-off-by: David Kilroy <kilroyd@googlemail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/orinoco/hermes_dld.c')
-rw-r--r-- | drivers/net/wireless/orinoco/hermes_dld.c | 243 |
1 files changed, 9 insertions, 234 deletions
diff --git a/drivers/net/wireless/orinoco/hermes_dld.c b/drivers/net/wireless/orinoco/hermes_dld.c index 8f22e2026a82..6da85e75fce0 100644 --- a/drivers/net/wireless/orinoco/hermes_dld.c +++ b/drivers/net/wireless/orinoco/hermes_dld.c | |||
@@ -46,37 +46,11 @@ | |||
46 | 46 | ||
47 | #define PFX "hermes_dld: " | 47 | #define PFX "hermes_dld: " |
48 | 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 */ | ||
57 | #define HERMES_AUX_DISABLED 0x0000 /* Auxiliary port is closed */ | ||
58 | |||
59 | #define HERMES_AUX_PW0 0xFE01 | ||
60 | #define HERMES_AUX_PW1 0xDC23 | ||
61 | #define HERMES_AUX_PW2 0xBA45 | ||
62 | |||
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) | ||
68 | |||
69 | /* End markers used in dblocks */ | 49 | /* End markers used in dblocks */ |
70 | #define PDI_END 0x00000000 /* End of PDA */ | 50 | #define PDI_END 0x00000000 /* End of PDA */ |
71 | #define BLOCK_END 0xFFFFFFFF /* Last image block */ | 51 | #define BLOCK_END 0xFFFFFFFF /* Last image block */ |
72 | #define TEXT_END 0x1A /* End of text header */ | 52 | #define TEXT_END 0x1A /* End of text header */ |
73 | 53 | ||
74 | /* 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 | ||
79 | |||
80 | /* | 54 | /* |
81 | * The following structures have little-endian fields denoted by | 55 | * The following structures have little-endian fields denoted by |
82 | * the leading underscore. Don't access them directly - use inline | 56 | * the leading underscore. Don't access them directly - use inline |
@@ -165,41 +139,6 @@ pdi_len(const struct pdi *pdi) | |||
165 | return 2 * (le16_to_cpu(pdi->len) - 1); | 139 | return 2 * (le16_to_cpu(pdi->len) - 1); |
166 | } | 140 | } |
167 | 141 | ||
168 | /*** Hermes AUX control ***/ | ||
169 | |||
170 | static inline void | ||
171 | hermes_aux_setaddr(hermes_t *hw, u32 addr) | ||
172 | { | ||
173 | hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7)); | ||
174 | hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F)); | ||
175 | } | ||
176 | |||
177 | static inline int | ||
178 | hermes_aux_control(hermes_t *hw, int enabled) | ||
179 | { | ||
180 | int desired_state = enabled ? HERMES_AUX_ENABLED : HERMES_AUX_DISABLED; | ||
181 | int action = enabled ? HERMES_AUX_ENABLE : HERMES_AUX_DISABLE; | ||
182 | int i; | ||
183 | |||
184 | /* Already open? */ | ||
185 | if (hermes_read_reg(hw, HERMES_CONTROL) == desired_state) | ||
186 | 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); | ||
191 | hermes_write_reg(hw, HERMES_CONTROL, action); | ||
192 | |||
193 | for (i = 0; i < 20; i++) { | ||
194 | udelay(10); | ||
195 | if (hermes_read_reg(hw, HERMES_CONTROL) == | ||
196 | desired_state) | ||
197 | return 0; | ||
198 | } | ||
199 | |||
200 | return -EBUSY; | ||
201 | } | ||
202 | |||
203 | /*** Plug Data Functions ***/ | 142 | /*** Plug Data Functions ***/ |
204 | 143 | ||
205 | /* | 144 | /* |
@@ -271,62 +210,7 @@ hermes_plug_pdi(hermes_t *hw, const struct pdr *first_pdr, | |||
271 | return -EINVAL; | 210 | return -EINVAL; |
272 | 211 | ||
273 | /* do the actual plugging */ | 212 | /* do the actual plugging */ |
274 | hermes_aux_setaddr(hw, pdr_addr(pdr)); | 213 | hw->ops->program(hw, pdi->data, pdr_addr(pdr), pdi_len(pdi)); |
275 | hermes_write_bytes(hw, HERMES_AUXDATA, pdi->data, pdi_len(pdi)); | ||
276 | |||
277 | return 0; | ||
278 | } | ||
279 | |||
280 | /* Read PDA from the adapter */ | ||
281 | int 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? */ | ||
286 | { | ||
287 | int ret; | ||
288 | u16 pda_size; | ||
289 | u16 data_len = pda_len; | ||
290 | __le16 *data = pda; | ||
291 | |||
292 | if (use_eeprom) { | ||
293 | /* PDA of spectrum symbol is in eeprom */ | ||
294 | |||
295 | /* Issue command to read EEPROM */ | ||
296 | ret = hw->ops->cmd_wait(hw, HERMES_CMD_READMIF, 0, NULL); | ||
297 | if (ret) | ||
298 | return ret; | ||
299 | } 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; | ||
308 | } | ||
309 | |||
310 | /* Open auxiliary port */ | ||
311 | ret = hermes_aux_control(hw, 1); | ||
312 | pr_debug(PFX "AUX enable returned %d\n", ret); | ||
313 | if (ret) | ||
314 | return ret; | ||
315 | |||
316 | /* read PDA from EEPROM */ | ||
317 | 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 | pr_debug(PFX "AUX disable returned %d\n", ret); | ||
323 | |||
324 | /* Check PDA length */ | ||
325 | pda_size = le16_to_cpu(pda[0]); | ||
326 | pr_debug(PFX "Actual PDA length %d, Max allowed %d\n", | ||
327 | pda_size, pda_len); | ||
328 | if (pda_size > pda_len) | ||
329 | return -EINVAL; | ||
330 | 214 | ||
331 | return 0; | 215 | return 0; |
332 | } | 216 | } |
@@ -389,101 +273,13 @@ hermes_blocks_length(const char *first_block, const void *end) | |||
389 | 273 | ||
390 | /*** Hermes programming ***/ | 274 | /*** Hermes programming ***/ |
391 | 275 | ||
392 | /* 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 | */ | ||
399 | int 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 init_cmd_wait rather than cmd_wait */ | ||
412 | err = hw->ops->init_cmd_wait(hw, | ||
413 | 0x0100 | HERMES_CMD_INIT, | ||
414 | 0, 0, 0, NULL); | ||
415 | if (err) | ||
416 | return err; | ||
417 | |||
418 | err = hw->ops->init_cmd_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 | pr_debug(PFX "AUX enable returned %d\n", err); | ||
426 | |||
427 | if (err) | ||
428 | return err; | ||
429 | |||
430 | pr_debug(PFX "Enabling volatile, EP 0x%08x\n", offset); | ||
431 | err = hw->ops->init_cmd_wait(hw, | ||
432 | HERMES_PROGRAM_ENABLE_VOLATILE, | ||
433 | offset & 0xFFFFu, | ||
434 | offset >> 16, | ||
435 | 0, | ||
436 | NULL); | ||
437 | pr_debug(PFX "PROGRAM_ENABLE returned %d\n", err); | ||
438 | |||
439 | return err; | ||
440 | } | ||
441 | |||
442 | /* Done programming data (Hermes I) | ||
443 | * | ||
444 | * Spectrum_cs' Symbol fw does not require this | ||
445 | * wl_lkm Agere fw does | ||
446 | * Don't know about intersil | ||
447 | */ | ||
448 | int hermesi_program_end(hermes_t *hw) | ||
449 | { | ||
450 | struct hermes_response resp; | ||
451 | int rc = 0; | ||
452 | int err; | ||
453 | |||
454 | rc = hw->ops->cmd_wait(hw, HERMES_PROGRAM_DISABLE, 0, &resp); | ||
455 | |||
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); | ||
459 | |||
460 | if ((rc == 0) && | ||
461 | ((resp.status & HERMES_STATUS_CMDCODE) != HERMES_CMD_DOWNLD)) | ||
462 | rc = -EIO; | ||
463 | |||
464 | err = hermes_aux_control(hw, 0); | ||
465 | pr_debug(PFX "AUX disable returned %d\n", err); | ||
466 | |||
467 | /* Acknowledge any outstanding command */ | ||
468 | hermes_write_regn(hw, EVACK, 0xFFFF); | ||
469 | |||
470 | /* Reinitialise, ignoring return */ | ||
471 | (void) hw->ops->init_cmd_wait(hw, 0x0000 | HERMES_CMD_INIT, | ||
472 | 0, 0, 0, NULL); | ||
473 | |||
474 | return rc ? rc : err; | ||
475 | } | ||
476 | |||
477 | /* Program the data blocks */ | 276 | /* Program the data blocks */ |
478 | int hermes_program(hermes_t *hw, const char *first_block, const void *end) | 277 | int hermes_program(hermes_t *hw, const char *first_block, const void *end) |
479 | { | 278 | { |
480 | const struct dblock *blk; | 279 | const struct dblock *blk; |
481 | u32 blkaddr; | 280 | u32 blkaddr; |
482 | u32 blklen; | 281 | u32 blklen; |
483 | #if LIMIT_PROGRAM_SIZE | 282 | int err = 0; |
484 | u32 addr; | ||
485 | u32 len; | ||
486 | #endif | ||
487 | 283 | ||
488 | blk = (const struct dblock *) first_block; | 284 | blk = (const struct dblock *) first_block; |
489 | 285 | ||
@@ -498,30 +294,10 @@ int hermes_program(hermes_t *hw, const char *first_block, const void *end) | |||
498 | pr_debug(PFX "Programming block of length %d " | 294 | pr_debug(PFX "Programming block of length %d " |
499 | "to address 0x%08x\n", blklen, blkaddr); | 295 | "to address 0x%08x\n", blklen, blkaddr); |
500 | 296 | ||
501 | #if !LIMIT_PROGRAM_SIZE | 297 | err = hw->ops->program(hw, blk->data, blkaddr, blklen); |
502 | /* wl_lkm driver splits this into writes of 2000 bytes */ | 298 | if (err) |
503 | hermes_aux_setaddr(hw, blkaddr); | 299 | break; |
504 | hermes_write_bytes(hw, HERMES_AUXDATA, blk->data, | 300 | |
505 | blklen); | ||
506 | #else | ||
507 | len = (blklen < MAX_DL_SIZE) ? blklen : MAX_DL_SIZE; | ||
508 | addr = blkaddr; | ||
509 | |||
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]); | ||
514 | |||
515 | hermes_aux_setaddr(hw, addr); | ||
516 | hermes_write_bytes(hw, HERMES_AUXDATA, | ||
517 | &blk->data[addr - blkaddr], | ||
518 | len); | ||
519 | |||
520 | addr += len; | ||
521 | len = ((blkaddr + blklen - addr) < MAX_DL_SIZE) ? | ||
522 | (blkaddr + blklen - addr) : MAX_DL_SIZE; | ||
523 | } | ||
524 | #endif | ||
525 | blk = (const struct dblock *) &blk->data[blklen]; | 301 | blk = (const struct dblock *) &blk->data[blklen]; |
526 | 302 | ||
527 | if ((void *) blk > (end - sizeof(*blk))) | 303 | if ((void *) blk > (end - sizeof(*blk))) |
@@ -530,7 +306,7 @@ int hermes_program(hermes_t *hw, const char *first_block, const void *end) | |||
530 | blkaddr = dblock_addr(blk); | 306 | blkaddr = dblock_addr(blk); |
531 | blklen = dblock_len(blk); | 307 | blklen = dblock_len(blk); |
532 | } | 308 | } |
533 | return 0; | 309 | return err; |
534 | } | 310 | } |
535 | 311 | ||
536 | /*** Default plugging data for Hermes I ***/ | 312 | /*** Default plugging data for Hermes I ***/ |
@@ -690,9 +466,8 @@ int hermes_apply_pda_with_defaults(hermes_t *hw, | |||
690 | if ((pdi_len(pdi) == pdr_len(pdr)) && | 466 | if ((pdi_len(pdi) == pdr_len(pdr)) && |
691 | ((void *) pdi->data + pdi_len(pdi) < pda_end)) { | 467 | ((void *) pdi->data + pdi_len(pdi) < pda_end)) { |
692 | /* do the actual plugging */ | 468 | /* do the actual plugging */ |
693 | hermes_aux_setaddr(hw, pdr_addr(pdr)); | 469 | hw->ops->program(hw, pdi->data, pdr_addr(pdr), |
694 | hermes_write_bytes(hw, HERMES_AUXDATA, | 470 | pdi_len(pdi)); |
695 | pdi->data, pdi_len(pdi)); | ||
696 | } | 471 | } |
697 | } | 472 | } |
698 | 473 | ||