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.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.c')
-rw-r--r-- | drivers/net/wireless/orinoco/hermes.c | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/drivers/net/wireless/orinoco/hermes.c b/drivers/net/wireless/orinoco/hermes.c index 845693fb25f3..6c6a23e08df6 100644 --- a/drivers/net/wireless/orinoco/hermes.c +++ b/drivers/net/wireless/orinoco/hermes.c | |||
@@ -52,6 +52,26 @@ | |||
52 | #define ALLOC_COMPL_TIMEOUT (1000) /* in iterations of ~10us */ | 52 | #define ALLOC_COMPL_TIMEOUT (1000) /* in iterations of ~10us */ |
53 | 53 | ||
54 | /* | 54 | /* |
55 | * AUX port access. To unlock the AUX port write the access keys to the | ||
56 | * PARAM0-2 registers, then write HERMES_AUX_ENABLE to the HERMES_CONTROL | ||
57 | * register. Then read it and make sure it's HERMES_AUX_ENABLED. | ||
58 | */ | ||
59 | #define HERMES_AUX_ENABLE 0x8000 /* Enable auxiliary port access */ | ||
60 | #define HERMES_AUX_DISABLE 0x4000 /* Disable to auxiliary port access */ | ||
61 | #define HERMES_AUX_ENABLED 0xC000 /* Auxiliary port is open */ | ||
62 | #define HERMES_AUX_DISABLED 0x0000 /* Auxiliary port is closed */ | ||
63 | |||
64 | #define HERMES_AUX_PW0 0xFE01 | ||
65 | #define HERMES_AUX_PW1 0xDC23 | ||
66 | #define HERMES_AUX_PW2 0xBA45 | ||
67 | |||
68 | /* HERMES_CMD_DOWNLD */ | ||
69 | #define HERMES_PROGRAM_DISABLE (0x0000 | HERMES_CMD_DOWNLD) | ||
70 | #define HERMES_PROGRAM_ENABLE_VOLATILE (0x0100 | HERMES_CMD_DOWNLD) | ||
71 | #define HERMES_PROGRAM_ENABLE_NON_VOLATILE (0x0200 | HERMES_CMD_DOWNLD) | ||
72 | #define HERMES_PROGRAM_NON_VOLATILE (0x0300 | HERMES_CMD_DOWNLD) | ||
73 | |||
74 | /* | ||
55 | * Debugging helpers | 75 | * Debugging helpers |
56 | */ | 76 | */ |
57 | 77 | ||
@@ -170,6 +190,7 @@ void hermes_struct_init(hermes_t *hw, void __iomem *address, int reg_spacing) | |||
170 | hw->iobase = address; | 190 | hw->iobase = address; |
171 | hw->reg_spacing = reg_spacing; | 191 | hw->reg_spacing = reg_spacing; |
172 | hw->inten = 0x0; | 192 | hw->inten = 0x0; |
193 | hw->eeprom_pda = false; | ||
173 | hw->ops = &hermes_ops_local; | 194 | hw->ops = &hermes_ops_local; |
174 | } | 195 | } |
175 | EXPORT_SYMBOL(hermes_struct_init); | 196 | EXPORT_SYMBOL(hermes_struct_init); |
@@ -529,6 +550,189 @@ static int hermes_write_ltv(hermes_t *hw, int bap, u16 rid, | |||
529 | return err; | 550 | return err; |
530 | } | 551 | } |
531 | 552 | ||
553 | /*** Hermes AUX control ***/ | ||
554 | |||
555 | static inline void | ||
556 | hermes_aux_setaddr(hermes_t *hw, u32 addr) | ||
557 | { | ||
558 | hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7)); | ||
559 | hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F)); | ||
560 | } | ||
561 | |||
562 | static inline int | ||
563 | hermes_aux_control(hermes_t *hw, int enabled) | ||
564 | { | ||
565 | int desired_state = enabled ? HERMES_AUX_ENABLED : HERMES_AUX_DISABLED; | ||
566 | int action = enabled ? HERMES_AUX_ENABLE : HERMES_AUX_DISABLE; | ||
567 | int i; | ||
568 | |||
569 | /* Already open? */ | ||
570 | if (hermes_read_reg(hw, HERMES_CONTROL) == desired_state) | ||
571 | return 0; | ||
572 | |||
573 | hermes_write_reg(hw, HERMES_PARAM0, HERMES_AUX_PW0); | ||
574 | hermes_write_reg(hw, HERMES_PARAM1, HERMES_AUX_PW1); | ||
575 | hermes_write_reg(hw, HERMES_PARAM2, HERMES_AUX_PW2); | ||
576 | hermes_write_reg(hw, HERMES_CONTROL, action); | ||
577 | |||
578 | for (i = 0; i < 20; i++) { | ||
579 | udelay(10); | ||
580 | if (hermes_read_reg(hw, HERMES_CONTROL) == | ||
581 | desired_state) | ||
582 | return 0; | ||
583 | } | ||
584 | |||
585 | return -EBUSY; | ||
586 | } | ||
587 | |||
588 | /*** Hermes programming ***/ | ||
589 | |||
590 | /* About to start programming data (Hermes I) | ||
591 | * offset is the entry point | ||
592 | * | ||
593 | * Spectrum_cs' Symbol fw does not require this | ||
594 | * wl_lkm Agere fw does | ||
595 | * Don't know about intersil | ||
596 | */ | ||
597 | static int hermesi_program_init(hermes_t *hw, u32 offset) | ||
598 | { | ||
599 | int err; | ||
600 | |||
601 | /* Disable interrupts?*/ | ||
602 | /*hw->inten = 0x0;*/ | ||
603 | /*hermes_write_regn(hw, INTEN, 0);*/ | ||
604 | /*hermes_set_irqmask(hw, 0);*/ | ||
605 | |||
606 | /* Acknowledge any outstanding command */ | ||
607 | hermes_write_regn(hw, EVACK, 0xFFFF); | ||
608 | |||
609 | /* Using init_cmd_wait rather than cmd_wait */ | ||
610 | err = hw->ops->init_cmd_wait(hw, | ||
611 | 0x0100 | HERMES_CMD_INIT, | ||
612 | 0, 0, 0, NULL); | ||
613 | if (err) | ||
614 | return err; | ||
615 | |||
616 | err = hw->ops->init_cmd_wait(hw, | ||
617 | 0x0000 | HERMES_CMD_INIT, | ||
618 | 0, 0, 0, NULL); | ||
619 | if (err) | ||
620 | return err; | ||
621 | |||
622 | err = hermes_aux_control(hw, 1); | ||
623 | pr_debug("AUX enable returned %d\n", err); | ||
624 | |||
625 | if (err) | ||
626 | return err; | ||
627 | |||
628 | pr_debug("Enabling volatile, EP 0x%08x\n", offset); | ||
629 | err = hw->ops->init_cmd_wait(hw, | ||
630 | HERMES_PROGRAM_ENABLE_VOLATILE, | ||
631 | offset & 0xFFFFu, | ||
632 | offset >> 16, | ||
633 | 0, | ||
634 | NULL); | ||
635 | pr_debug("PROGRAM_ENABLE returned %d\n", err); | ||
636 | |||
637 | return err; | ||
638 | } | ||
639 | |||
640 | /* Done programming data (Hermes I) | ||
641 | * | ||
642 | * Spectrum_cs' Symbol fw does not require this | ||
643 | * wl_lkm Agere fw does | ||
644 | * Don't know about intersil | ||
645 | */ | ||
646 | static int hermesi_program_end(hermes_t *hw) | ||
647 | { | ||
648 | struct hermes_response resp; | ||
649 | int rc = 0; | ||
650 | int err; | ||
651 | |||
652 | rc = hw->ops->cmd_wait(hw, HERMES_PROGRAM_DISABLE, 0, &resp); | ||
653 | |||
654 | pr_debug("PROGRAM_DISABLE returned %d, " | ||
655 | "r0 0x%04x, r1 0x%04x, r2 0x%04x\n", | ||
656 | rc, resp.resp0, resp.resp1, resp.resp2); | ||
657 | |||
658 | if ((rc == 0) && | ||
659 | ((resp.status & HERMES_STATUS_CMDCODE) != HERMES_CMD_DOWNLD)) | ||
660 | rc = -EIO; | ||
661 | |||
662 | err = hermes_aux_control(hw, 0); | ||
663 | pr_debug("AUX disable returned %d\n", err); | ||
664 | |||
665 | /* Acknowledge any outstanding command */ | ||
666 | hermes_write_regn(hw, EVACK, 0xFFFF); | ||
667 | |||
668 | /* Reinitialise, ignoring return */ | ||
669 | (void) hw->ops->init_cmd_wait(hw, 0x0000 | HERMES_CMD_INIT, | ||
670 | 0, 0, 0, NULL); | ||
671 | |||
672 | return rc ? rc : err; | ||
673 | } | ||
674 | |||
675 | static int hermes_program_bytes(struct hermes *hw, const char *data, | ||
676 | u32 addr, u32 len) | ||
677 | { | ||
678 | /* wl lkm splits the programming into chunks of 2000 bytes. | ||
679 | * This restriction appears to come from USB. The PCMCIA | ||
680 | * adapters can program the whole lot in one go */ | ||
681 | hermes_aux_setaddr(hw, addr); | ||
682 | hermes_write_bytes(hw, HERMES_AUXDATA, data, len); | ||
683 | return 0; | ||
684 | } | ||
685 | |||
686 | /* Read PDA from the adapter */ | ||
687 | static int hermes_read_pda(hermes_t *hw, __le16 *pda, u32 pda_addr, u16 pda_len) | ||
688 | { | ||
689 | int ret; | ||
690 | u16 pda_size; | ||
691 | u16 data_len = pda_len; | ||
692 | __le16 *data = pda; | ||
693 | |||
694 | if (hw->eeprom_pda) { | ||
695 | /* PDA of spectrum symbol is in eeprom */ | ||
696 | |||
697 | /* Issue command to read EEPROM */ | ||
698 | ret = hw->ops->cmd_wait(hw, HERMES_CMD_READMIF, 0, NULL); | ||
699 | if (ret) | ||
700 | return ret; | ||
701 | } else { | ||
702 | /* wl_lkm does not include PDA size in the PDA area. | ||
703 | * We will pad the information into pda, so other routines | ||
704 | * don't have to be modified */ | ||
705 | pda[0] = cpu_to_le16(pda_len - 2); | ||
706 | /* Includes CFG_PROD_DATA but not itself */ | ||
707 | pda[1] = cpu_to_le16(0x0800); /* CFG_PROD_DATA */ | ||
708 | data_len = pda_len - 4; | ||
709 | data = pda + 2; | ||
710 | } | ||
711 | |||
712 | /* Open auxiliary port */ | ||
713 | ret = hermes_aux_control(hw, 1); | ||
714 | pr_debug("AUX enable returned %d\n", ret); | ||
715 | if (ret) | ||
716 | return ret; | ||
717 | |||
718 | /* Read PDA */ | ||
719 | hermes_aux_setaddr(hw, pda_addr); | ||
720 | hermes_read_words(hw, HERMES_AUXDATA, data, data_len / 2); | ||
721 | |||
722 | /* Close aux port */ | ||
723 | ret = hermes_aux_control(hw, 0); | ||
724 | pr_debug("AUX disable returned %d\n", ret); | ||
725 | |||
726 | /* Check PDA length */ | ||
727 | pda_size = le16_to_cpu(pda[0]); | ||
728 | pr_debug("Actual PDA length %d, Max allowed %d\n", | ||
729 | pda_size, pda_len); | ||
730 | if (pda_size > pda_len) | ||
731 | return -EINVAL; | ||
732 | |||
733 | return 0; | ||
734 | } | ||
735 | |||
532 | static void hermes_lock_irqsave(spinlock_t *lock, | 736 | static void hermes_lock_irqsave(spinlock_t *lock, |
533 | unsigned long *flags) __acquires(lock) | 737 | unsigned long *flags) __acquires(lock) |
534 | { | 738 | { |
@@ -561,6 +765,10 @@ static const struct hermes_ops hermes_ops_local = { | |||
561 | .write_ltv = hermes_write_ltv, | 765 | .write_ltv = hermes_write_ltv, |
562 | .bap_pread = hermes_bap_pread, | 766 | .bap_pread = hermes_bap_pread, |
563 | .bap_pwrite = hermes_bap_pwrite, | 767 | .bap_pwrite = hermes_bap_pwrite, |
768 | .read_pda = hermes_read_pda, | ||
769 | .program_init = hermesi_program_init, | ||
770 | .program_end = hermesi_program_end, | ||
771 | .program = hermes_program_bytes, | ||
564 | .lock_irqsave = hermes_lock_irqsave, | 772 | .lock_irqsave = hermes_lock_irqsave, |
565 | .unlock_irqrestore = hermes_unlock_irqrestore, | 773 | .unlock_irqrestore = hermes_unlock_irqrestore, |
566 | .lock_irq = hermes_lock_irq, | 774 | .lock_irq = hermes_lock_irq, |