diff options
author | John W. Linville <linville@tuxdriver.com> | 2010-10-01 11:12:36 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-10-01 11:12:36 -0400 |
commit | 41f4a6f71fe33faa7971c173c263fb431fe987fe (patch) | |
tree | fdc3e603162e3ad63f6ae4160f68eb803bb78d58 /drivers/net/wireless/ath/ath5k/pcu.c | |
parent | 94d57c4cfaa43e29ca5fa5ff874048cfc67276f5 (diff) | |
parent | 1728943d83e9fd919e454332fe344944123b3c3a (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6 into for-davem
Diffstat (limited to 'drivers/net/wireless/ath/ath5k/pcu.c')
-rw-r--r-- | drivers/net/wireless/ath/ath5k/pcu.c | 99 |
1 files changed, 98 insertions, 1 deletions
diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c index 6a891c4484a0..095d30b50ec7 100644 --- a/drivers/net/wireless/ath/ath5k/pcu.c +++ b/drivers/net/wireless/ath/ath5k/pcu.c | |||
@@ -495,6 +495,10 @@ u64 ath5k_hw_get_tsf64(struct ath5k_hw *ah) | |||
495 | { | 495 | { |
496 | u32 tsf_lower, tsf_upper1, tsf_upper2; | 496 | u32 tsf_lower, tsf_upper1, tsf_upper2; |
497 | int i; | 497 | int i; |
498 | unsigned long flags; | ||
499 | |||
500 | /* This code is time critical - we don't want to be interrupted here */ | ||
501 | local_irq_save(flags); | ||
498 | 502 | ||
499 | /* | 503 | /* |
500 | * While reading TSF upper and then lower part, the clock is still | 504 | * While reading TSF upper and then lower part, the clock is still |
@@ -517,6 +521,8 @@ u64 ath5k_hw_get_tsf64(struct ath5k_hw *ah) | |||
517 | tsf_upper1 = tsf_upper2; | 521 | tsf_upper1 = tsf_upper2; |
518 | } | 522 | } |
519 | 523 | ||
524 | local_irq_restore(flags); | ||
525 | |||
520 | WARN_ON( i == ATH5K_MAX_TSF_READ ); | 526 | WARN_ON( i == ATH5K_MAX_TSF_READ ); |
521 | 527 | ||
522 | return (((u64)tsf_upper1 << 32) | tsf_lower); | 528 | return (((u64)tsf_upper1 << 32) | tsf_lower); |
@@ -600,7 +606,7 @@ void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval) | |||
600 | /* Timer3 marks the end of our ATIM window | 606 | /* Timer3 marks the end of our ATIM window |
601 | * a zero length window is not allowed because | 607 | * a zero length window is not allowed because |
602 | * we 'll get no beacons */ | 608 | * we 'll get no beacons */ |
603 | timer3 = next_beacon + (ah->ah_atim_window ? ah->ah_atim_window : 1); | 609 | timer3 = next_beacon + 1; |
604 | 610 | ||
605 | /* | 611 | /* |
606 | * Set the beacon register and enable all timers. | 612 | * Set the beacon register and enable all timers. |
@@ -641,6 +647,97 @@ void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval) | |||
641 | } | 647 | } |
642 | 648 | ||
643 | /** | 649 | /** |
650 | * ath5k_check_timer_win - Check if timer B is timer A + window | ||
651 | * | ||
652 | * @a: timer a (before b) | ||
653 | * @b: timer b (after a) | ||
654 | * @window: difference between a and b | ||
655 | * @intval: timers are increased by this interval | ||
656 | * | ||
657 | * This helper function checks if timer B is timer A + window and covers | ||
658 | * cases where timer A or B might have already been updated or wrapped | ||
659 | * around (Timers are 16 bit). | ||
660 | * | ||
661 | * Returns true if O.K. | ||
662 | */ | ||
663 | static inline bool | ||
664 | ath5k_check_timer_win(int a, int b, int window, int intval) | ||
665 | { | ||
666 | /* | ||
667 | * 1.) usually B should be A + window | ||
668 | * 2.) A already updated, B not updated yet | ||
669 | * 3.) A already updated and has wrapped around | ||
670 | * 4.) B has wrapped around | ||
671 | */ | ||
672 | if ((b - a == window) || /* 1.) */ | ||
673 | (a - b == intval - window) || /* 2.) */ | ||
674 | ((a | 0x10000) - b == intval - window) || /* 3.) */ | ||
675 | ((b | 0x10000) - a == window)) /* 4.) */ | ||
676 | return true; /* O.K. */ | ||
677 | return false; | ||
678 | } | ||
679 | |||
680 | /** | ||
681 | * ath5k_hw_check_beacon_timers - Check if the beacon timers are correct | ||
682 | * | ||
683 | * @ah: The &struct ath5k_hw | ||
684 | * @intval: beacon interval | ||
685 | * | ||
686 | * This is a workaround for IBSS mode: | ||
687 | * | ||
688 | * The need for this function arises from the fact that we have 4 separate | ||
689 | * HW timer registers (TIMER0 - TIMER3), which are closely related to the | ||
690 | * next beacon target time (NBTT), and that the HW updates these timers | ||
691 | * seperately based on the current TSF value. The hardware increments each | ||
692 | * timer by the beacon interval, when the local TSF coverted to TU is equal | ||
693 | * to the value stored in the timer. | ||
694 | * | ||
695 | * The reception of a beacon with the same BSSID can update the local HW TSF | ||
696 | * at any time - this is something we can't avoid. If the TSF jumps to a | ||
697 | * time which is later than the time stored in a timer, this timer will not | ||
698 | * be updated until the TSF in TU wraps around at 16 bit (the size of the | ||
699 | * timers) and reaches the time which is stored in the timer. | ||
700 | * | ||
701 | * The problem is that these timers are closely related to TIMER0 (NBTT) and | ||
702 | * that they define a time "window". When the TSF jumps between two timers | ||
703 | * (e.g. ATIM and NBTT), the one in the past will be left behind (not | ||
704 | * updated), while the one in the future will be updated every beacon | ||
705 | * interval. This causes the window to get larger, until the TSF wraps | ||
706 | * around as described above and the timer which was left behind gets | ||
707 | * updated again. But - because the beacon interval is usually not an exact | ||
708 | * divisor of the size of the timers (16 bit), an unwanted "window" between | ||
709 | * these timers has developed! | ||
710 | * | ||
711 | * This is especially important with the ATIM window, because during | ||
712 | * the ATIM window only ATIM frames and no data frames are allowed to be | ||
713 | * sent, which creates transmission pauses after each beacon. This symptom | ||
714 | * has been described as "ramping ping" because ping times increase linearly | ||
715 | * for some time and then drop down again. A wrong window on the DMA beacon | ||
716 | * timer has the same effect, so we check for these two conditions. | ||
717 | * | ||
718 | * Returns true if O.K. | ||
719 | */ | ||
720 | bool | ||
721 | ath5k_hw_check_beacon_timers(struct ath5k_hw *ah, int intval) | ||
722 | { | ||
723 | unsigned int nbtt, atim, dma; | ||
724 | |||
725 | nbtt = ath5k_hw_reg_read(ah, AR5K_TIMER0); | ||
726 | atim = ath5k_hw_reg_read(ah, AR5K_TIMER3); | ||
727 | dma = ath5k_hw_reg_read(ah, AR5K_TIMER1) >> 3; | ||
728 | |||
729 | /* NOTE: SWBA is different. Having a wrong window there does not | ||
730 | * stop us from sending data and this condition is catched thru | ||
731 | * other means (SWBA interrupt) */ | ||
732 | |||
733 | if (ath5k_check_timer_win(nbtt, atim, 1, intval) && | ||
734 | ath5k_check_timer_win(dma, nbtt, AR5K_TUNE_DMA_BEACON_RESP, | ||
735 | intval)) | ||
736 | return true; /* O.K. */ | ||
737 | return false; | ||
738 | } | ||
739 | |||
740 | /** | ||
644 | * ath5k_hw_set_coverage_class - Set IEEE 802.11 coverage class | 741 | * ath5k_hw_set_coverage_class - Set IEEE 802.11 coverage class |
645 | * | 742 | * |
646 | * @ah: The &struct ath5k_hw | 743 | * @ah: The &struct ath5k_hw |