diff options
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r-- | drivers/net/wireless/hermes_dld.c | 285 | ||||
-rw-r--r-- | drivers/net/wireless/hermes_dld.h | 5 |
2 files changed, 290 insertions, 0 deletions
diff --git a/drivers/net/wireless/hermes_dld.c b/drivers/net/wireless/hermes_dld.c index 22ae79dae41e..d8c626e61a3a 100644 --- a/drivers/net/wireless/hermes_dld.c +++ b/drivers/net/wireless/hermes_dld.c | |||
@@ -70,6 +70,12 @@ MODULE_LICENSE("Dual MPL/GPL"); | |||
70 | #define HERMES_AUX_PW1 0xDC23 | 70 | #define HERMES_AUX_PW1 0xDC23 |
71 | #define HERMES_AUX_PW2 0xBA45 | 71 | #define HERMES_AUX_PW2 0xBA45 |
72 | 72 | ||
73 | /* 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 | |||
73 | /* End markers used in dblocks */ | 79 | /* End markers used in dblocks */ |
74 | #define PDI_END 0x00000000 /* End of PDA */ | 80 | #define PDI_END 0x00000000 /* End of PDA */ |
75 | #define BLOCK_END 0xFFFFFFFF /* Last image block */ | 81 | #define BLOCK_END 0xFFFFFFFF /* Last image block */ |
@@ -247,6 +253,23 @@ hermes_find_pdr(struct pdr *first_pdr, u32 record_id) | |||
247 | return NULL; | 253 | return NULL; |
248 | } | 254 | } |
249 | 255 | ||
256 | /* Scan production data items for a particular entry */ | ||
257 | static struct pdi * | ||
258 | hermes_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 | |||
250 | /* Process one Plug Data Item - find corresponding PDR and plug it */ | 273 | /* Process one Plug Data Item - find corresponding PDR and plug it */ |
251 | static int | 274 | static int |
252 | hermes_plug_pdi(hermes_t *hw, struct pdr *first_pdr, const struct pdi *pdi) | 275 | hermes_plug_pdi(hermes_t *hw, struct pdr *first_pdr, const struct pdi *pdi) |
@@ -290,6 +313,15 @@ int hermes_read_pda(hermes_t *hw, | |||
290 | ret = hermes_docmd_wait(hw, HERMES_CMD_READMIF, 0, NULL); | 313 | ret = hermes_docmd_wait(hw, HERMES_CMD_READMIF, 0, NULL); |
291 | if (ret) | 314 | if (ret) |
292 | return ret; | 315 | return ret; |
316 | } 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; | ||
293 | } | 325 | } |
294 | 326 | ||
295 | /* Open auxiliary port */ | 327 | /* Open auxiliary port */ |
@@ -370,6 +402,94 @@ EXPORT_SYMBOL(hermes_blocks_length); | |||
370 | 402 | ||
371 | /*** Hermes programming ***/ | 403 | /*** Hermes programming ***/ |
372 | 404 | ||
405 | /* 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 | */ | ||
412 | int 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 | } | ||
455 | EXPORT_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 | */ | ||
463 | int 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 | } | ||
491 | EXPORT_SYMBOL(hermesi_program_end); | ||
492 | |||
373 | /* Program the data blocks */ | 493 | /* Program the data blocks */ |
374 | int hermes_program(hermes_t *hw, const char *first_block, const char *end) | 494 | int hermes_program(hermes_t *hw, const char *first_block, const char *end) |
375 | { | 495 | { |
@@ -443,3 +563,168 @@ static void __exit exit_hermes_dld(void) | |||
443 | 563 | ||
444 | module_init(init_hermes_dld); | 564 | module_init(init_hermes_dld); |
445 | module_exit(exit_hermes_dld); | 565 | module_exit(exit_hermes_dld); |
566 | |||
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) \ | ||
571 | static 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 */ | ||
585 | DEFINE_DEFAULT_PDR(0x0005, 10, "\x00\x00\x06\x00\x01\x00\x01\x00\x01\x00"); | ||
586 | |||
587 | /* PPPPSign */ | ||
588 | DEFINE_DEFAULT_PDR(0x0108, 4, "\x00\x00\x00\x00"); | ||
589 | |||
590 | /* PPPPProf */ | ||
591 | DEFINE_DEFAULT_PDR(0x0109, 10, "\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00"); | ||
592 | |||
593 | /* Antenna diversity */ | ||
594 | DEFINE_DEFAULT_PDR(0x0150, 2, "\x00\x3F"); | ||
595 | |||
596 | /* Modem VCO band Set-up */ | ||
597 | DEFINE_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 */ | ||
604 | DEFINE_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 | */ | ||
645 | int 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 | } | ||
730 | EXPORT_SYMBOL(hermes_apply_pda_with_defaults); | ||
diff --git a/drivers/net/wireless/hermes_dld.h b/drivers/net/wireless/hermes_dld.h index af75c030b11b..6fcb26277999 100644 --- a/drivers/net/wireless/hermes_dld.h +++ b/drivers/net/wireless/hermes_dld.h | |||
@@ -27,6 +27,8 @@ | |||
27 | 27 | ||
28 | #include "hermes.h" | 28 | #include "hermes.h" |
29 | 29 | ||
30 | int hermesi_program_init(hermes_t *hw, u32 offset); | ||
31 | int hermesi_program_end(hermes_t *hw); | ||
30 | int hermes_program(hermes_t *hw, const char *first_block, const char *end); | 32 | int hermes_program(hermes_t *hw, const char *first_block, const char *end); |
31 | 33 | ||
32 | int hermes_read_pda(hermes_t *hw, | 34 | int hermes_read_pda(hermes_t *hw, |
@@ -37,6 +39,9 @@ int hermes_read_pda(hermes_t *hw, | |||
37 | int hermes_apply_pda(hermes_t *hw, | 39 | int hermes_apply_pda(hermes_t *hw, |
38 | const char *first_pdr, | 40 | const char *first_pdr, |
39 | const __le16 *pda); | 41 | const __le16 *pda); |
42 | int hermes_apply_pda_with_defaults(hermes_t *hw, | ||
43 | const char *first_pdr, | ||
44 | const __le16 *pda); | ||
40 | 45 | ||
41 | size_t hermes_blocks_length(const char *first_block); | 46 | size_t hermes_blocks_length(const char *first_block); |
42 | 47 | ||