Commit c67ce339 authored by Felix Fietkau's avatar Felix Fietkau Committed by John W. Linville

ath9k_hw: clean up generic timer code

- Use generic bitops instead of custom hackery
- Move interrupt enable/disable logic from ath9k to ath9k_hw
- Decouple ISR call from btcoex
- Make the overflow callback optional (to prevent IRQ storms)
Signed-off-by: default avatarFelix Fietkau <nbd@openwrt.org>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 168c6f89
...@@ -66,7 +66,6 @@ void ath9k_hw_init_btcoex_hw(struct ath_hw *ah, int qnum) ...@@ -66,7 +66,6 @@ void ath9k_hw_init_btcoex_hw(struct ath_hw *ah, int qnum)
.bt_first_slot_time = 5, .bt_first_slot_time = 5,
.bt_hold_rx_clear = true, .bt_hold_rx_clear = true,
}; };
u32 i, idx;
bool rxclear_polarity = ath_bt_config.bt_rxclear_polarity; bool rxclear_polarity = ath_bt_config.bt_rxclear_polarity;
if (AR_SREV_9300_20_OR_LATER(ah)) if (AR_SREV_9300_20_OR_LATER(ah))
...@@ -88,11 +87,6 @@ void ath9k_hw_init_btcoex_hw(struct ath_hw *ah, int qnum) ...@@ -88,11 +87,6 @@ void ath9k_hw_init_btcoex_hw(struct ath_hw *ah, int qnum)
SM(ath_bt_config.bt_hold_rx_clear, AR_BT_HOLD_RX_CLEAR) | SM(ath_bt_config.bt_hold_rx_clear, AR_BT_HOLD_RX_CLEAR) |
SM(ATH_BTCOEX_BMISS_THRESH, AR_BT_BCN_MISS_THRESH) | SM(ATH_BTCOEX_BMISS_THRESH, AR_BT_BCN_MISS_THRESH) |
AR_BT_DISABLE_BT_ANT; AR_BT_DISABLE_BT_ANT;
for (i = 0; i < 32; i++) {
idx = (debruijn32 << i) >> 27;
ah->hw_gen_timers.gen_timer_index[idx] = i;
}
} }
EXPORT_SYMBOL(ath9k_hw_init_btcoex_hw); EXPORT_SYMBOL(ath9k_hw_init_btcoex_hw);
......
...@@ -157,36 +157,6 @@ static void ath_detect_bt_priority(struct ath_softc *sc) ...@@ -157,36 +157,6 @@ static void ath_detect_bt_priority(struct ath_softc *sc)
} }
} }
static void ath9k_gen_timer_start(struct ath_hw *ah,
struct ath_gen_timer *timer,
u32 trig_timeout,
u32 timer_period)
{
ath9k_hw_gen_timer_start(ah, timer, trig_timeout, timer_period);
if ((ah->imask & ATH9K_INT_GENTIMER) == 0) {
ath9k_hw_disable_interrupts(ah);
ah->imask |= ATH9K_INT_GENTIMER;
ath9k_hw_set_interrupts(ah);
ath9k_hw_enable_interrupts(ah);
}
}
static void ath9k_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer)
{
struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
ath9k_hw_gen_timer_stop(ah, timer);
/* if no timer is enabled, turn off interrupt mask */
if (timer_table->timer_mask.val == 0) {
ath9k_hw_disable_interrupts(ah);
ah->imask &= ~ATH9K_INT_GENTIMER;
ath9k_hw_set_interrupts(ah);
ath9k_hw_enable_interrupts(ah);
}
}
static void ath_mci_ftp_adjust(struct ath_softc *sc) static void ath_mci_ftp_adjust(struct ath_softc *sc)
{ {
struct ath_btcoex *btcoex = &sc->btcoex; struct ath_btcoex *btcoex = &sc->btcoex;
...@@ -373,12 +343,6 @@ u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen) ...@@ -373,12 +343,6 @@ u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen)
void ath9k_btcoex_handle_interrupt(struct ath_softc *sc, u32 status) void ath9k_btcoex_handle_interrupt(struct ath_softc *sc, u32 status)
{ {
struct ath_hw *ah = sc->sc_ah;
if (ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_3WIRE)
if (status & ATH9K_INT_GENTIMER)
ath_gen_timer_isr(sc->sc_ah);
if (status & ATH9K_INT_MCI) if (status & ATH9K_INT_MCI)
ath_mci_intr(sc); ath_mci_intr(sc);
} }
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/time.h> #include <linux/time.h>
#include <linux/bitops.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
#include "hw.h" #include "hw.h"
...@@ -2991,20 +2992,6 @@ static const struct ath_gen_timer_configuration gen_tmr_configuration[] = ...@@ -2991,20 +2992,6 @@ static const struct ath_gen_timer_configuration gen_tmr_configuration[] =
/* HW generic timer primitives */ /* HW generic timer primitives */
/* compute and clear index of rightmost 1 */
static u32 rightmost_index(struct ath_gen_timer_table *timer_table, u32 *mask)
{
u32 b;
b = *mask;
b &= (0-b);
*mask &= ~b;
b *= debruijn32;
b >>= 27;
return timer_table->gen_timer_index[b];
}
u32 ath9k_hw_gettsf32(struct ath_hw *ah) u32 ath9k_hw_gettsf32(struct ath_hw *ah)
{ {
return REG_READ(ah, AR_TSF_L32); return REG_READ(ah, AR_TSF_L32);
...@@ -3020,6 +3007,10 @@ struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah, ...@@ -3020,6 +3007,10 @@ struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah,
struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
struct ath_gen_timer *timer; struct ath_gen_timer *timer;
if ((timer_index < AR_FIRST_NDP_TIMER) ||
(timer_index >= ATH_MAX_GEN_TIMER))
return NULL;
timer = kzalloc(sizeof(struct ath_gen_timer), GFP_KERNEL); timer = kzalloc(sizeof(struct ath_gen_timer), GFP_KERNEL);
if (timer == NULL) if (timer == NULL)
return NULL; return NULL;
...@@ -3037,23 +3028,13 @@ EXPORT_SYMBOL(ath_gen_timer_alloc); ...@@ -3037,23 +3028,13 @@ EXPORT_SYMBOL(ath_gen_timer_alloc);
void ath9k_hw_gen_timer_start(struct ath_hw *ah, void ath9k_hw_gen_timer_start(struct ath_hw *ah,
struct ath_gen_timer *timer, struct ath_gen_timer *timer,
u32 trig_timeout, u32 timer_next,
u32 timer_period) u32 timer_period)
{ {
struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
u32 tsf, timer_next; u32 mask = 0;
BUG_ON(!timer_period); timer_table->timer_mask |= BIT(timer->index);
set_bit(timer->index, &timer_table->timer_mask.timer_bits);
tsf = ath9k_hw_gettsf32(ah);
timer_next = tsf + trig_timeout;
ath_dbg(ath9k_hw_common(ah), BTCOEX,
"current tsf %x period %x timer_next %x\n",
tsf, timer_period, timer_next);
/* /*
* Program generic timer registers * Program generic timer registers
...@@ -3079,10 +3060,19 @@ void ath9k_hw_gen_timer_start(struct ath_hw *ah, ...@@ -3079,10 +3060,19 @@ void ath9k_hw_gen_timer_start(struct ath_hw *ah,
(1 << timer->index)); (1 << timer->index));
} }
/* Enable both trigger and thresh interrupt masks */ if (timer->trigger)
REG_SET_BIT(ah, AR_IMR_S5, mask |= SM(AR_GENTMR_BIT(timer->index),
(SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_THRESH) | AR_IMR_S5_GENTIMER_TRIG);
SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_TRIG))); if (timer->overflow)
mask |= SM(AR_GENTMR_BIT(timer->index),
AR_IMR_S5_GENTIMER_THRESH);
REG_SET_BIT(ah, AR_IMR_S5, mask);
if ((ah->imask & ATH9K_INT_GENTIMER) == 0) {
ah->imask |= ATH9K_INT_GENTIMER;
ath9k_hw_set_interrupts(ah);
}
} }
EXPORT_SYMBOL(ath9k_hw_gen_timer_start); EXPORT_SYMBOL(ath9k_hw_gen_timer_start);
...@@ -3090,11 +3080,6 @@ void ath9k_hw_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer) ...@@ -3090,11 +3080,6 @@ void ath9k_hw_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer)
{ {
struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
if ((timer->index < AR_FIRST_NDP_TIMER) ||
(timer->index >= ATH_MAX_GEN_TIMER)) {
return;
}
/* Clear generic timer enable bits. */ /* Clear generic timer enable bits. */
REG_CLR_BIT(ah, gen_tmr_configuration[timer->index].mode_addr, REG_CLR_BIT(ah, gen_tmr_configuration[timer->index].mode_addr,
gen_tmr_configuration[timer->index].mode_mask); gen_tmr_configuration[timer->index].mode_mask);
...@@ -3114,7 +3099,12 @@ void ath9k_hw_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer) ...@@ -3114,7 +3099,12 @@ void ath9k_hw_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer)
(SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_THRESH) | (SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_THRESH) |
SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_TRIG))); SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_TRIG)));
clear_bit(timer->index, &timer_table->timer_mask.timer_bits); timer_table->timer_mask &= ~BIT(timer->index);
if (timer_table->timer_mask == 0) {
ah->imask &= ~ATH9K_INT_GENTIMER;
ath9k_hw_set_interrupts(ah);
}
} }
EXPORT_SYMBOL(ath9k_hw_gen_timer_stop); EXPORT_SYMBOL(ath9k_hw_gen_timer_stop);
...@@ -3135,32 +3125,32 @@ void ath_gen_timer_isr(struct ath_hw *ah) ...@@ -3135,32 +3125,32 @@ void ath_gen_timer_isr(struct ath_hw *ah)
{ {
struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
struct ath_gen_timer *timer; struct ath_gen_timer *timer;
struct ath_common *common = ath9k_hw_common(ah); unsigned long trigger_mask, thresh_mask;
u32 trigger_mask, thresh_mask, index; unsigned int index;
/* get hardware generic timer interrupt status */ /* get hardware generic timer interrupt status */
trigger_mask = ah->intr_gen_timer_trigger; trigger_mask = ah->intr_gen_timer_trigger;
thresh_mask = ah->intr_gen_timer_thresh; thresh_mask = ah->intr_gen_timer_thresh;
trigger_mask &= timer_table->timer_mask.val; trigger_mask &= timer_table->timer_mask;
thresh_mask &= timer_table->timer_mask.val; thresh_mask &= timer_table->timer_mask;
trigger_mask &= ~thresh_mask; trigger_mask &= ~thresh_mask;
while (thresh_mask) { for_each_set_bit(index, &thresh_mask, ARRAY_SIZE(timer_table->timers)) {
index = rightmost_index(timer_table, &thresh_mask);
timer = timer_table->timers[index]; timer = timer_table->timers[index];
BUG_ON(!timer); if (!timer)
ath_dbg(common, BTCOEX, "TSF overflow for Gen timer %d\n", continue;
index); if (!timer->overflow)
continue;
timer->overflow(timer->arg); timer->overflow(timer->arg);
} }
while (trigger_mask) { for_each_set_bit(index, &trigger_mask, ARRAY_SIZE(timer_table->timers)) {
index = rightmost_index(timer_table, &trigger_mask);
timer = timer_table->timers[index]; timer = timer_table->timers[index];
BUG_ON(!timer); if (!timer)
ath_dbg(common, BTCOEX, continue;
"Gen timer[%d] trigger\n", index); if (!timer->trigger)
continue;
timer->trigger(timer->arg); timer->trigger(timer->arg);
} }
} }
......
...@@ -499,12 +499,6 @@ struct ath9k_hw_version { ...@@ -499,12 +499,6 @@ struct ath9k_hw_version {
#define AR_GENTMR_BIT(_index) (1 << (_index)) #define AR_GENTMR_BIT(_index) (1 << (_index))
/*
* Using de Bruijin sequence to look up 1's index in a 32 bit number
* debruijn32 = 0000 0111 0111 1100 1011 0101 0011 0001
*/
#define debruijn32 0x077CB531U
struct ath_gen_timer_configuration { struct ath_gen_timer_configuration {
u32 next_addr; u32 next_addr;
u32 period_addr; u32 period_addr;
...@@ -520,12 +514,8 @@ struct ath_gen_timer { ...@@ -520,12 +514,8 @@ struct ath_gen_timer {
}; };
struct ath_gen_timer_table { struct ath_gen_timer_table {
u32 gen_timer_index[32];
struct ath_gen_timer *timers[ATH_MAX_GEN_TIMER]; struct ath_gen_timer *timers[ATH_MAX_GEN_TIMER];
union { u16 timer_mask;
unsigned long timer_bits;
u16 val;
} timer_mask;
}; };
struct ath_hw_antcomb_conf { struct ath_hw_antcomb_conf {
......
...@@ -508,6 +508,9 @@ void ath9k_tasklet(unsigned long data) ...@@ -508,6 +508,9 @@ void ath9k_tasklet(unsigned long data)
wake_up(&sc->tx_wait); wake_up(&sc->tx_wait);
} }
if (status & ATH9K_INT_GENTIMER)
ath_gen_timer_isr(sc->sc_ah);
ath9k_btcoex_handle_interrupt(sc, status); ath9k_btcoex_handle_interrupt(sc, status);
/* re-enable hardware interrupt */ /* re-enable hardware interrupt */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment