ath9k: Add infrastructure for generic hw timers
Signed-off-by: Vasanthakumar Thiagarajan <vasanth@atheros.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
81fa16fbe0
commit
ff155a45ce
4 changed files with 278 additions and 2 deletions
|
@ -30,6 +30,7 @@ enum ATH_DEBUG {
|
|||
ATH_DBG_CONFIG = 0x00000200,
|
||||
ATH_DBG_FATAL = 0x00000400,
|
||||
ATH_DBG_PS = 0x00000800,
|
||||
ATH_DBG_HWTIMER = 0x00001000,
|
||||
ATH_DBG_ANY = 0xffffffff
|
||||
};
|
||||
|
||||
|
|
|
@ -3215,6 +3215,23 @@ bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked)
|
|||
if (AR_SREV_9100(ah))
|
||||
return true;
|
||||
|
||||
if (isr & AR_ISR_GENTMR) {
|
||||
u32 s5_s;
|
||||
|
||||
s5_s = REG_READ(ah, AR_ISR_S5_S);
|
||||
if (isr & AR_ISR_GENTMR) {
|
||||
ah->intr_gen_timer_trigger =
|
||||
MS(s5_s, AR_ISR_S5_GENTIMER_TRIG);
|
||||
|
||||
ah->intr_gen_timer_thresh =
|
||||
MS(s5_s, AR_ISR_S5_GENTIMER_THRESH);
|
||||
|
||||
if (ah->intr_gen_timer_trigger)
|
||||
*masked |= ATH9K_INT_GENTIMER;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (sync_cause) {
|
||||
fatal_int =
|
||||
(sync_cause &
|
||||
|
@ -4078,3 +4095,198 @@ void ath9k_hw_set11nmac2040(struct ath_hw *ah, enum ath9k_ht_macmode mode)
|
|||
|
||||
REG_WRITE(ah, AR_2040_MODE, macmode);
|
||||
}
|
||||
|
||||
/* HW Generic timers configuration */
|
||||
|
||||
static const struct ath_gen_timer_configuration gen_tmr_configuration[] =
|
||||
{
|
||||
{AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080},
|
||||
{AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080},
|
||||
{AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080},
|
||||
{AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080},
|
||||
{AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080},
|
||||
{AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080},
|
||||
{AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080},
|
||||
{AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080},
|
||||
{AR_NEXT_NDP2_TIMER, AR_NDP2_PERIOD, AR_NDP2_TIMER_MODE, 0x0001},
|
||||
{AR_NEXT_NDP2_TIMER + 1*4, AR_NDP2_PERIOD + 1*4,
|
||||
AR_NDP2_TIMER_MODE, 0x0002},
|
||||
{AR_NEXT_NDP2_TIMER + 2*4, AR_NDP2_PERIOD + 2*4,
|
||||
AR_NDP2_TIMER_MODE, 0x0004},
|
||||
{AR_NEXT_NDP2_TIMER + 3*4, AR_NDP2_PERIOD + 3*4,
|
||||
AR_NDP2_TIMER_MODE, 0x0008},
|
||||
{AR_NEXT_NDP2_TIMER + 4*4, AR_NDP2_PERIOD + 4*4,
|
||||
AR_NDP2_TIMER_MODE, 0x0010},
|
||||
{AR_NEXT_NDP2_TIMER + 5*4, AR_NDP2_PERIOD + 5*4,
|
||||
AR_NDP2_TIMER_MODE, 0x0020},
|
||||
{AR_NEXT_NDP2_TIMER + 6*4, AR_NDP2_PERIOD + 6*4,
|
||||
AR_NDP2_TIMER_MODE, 0x0040},
|
||||
{AR_NEXT_NDP2_TIMER + 7*4, AR_NDP2_PERIOD + 7*4,
|
||||
AR_NDP2_TIMER_MODE, 0x0080}
|
||||
};
|
||||
|
||||
/* 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];
|
||||
}
|
||||
|
||||
static u32 ath9k_hw_gettsf32(struct ath_hw *ah)
|
||||
{
|
||||
return REG_READ(ah, AR_TSF_L32);
|
||||
}
|
||||
|
||||
struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah,
|
||||
void (*trigger)(void *),
|
||||
void (*overflow)(void *),
|
||||
void *arg,
|
||||
u8 timer_index)
|
||||
{
|
||||
struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
|
||||
struct ath_gen_timer *timer;
|
||||
|
||||
timer = kzalloc(sizeof(struct ath_gen_timer), GFP_KERNEL);
|
||||
|
||||
if (timer == NULL) {
|
||||
printk(KERN_DEBUG "Failed to allocate memory"
|
||||
"for hw timer[%d]\n", timer_index);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* allocate a hardware generic timer slot */
|
||||
timer_table->timers[timer_index] = timer;
|
||||
timer->index = timer_index;
|
||||
timer->trigger = trigger;
|
||||
timer->overflow = overflow;
|
||||
timer->arg = arg;
|
||||
|
||||
return timer;
|
||||
}
|
||||
|
||||
void ath_gen_timer_start(struct ath_hw *ah,
|
||||
struct ath_gen_timer *timer,
|
||||
u32 timer_next, u32 timer_period)
|
||||
{
|
||||
struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
|
||||
u32 tsf;
|
||||
|
||||
BUG_ON(!timer_period);
|
||||
|
||||
set_bit(timer->index, &timer_table->timer_mask.timer_bits);
|
||||
|
||||
tsf = ath9k_hw_gettsf32(ah);
|
||||
|
||||
DPRINTF(ah->ah_sc, ATH_DBG_HWTIMER, "curent tsf %x period %x"
|
||||
"timer_next %x\n", tsf, timer_period, timer_next);
|
||||
|
||||
/*
|
||||
* Pull timer_next forward if the current TSF already passed it
|
||||
* because of software latency
|
||||
*/
|
||||
if (timer_next < tsf)
|
||||
timer_next = tsf + timer_period;
|
||||
|
||||
/*
|
||||
* Program generic timer registers
|
||||
*/
|
||||
REG_WRITE(ah, gen_tmr_configuration[timer->index].next_addr,
|
||||
timer_next);
|
||||
REG_WRITE(ah, gen_tmr_configuration[timer->index].period_addr,
|
||||
timer_period);
|
||||
REG_SET_BIT(ah, gen_tmr_configuration[timer->index].mode_addr,
|
||||
gen_tmr_configuration[timer->index].mode_mask);
|
||||
|
||||
/* Enable both trigger and thresh interrupt masks */
|
||||
REG_SET_BIT(ah, AR_IMR_S5,
|
||||
(SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_THRESH) |
|
||||
SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_TRIG)));
|
||||
|
||||
if ((ah->ah_sc->imask & ATH9K_INT_GENTIMER) == 0) {
|
||||
ath9k_hw_set_interrupts(ah, 0);
|
||||
ah->ah_sc->imask |= ATH9K_INT_GENTIMER;
|
||||
ath9k_hw_set_interrupts(ah, ah->ah_sc->imask);
|
||||
}
|
||||
}
|
||||
|
||||
void ath_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer)
|
||||
{
|
||||
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. */
|
||||
REG_CLR_BIT(ah, gen_tmr_configuration[timer->index].mode_addr,
|
||||
gen_tmr_configuration[timer->index].mode_mask);
|
||||
|
||||
/* Disable both trigger and thresh interrupt masks */
|
||||
REG_CLR_BIT(ah, AR_IMR_S5,
|
||||
(SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_THRESH) |
|
||||
SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_TRIG)));
|
||||
|
||||
clear_bit(timer->index, &timer_table->timer_mask.timer_bits);
|
||||
|
||||
/* if no timer is enabled, turn off interrupt mask */
|
||||
if (timer_table->timer_mask.val == 0) {
|
||||
ath9k_hw_set_interrupts(ah, 0);
|
||||
ah->ah_sc->imask &= ~ATH9K_INT_GENTIMER;
|
||||
ath9k_hw_set_interrupts(ah, ah->ah_sc->imask);
|
||||
}
|
||||
}
|
||||
|
||||
void ath_gen_timer_free(struct ath_hw *ah, struct ath_gen_timer *timer)
|
||||
{
|
||||
struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
|
||||
|
||||
/* free the hardware generic timer slot */
|
||||
timer_table->timers[timer->index] = NULL;
|
||||
kfree(timer);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic Timer Interrupts handling
|
||||
*/
|
||||
void ath_gen_timer_isr(struct ath_hw *ah)
|
||||
{
|
||||
struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers;
|
||||
struct ath_gen_timer *timer;
|
||||
u32 trigger_mask, thresh_mask, index;
|
||||
|
||||
/* get hardware generic timer interrupt status */
|
||||
trigger_mask = ah->intr_gen_timer_trigger;
|
||||
thresh_mask = ah->intr_gen_timer_thresh;
|
||||
trigger_mask &= timer_table->timer_mask.val;
|
||||
thresh_mask &= timer_table->timer_mask.val;
|
||||
|
||||
trigger_mask &= ~thresh_mask;
|
||||
|
||||
while (thresh_mask) {
|
||||
index = rightmost_index(timer_table, &thresh_mask);
|
||||
timer = timer_table->timers[index];
|
||||
BUG_ON(!timer);
|
||||
DPRINTF(ah->ah_sc, ATH_DBG_HWTIMER,
|
||||
"TSF overflow for Gen timer %d\n", index);
|
||||
timer->overflow(timer->arg);
|
||||
}
|
||||
|
||||
while (trigger_mask) {
|
||||
index = rightmost_index(timer_table, &trigger_mask);
|
||||
timer = timer_table->timers[index];
|
||||
BUG_ON(!timer);
|
||||
DPRINTF(ah->ah_sc, ATH_DBG_HWTIMER,
|
||||
"Gen timer[%d] trigger\n", index);
|
||||
timer->trigger(timer->arg);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -237,6 +237,7 @@ enum ath9k_int {
|
|||
ATH9K_INT_GPIO = 0x01000000,
|
||||
ATH9K_INT_CABEND = 0x02000000,
|
||||
ATH9K_INT_TSFOOR = 0x04000000,
|
||||
ATH9K_INT_GENTIMER = 0x08000000,
|
||||
ATH9K_INT_CST = 0x10000000,
|
||||
ATH9K_INT_GTT = 0x20000000,
|
||||
ATH9K_INT_FATAL = 0x40000000,
|
||||
|
@ -390,6 +391,41 @@ struct ath9k_hw_version {
|
|||
u16 analog2GhzRev;
|
||||
};
|
||||
|
||||
/* Generic TSF timer definitions */
|
||||
|
||||
#define ATH_MAX_GEN_TIMER 16
|
||||
|
||||
#define AR_GENTMR_BIT(_index) (1 << (_index))
|
||||
|
||||
/*
|
||||
* Using de Bruijin sequence to to look up 1's index in a 32 bit number
|
||||
* debruijn32 = 0000 0111 0111 1100 1011 0101 0011 0001
|
||||
*/
|
||||
#define debruijn32 0x077CB531UL
|
||||
|
||||
struct ath_gen_timer_configuration {
|
||||
u32 next_addr;
|
||||
u32 period_addr;
|
||||
u32 mode_addr;
|
||||
u32 mode_mask;
|
||||
};
|
||||
|
||||
struct ath_gen_timer {
|
||||
void (*trigger)(void *arg);
|
||||
void (*overflow)(void *arg);
|
||||
void *arg;
|
||||
u8 index;
|
||||
};
|
||||
|
||||
struct ath_gen_timer_table {
|
||||
u32 gen_timer_index[32];
|
||||
struct ath_gen_timer *timers[ATH_MAX_GEN_TIMER];
|
||||
union {
|
||||
unsigned long timer_bits;
|
||||
u16 val;
|
||||
} timer_mask;
|
||||
};
|
||||
|
||||
struct ath_hw {
|
||||
struct ath_softc *ah_sc;
|
||||
struct ath9k_hw_version hw_version;
|
||||
|
@ -536,6 +572,10 @@ struct ath_hw {
|
|||
struct ar5416IniArray iniModesAdditional;
|
||||
struct ar5416IniArray iniModesRxGain;
|
||||
struct ar5416IniArray iniModesTxGain;
|
||||
|
||||
u32 intr_gen_timer_trigger;
|
||||
u32 intr_gen_timer_thresh;
|
||||
struct ath_gen_timer_table hw_gen_timers;
|
||||
};
|
||||
|
||||
/* Initialization, Detach, Reset */
|
||||
|
@ -611,4 +651,16 @@ bool ath9k_hw_intrpend(struct ath_hw *ah);
|
|||
bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked);
|
||||
enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints);
|
||||
|
||||
/* Generic hw timer primitives */
|
||||
struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah,
|
||||
void (*trigger)(void *),
|
||||
void (*overflow)(void *),
|
||||
void *arg,
|
||||
u8 timer_index);
|
||||
void ath_gen_timer_start(struct ath_hw *ah, struct ath_gen_timer *timer,
|
||||
u32 timer_next, u32 timer_period);
|
||||
void ath_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer);
|
||||
void ath_gen_timer_free(struct ath_hw *ah, struct ath_gen_timer *timer);
|
||||
void ath_gen_timer_isr(struct ath_hw *hw);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -234,7 +234,15 @@
|
|||
#define AR_IMR_S5 0x00b8
|
||||
#define AR_IMR_S5_TIM_TIMER 0x00000010
|
||||
#define AR_IMR_S5_DTIM_TIMER 0x00000020
|
||||
|
||||
#define AR_ISR_S5_GENTIMER_TRIG 0x0000FF80
|
||||
#define AR_ISR_S5_GENTIMER_TRIG_S 0
|
||||
#define AR_ISR_S5_GENTIMER_THRESH 0xFF800000
|
||||
#define AR_ISR_S5_GENTIMER_THRESH_S 16
|
||||
#define AR_ISR_S5_S 0x00d8
|
||||
#define AR_IMR_S5_GENTIMER_TRIG 0x0000FF80
|
||||
#define AR_IMR_S5_GENTIMER_TRIG_S 0
|
||||
#define AR_IMR_S5_GENTIMER_THRESH 0xFF800000
|
||||
#define AR_IMR_S5_GENTIMER_THRESH_S 16
|
||||
|
||||
#define AR_IMR 0x00a0
|
||||
#define AR_IMR_RXOK 0x00000001
|
||||
|
@ -1516,7 +1524,10 @@ enum {
|
|||
#define AR_TXOP_8_11 0x81f8
|
||||
#define AR_TXOP_12_15 0x81fc
|
||||
|
||||
|
||||
#define AR_NEXT_NDP2_TIMER 0x8180
|
||||
#define AR_FIRST_NDP_TIMER 7
|
||||
#define AR_NDP2_PERIOD 0x81a0
|
||||
#define AR_NDP2_TIMER_MODE 0x81c0
|
||||
#define AR_NEXT_TBTT_TIMER 0x8200
|
||||
#define AR_NEXT_DMA_BEACON_ALERT 0x8204
|
||||
#define AR_NEXT_SWBA 0x8208
|
||||
|
|
Loading…
Reference in a new issue