diff options
Diffstat (limited to 'drivers/net/wireless/orinoco/hermes.c')
-rw-r--r-- | drivers/net/wireless/orinoco/hermes.c | 286 |
1 files changed, 263 insertions, 23 deletions
diff --git a/drivers/net/wireless/orinoco/hermes.c b/drivers/net/wireless/orinoco/hermes.c index 1a2fca76fd3c..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 | ||
@@ -70,6 +90,7 @@ | |||
70 | 90 | ||
71 | #endif /* ! HERMES_DEBUG */ | 91 | #endif /* ! HERMES_DEBUG */ |
72 | 92 | ||
93 | static const struct hermes_ops hermes_ops_local; | ||
73 | 94 | ||
74 | /* | 95 | /* |
75 | * Internal functions | 96 | * Internal functions |
@@ -111,9 +132,9 @@ static int hermes_issue_cmd(hermes_t *hw, u16 cmd, u16 param0, | |||
111 | */ | 132 | */ |
112 | 133 | ||
113 | /* For doing cmds that wipe the magic constant in SWSUPPORT0 */ | 134 | /* For doing cmds that wipe the magic constant in SWSUPPORT0 */ |
114 | int hermes_doicmd_wait(hermes_t *hw, u16 cmd, | 135 | static int hermes_doicmd_wait(hermes_t *hw, u16 cmd, |
115 | u16 parm0, u16 parm1, u16 parm2, | 136 | u16 parm0, u16 parm1, u16 parm2, |
116 | struct hermes_response *resp) | 137 | struct hermes_response *resp) |
117 | { | 138 | { |
118 | int err = 0; | 139 | int err = 0; |
119 | int k; | 140 | int k; |
@@ -163,17 +184,18 @@ int hermes_doicmd_wait(hermes_t *hw, u16 cmd, | |||
163 | out: | 184 | out: |
164 | return err; | 185 | return err; |
165 | } | 186 | } |
166 | EXPORT_SYMBOL(hermes_doicmd_wait); | ||
167 | 187 | ||
168 | void hermes_struct_init(hermes_t *hw, void __iomem *address, int reg_spacing) | 188 | void hermes_struct_init(hermes_t *hw, void __iomem *address, int reg_spacing) |
169 | { | 189 | { |
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; | ||
194 | hw->ops = &hermes_ops_local; | ||
173 | } | 195 | } |
174 | EXPORT_SYMBOL(hermes_struct_init); | 196 | EXPORT_SYMBOL(hermes_struct_init); |
175 | 197 | ||
176 | int hermes_init(hermes_t *hw) | 198 | static int hermes_init(hermes_t *hw) |
177 | { | 199 | { |
178 | u16 reg; | 200 | u16 reg; |
179 | int err = 0; | 201 | int err = 0; |
@@ -217,7 +239,6 @@ int hermes_init(hermes_t *hw) | |||
217 | 239 | ||
218 | return err; | 240 | return err; |
219 | } | 241 | } |
220 | EXPORT_SYMBOL(hermes_init); | ||
221 | 242 | ||
222 | /* Issue a command to the chip, and (busy!) wait for it to | 243 | /* Issue a command to the chip, and (busy!) wait for it to |
223 | * complete. | 244 | * complete. |
@@ -228,8 +249,8 @@ EXPORT_SYMBOL(hermes_init); | |||
228 | * > 0 on error returned by the firmware | 249 | * > 0 on error returned by the firmware |
229 | * | 250 | * |
230 | * Callable from any context, but locking is your problem. */ | 251 | * Callable from any context, but locking is your problem. */ |
231 | int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, | 252 | static int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, |
232 | struct hermes_response *resp) | 253 | struct hermes_response *resp) |
233 | { | 254 | { |
234 | int err; | 255 | int err; |
235 | int k; | 256 | int k; |
@@ -291,9 +312,8 @@ int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, | |||
291 | out: | 312 | out: |
292 | return err; | 313 | return err; |
293 | } | 314 | } |
294 | EXPORT_SYMBOL(hermes_docmd_wait); | ||
295 | 315 | ||
296 | int hermes_allocate(hermes_t *hw, u16 size, u16 *fid) | 316 | static int hermes_allocate(hermes_t *hw, u16 size, u16 *fid) |
297 | { | 317 | { |
298 | int err = 0; | 318 | int err = 0; |
299 | int k; | 319 | int k; |
@@ -333,7 +353,6 @@ int hermes_allocate(hermes_t *hw, u16 size, u16 *fid) | |||
333 | 353 | ||
334 | return 0; | 354 | return 0; |
335 | } | 355 | } |
336 | EXPORT_SYMBOL(hermes_allocate); | ||
337 | 356 | ||
338 | /* Set up a BAP to read a particular chunk of data from card's internal buffer. | 357 | /* Set up a BAP to read a particular chunk of data from card's internal buffer. |
339 | * | 358 | * |
@@ -403,8 +422,8 @@ static int hermes_bap_seek(hermes_t *hw, int bap, u16 id, u16 offset) | |||
403 | * 0 on success | 422 | * 0 on success |
404 | * > 0 on error from firmware | 423 | * > 0 on error from firmware |
405 | */ | 424 | */ |
406 | int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len, | 425 | static int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len, |
407 | u16 id, u16 offset) | 426 | u16 id, u16 offset) |
408 | { | 427 | { |
409 | int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; | 428 | int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; |
410 | int err = 0; | 429 | int err = 0; |
@@ -422,7 +441,6 @@ int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len, | |||
422 | out: | 441 | out: |
423 | return err; | 442 | return err; |
424 | } | 443 | } |
425 | EXPORT_SYMBOL(hermes_bap_pread); | ||
426 | 444 | ||
427 | /* Write a block of data to the chip's buffer, via the | 445 | /* Write a block of data to the chip's buffer, via the |
428 | * BAP. Synchronization/serialization is the caller's problem. | 446 | * BAP. Synchronization/serialization is the caller's problem. |
@@ -432,8 +450,8 @@ EXPORT_SYMBOL(hermes_bap_pread); | |||
432 | * 0 on success | 450 | * 0 on success |
433 | * > 0 on error from firmware | 451 | * > 0 on error from firmware |
434 | */ | 452 | */ |
435 | int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, int len, | 453 | static int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, int len, |
436 | u16 id, u16 offset) | 454 | u16 id, u16 offset) |
437 | { | 455 | { |
438 | int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; | 456 | int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; |
439 | int err = 0; | 457 | int err = 0; |
@@ -451,7 +469,6 @@ int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, int len, | |||
451 | out: | 469 | out: |
452 | return err; | 470 | return err; |
453 | } | 471 | } |
454 | EXPORT_SYMBOL(hermes_bap_pwrite); | ||
455 | 472 | ||
456 | /* Read a Length-Type-Value record from the card. | 473 | /* Read a Length-Type-Value record from the card. |
457 | * | 474 | * |
@@ -461,8 +478,8 @@ EXPORT_SYMBOL(hermes_bap_pwrite); | |||
461 | * practice. | 478 | * practice. |
462 | * | 479 | * |
463 | * Callable from user or bh context. */ | 480 | * Callable from user or bh context. */ |
464 | int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, unsigned bufsize, | 481 | static int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, unsigned bufsize, |
465 | u16 *length, void *buf) | 482 | u16 *length, void *buf) |
466 | { | 483 | { |
467 | int err = 0; | 484 | int err = 0; |
468 | int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; | 485 | int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; |
@@ -505,10 +522,9 @@ int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, unsigned bufsize, | |||
505 | 522 | ||
506 | return 0; | 523 | return 0; |
507 | } | 524 | } |
508 | EXPORT_SYMBOL(hermes_read_ltv); | ||
509 | 525 | ||
510 | int hermes_write_ltv(hermes_t *hw, int bap, u16 rid, | 526 | static int hermes_write_ltv(hermes_t *hw, int bap, u16 rid, |
511 | u16 length, const void *value) | 527 | u16 length, const void *value) |
512 | { | 528 | { |
513 | int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; | 529 | int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; |
514 | int err = 0; | 530 | int err = 0; |
@@ -533,4 +549,228 @@ int hermes_write_ltv(hermes_t *hw, int bap, u16 rid, | |||
533 | 549 | ||
534 | return err; | 550 | return err; |
535 | } | 551 | } |
536 | EXPORT_SYMBOL(hermes_write_ltv); | 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 | |||
736 | static void hermes_lock_irqsave(spinlock_t *lock, | ||
737 | unsigned long *flags) __acquires(lock) | ||
738 | { | ||
739 | spin_lock_irqsave(lock, *flags); | ||
740 | } | ||
741 | |||
742 | static void hermes_unlock_irqrestore(spinlock_t *lock, | ||
743 | unsigned long *flags) __releases(lock) | ||
744 | { | ||
745 | spin_unlock_irqrestore(lock, *flags); | ||
746 | } | ||
747 | |||
748 | static void hermes_lock_irq(spinlock_t *lock) __acquires(lock) | ||
749 | { | ||
750 | spin_lock_irq(lock); | ||
751 | } | ||
752 | |||
753 | static void hermes_unlock_irq(spinlock_t *lock) __releases(lock) | ||
754 | { | ||
755 | spin_unlock_irq(lock); | ||
756 | } | ||
757 | |||
758 | /* Hermes operations for local buses */ | ||
759 | static const struct hermes_ops hermes_ops_local = { | ||
760 | .init = hermes_init, | ||
761 | .cmd_wait = hermes_docmd_wait, | ||
762 | .init_cmd_wait = hermes_doicmd_wait, | ||
763 | .allocate = hermes_allocate, | ||
764 | .read_ltv = hermes_read_ltv, | ||
765 | .write_ltv = hermes_write_ltv, | ||
766 | .bap_pread = hermes_bap_pread, | ||
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, | ||
772 | .lock_irqsave = hermes_lock_irqsave, | ||
773 | .unlock_irqrestore = hermes_unlock_irqrestore, | ||
774 | .lock_irq = hermes_lock_irq, | ||
775 | .unlock_irq = hermes_unlock_irq, | ||
776 | }; | ||