diff options
author | Vasanthakumar Thiagarajan <vasanth@atheros.com> | 2009-08-26 11:38:49 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-08-28 14:40:51 -0400 |
commit | ff155a45cea56ad7a90c3f5192db59a4c7812fde (patch) | |
tree | 3733ebad8263ba4c92e9ad429b901e56958d8c76 /drivers/net/wireless/ath | |
parent | 81fa16fbe06cb3a4d29cc5a6f925132554521c72 (diff) |
ath9k: Add infrastructure for generic hw timers
Signed-off-by: Vasanthakumar Thiagarajan <vasanth@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/ath')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/debug.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/hw.c | 212 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/hw.h | 52 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/reg.h | 15 |
4 files changed, 278 insertions, 2 deletions
diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index 5e56b79d0cb0..ea0dd1ec15c3 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h | |||
@@ -30,6 +30,7 @@ enum ATH_DEBUG { | |||
30 | ATH_DBG_CONFIG = 0x00000200, | 30 | ATH_DBG_CONFIG = 0x00000200, |
31 | ATH_DBG_FATAL = 0x00000400, | 31 | ATH_DBG_FATAL = 0x00000400, |
32 | ATH_DBG_PS = 0x00000800, | 32 | ATH_DBG_PS = 0x00000800, |
33 | ATH_DBG_HWTIMER = 0x00001000, | ||
33 | ATH_DBG_ANY = 0xffffffff | 34 | ATH_DBG_ANY = 0xffffffff |
34 | }; | 35 | }; |
35 | 36 | ||
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index c80be8c78e8b..3afd7a9fc8a3 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c | |||
@@ -3215,6 +3215,23 @@ bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked) | |||
3215 | if (AR_SREV_9100(ah)) | 3215 | if (AR_SREV_9100(ah)) |
3216 | return true; | 3216 | return true; |
3217 | 3217 | ||
3218 | if (isr & AR_ISR_GENTMR) { | ||
3219 | u32 s5_s; | ||
3220 | |||
3221 | s5_s = REG_READ(ah, AR_ISR_S5_S); | ||
3222 | if (isr & AR_ISR_GENTMR) { | ||
3223 | ah->intr_gen_timer_trigger = | ||
3224 | MS(s5_s, AR_ISR_S5_GENTIMER_TRIG); | ||
3225 | |||
3226 | ah->intr_gen_timer_thresh = | ||
3227 | MS(s5_s, AR_ISR_S5_GENTIMER_THRESH); | ||
3228 | |||
3229 | if (ah->intr_gen_timer_trigger) | ||
3230 | *masked |= ATH9K_INT_GENTIMER; | ||
3231 | |||
3232 | } | ||
3233 | } | ||
3234 | |||
3218 | if (sync_cause) { | 3235 | if (sync_cause) { |
3219 | fatal_int = | 3236 | fatal_int = |
3220 | (sync_cause & | 3237 | (sync_cause & |
@@ -4078,3 +4095,198 @@ void ath9k_hw_set11nmac2040(struct ath_hw *ah, enum ath9k_ht_macmode mode) | |||
4078 | 4095 | ||
4079 | REG_WRITE(ah, AR_2040_MODE, macmode); | 4096 | REG_WRITE(ah, AR_2040_MODE, macmode); |
4080 | } | 4097 | } |
4098 | |||
4099 | /* HW Generic timers configuration */ | ||
4100 | |||
4101 | static const struct ath_gen_timer_configuration gen_tmr_configuration[] = | ||
4102 | { | ||
4103 | {AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080}, | ||
4104 | {AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080}, | ||
4105 | {AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080}, | ||
4106 | {AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080}, | ||
4107 | {AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080}, | ||
4108 | {AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080}, | ||
4109 | {AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080}, | ||
4110 | {AR_NEXT_NDP_TIMER, AR_NDP_PERIOD, AR_TIMER_MODE, 0x0080}, | ||
4111 | {AR_NEXT_NDP2_TIMER, AR_NDP2_PERIOD, AR_NDP2_TIMER_MODE, 0x0001}, | ||
4112 | {AR_NEXT_NDP2_TIMER + 1*4, AR_NDP2_PERIOD + 1*4, | ||
4113 | AR_NDP2_TIMER_MODE, 0x0002}, | ||
4114 | {AR_NEXT_NDP2_TIMER + 2*4, AR_NDP2_PERIOD + 2*4, | ||
4115 | AR_NDP2_TIMER_MODE, 0x0004}, | ||
4116 | {AR_NEXT_NDP2_TIMER + 3*4, AR_NDP2_PERIOD + 3*4, | ||
4117 | AR_NDP2_TIMER_MODE, 0x0008}, | ||
4118 | {AR_NEXT_NDP2_TIMER + 4*4, AR_NDP2_PERIOD + 4*4, | ||
4119 | AR_NDP2_TIMER_MODE, 0x0010}, | ||
4120 | {AR_NEXT_NDP2_TIMER + 5*4, AR_NDP2_PERIOD + 5*4, | ||
4121 | AR_NDP2_TIMER_MODE, 0x0020}, | ||
4122 | {AR_NEXT_NDP2_TIMER + 6*4, AR_NDP2_PERIOD + 6*4, | ||
4123 | AR_NDP2_TIMER_MODE, 0x0040}, | ||
4124 | {AR_NEXT_NDP2_TIMER + 7*4, AR_NDP2_PERIOD + 7*4, | ||
4125 | AR_NDP2_TIMER_MODE, 0x0080} | ||
4126 | }; | ||
4127 | |||
4128 | /* HW generic timer primitives */ | ||
4129 | |||
4130 | /* compute and clear index of rightmost 1 */ | ||
4131 | static u32 rightmost_index(struct ath_gen_timer_table *timer_table, u32 *mask) | ||
4132 | { | ||
4133 | u32 b; | ||
4134 | |||
4135 | b = *mask; | ||
4136 | b &= (0-b); | ||
4137 | *mask &= ~b; | ||
4138 | b *= debruijn32; | ||
4139 | b >>= 27; | ||
4140 | |||
4141 | return timer_table->gen_timer_index[b]; | ||
4142 | } | ||
4143 | |||
4144 | static u32 ath9k_hw_gettsf32(struct ath_hw *ah) | ||
4145 | { | ||
4146 | return REG_READ(ah, AR_TSF_L32); | ||
4147 | } | ||
4148 | |||
4149 | struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah, | ||
4150 | void (*trigger)(void *), | ||
4151 | void (*overflow)(void *), | ||
4152 | void *arg, | ||
4153 | u8 timer_index) | ||
4154 | { | ||
4155 | struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; | ||
4156 | struct ath_gen_timer *timer; | ||
4157 | |||
4158 | timer = kzalloc(sizeof(struct ath_gen_timer), GFP_KERNEL); | ||
4159 | |||
4160 | if (timer == NULL) { | ||
4161 | printk(KERN_DEBUG "Failed to allocate memory" | ||
4162 | "for hw timer[%d]\n", timer_index); | ||
4163 | return NULL; | ||
4164 | } | ||
4165 | |||
4166 | /* allocate a hardware generic timer slot */ | ||
4167 | timer_table->timers[timer_index] = timer; | ||
4168 | timer->index = timer_index; | ||
4169 | timer->trigger = trigger; | ||
4170 | timer->overflow = overflow; | ||
4171 | timer->arg = arg; | ||
4172 | |||
4173 | return timer; | ||
4174 | } | ||
4175 | |||
4176 | void ath_gen_timer_start(struct ath_hw *ah, | ||
4177 | struct ath_gen_timer *timer, | ||
4178 | u32 timer_next, u32 timer_period) | ||
4179 | { | ||
4180 | struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; | ||
4181 | u32 tsf; | ||
4182 | |||
4183 | BUG_ON(!timer_period); | ||
4184 | |||
4185 | set_bit(timer->index, &timer_table->timer_mask.timer_bits); | ||
4186 | |||
4187 | tsf = ath9k_hw_gettsf32(ah); | ||
4188 | |||
4189 | DPRINTF(ah->ah_sc, ATH_DBG_HWTIMER, "curent tsf %x period %x" | ||
4190 | "timer_next %x\n", tsf, timer_period, timer_next); | ||
4191 | |||
4192 | /* | ||
4193 | * Pull timer_next forward if the current TSF already passed it | ||
4194 | * because of software latency | ||
4195 | */ | ||
4196 | if (timer_next < tsf) | ||
4197 | timer_next = tsf + timer_period; | ||
4198 | |||
4199 | /* | ||
4200 | * Program generic timer registers | ||
4201 | */ | ||
4202 | REG_WRITE(ah, gen_tmr_configuration[timer->index].next_addr, | ||
4203 | timer_next); | ||
4204 | REG_WRITE(ah, gen_tmr_configuration[timer->index].period_addr, | ||
4205 | timer_period); | ||
4206 | REG_SET_BIT(ah, gen_tmr_configuration[timer->index].mode_addr, | ||
4207 | gen_tmr_configuration[timer->index].mode_mask); | ||
4208 | |||
4209 | /* Enable both trigger and thresh interrupt masks */ | ||
4210 | REG_SET_BIT(ah, AR_IMR_S5, | ||
4211 | (SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_THRESH) | | ||
4212 | SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_TRIG))); | ||
4213 | |||
4214 | if ((ah->ah_sc->imask & ATH9K_INT_GENTIMER) == 0) { | ||
4215 | ath9k_hw_set_interrupts(ah, 0); | ||
4216 | ah->ah_sc->imask |= ATH9K_INT_GENTIMER; | ||
4217 | ath9k_hw_set_interrupts(ah, ah->ah_sc->imask); | ||
4218 | } | ||
4219 | } | ||
4220 | |||
4221 | void ath_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer) | ||
4222 | { | ||
4223 | struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; | ||
4224 | |||
4225 | if ((timer->index < AR_FIRST_NDP_TIMER) || | ||
4226 | (timer->index >= ATH_MAX_GEN_TIMER)) { | ||
4227 | return; | ||
4228 | } | ||
4229 | |||
4230 | /* Clear generic timer enable bits. */ | ||
4231 | REG_CLR_BIT(ah, gen_tmr_configuration[timer->index].mode_addr, | ||
4232 | gen_tmr_configuration[timer->index].mode_mask); | ||
4233 | |||
4234 | /* Disable both trigger and thresh interrupt masks */ | ||
4235 | REG_CLR_BIT(ah, AR_IMR_S5, | ||
4236 | (SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_THRESH) | | ||
4237 | SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_TRIG))); | ||
4238 | |||
4239 | clear_bit(timer->index, &timer_table->timer_mask.timer_bits); | ||
4240 | |||
4241 | /* if no timer is enabled, turn off interrupt mask */ | ||
4242 | if (timer_table->timer_mask.val == 0) { | ||
4243 | ath9k_hw_set_interrupts(ah, 0); | ||
4244 | ah->ah_sc->imask &= ~ATH9K_INT_GENTIMER; | ||
4245 | ath9k_hw_set_interrupts(ah, ah->ah_sc->imask); | ||
4246 | } | ||
4247 | } | ||
4248 | |||
4249 | void ath_gen_timer_free(struct ath_hw *ah, struct ath_gen_timer *timer) | ||
4250 | { | ||
4251 | struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; | ||
4252 | |||
4253 | /* free the hardware generic timer slot */ | ||
4254 | timer_table->timers[timer->index] = NULL; | ||
4255 | kfree(timer); | ||
4256 | } | ||
4257 | |||
4258 | /* | ||
4259 | * Generic Timer Interrupts handling | ||
4260 | */ | ||
4261 | void ath_gen_timer_isr(struct ath_hw *ah) | ||
4262 | { | ||
4263 | struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; | ||
4264 | struct ath_gen_timer *timer; | ||
4265 | u32 trigger_mask, thresh_mask, index; | ||
4266 | |||
4267 | /* get hardware generic timer interrupt status */ | ||
4268 | trigger_mask = ah->intr_gen_timer_trigger; | ||
4269 | thresh_mask = ah->intr_gen_timer_thresh; | ||
4270 | trigger_mask &= timer_table->timer_mask.val; | ||
4271 | thresh_mask &= timer_table->timer_mask.val; | ||
4272 | |||
4273 | trigger_mask &= ~thresh_mask; | ||
4274 | |||
4275 | while (thresh_mask) { | ||
4276 | index = rightmost_index(timer_table, &thresh_mask); | ||
4277 | timer = timer_table->timers[index]; | ||
4278 | BUG_ON(!timer); | ||
4279 | DPRINTF(ah->ah_sc, ATH_DBG_HWTIMER, | ||
4280 | "TSF overflow for Gen timer %d\n", index); | ||
4281 | timer->overflow(timer->arg); | ||
4282 | } | ||
4283 | |||
4284 | while (trigger_mask) { | ||
4285 | index = rightmost_index(timer_table, &trigger_mask); | ||
4286 | timer = timer_table->timers[index]; | ||
4287 | BUG_ON(!timer); | ||
4288 | DPRINTF(ah->ah_sc, ATH_DBG_HWTIMER, | ||
4289 | "Gen timer[%d] trigger\n", index); | ||
4290 | timer->trigger(timer->arg); | ||
4291 | } | ||
4292 | } | ||
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index de10de8370d2..052a9c4ebb92 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h | |||
@@ -237,6 +237,7 @@ enum ath9k_int { | |||
237 | ATH9K_INT_GPIO = 0x01000000, | 237 | ATH9K_INT_GPIO = 0x01000000, |
238 | ATH9K_INT_CABEND = 0x02000000, | 238 | ATH9K_INT_CABEND = 0x02000000, |
239 | ATH9K_INT_TSFOOR = 0x04000000, | 239 | ATH9K_INT_TSFOOR = 0x04000000, |
240 | ATH9K_INT_GENTIMER = 0x08000000, | ||
240 | ATH9K_INT_CST = 0x10000000, | 241 | ATH9K_INT_CST = 0x10000000, |
241 | ATH9K_INT_GTT = 0x20000000, | 242 | ATH9K_INT_GTT = 0x20000000, |
242 | ATH9K_INT_FATAL = 0x40000000, | 243 | ATH9K_INT_FATAL = 0x40000000, |
@@ -390,6 +391,41 @@ struct ath9k_hw_version { | |||
390 | u16 analog2GhzRev; | 391 | u16 analog2GhzRev; |
391 | }; | 392 | }; |
392 | 393 | ||
394 | /* Generic TSF timer definitions */ | ||
395 | |||
396 | #define ATH_MAX_GEN_TIMER 16 | ||
397 | |||
398 | #define AR_GENTMR_BIT(_index) (1 << (_index)) | ||
399 | |||
400 | /* | ||
401 | * Using de Bruijin sequence to to look up 1's index in a 32 bit number | ||
402 | * debruijn32 = 0000 0111 0111 1100 1011 0101 0011 0001 | ||
403 | */ | ||
404 | #define debruijn32 0x077CB531UL | ||
405 | |||
406 | struct ath_gen_timer_configuration { | ||
407 | u32 next_addr; | ||
408 | u32 period_addr; | ||
409 | u32 mode_addr; | ||
410 | u32 mode_mask; | ||
411 | }; | ||
412 | |||
413 | struct ath_gen_timer { | ||
414 | void (*trigger)(void *arg); | ||
415 | void (*overflow)(void *arg); | ||
416 | void *arg; | ||
417 | u8 index; | ||
418 | }; | ||
419 | |||
420 | struct ath_gen_timer_table { | ||
421 | u32 gen_timer_index[32]; | ||
422 | struct ath_gen_timer *timers[ATH_MAX_GEN_TIMER]; | ||
423 | union { | ||
424 | unsigned long timer_bits; | ||
425 | u16 val; | ||
426 | } timer_mask; | ||
427 | }; | ||
428 | |||
393 | struct ath_hw { | 429 | struct ath_hw { |
394 | struct ath_softc *ah_sc; | 430 | struct ath_softc *ah_sc; |
395 | struct ath9k_hw_version hw_version; | 431 | struct ath9k_hw_version hw_version; |
@@ -536,6 +572,10 @@ struct ath_hw { | |||
536 | struct ar5416IniArray iniModesAdditional; | 572 | struct ar5416IniArray iniModesAdditional; |
537 | struct ar5416IniArray iniModesRxGain; | 573 | struct ar5416IniArray iniModesRxGain; |
538 | struct ar5416IniArray iniModesTxGain; | 574 | struct ar5416IniArray iniModesTxGain; |
575 | |||
576 | u32 intr_gen_timer_trigger; | ||
577 | u32 intr_gen_timer_thresh; | ||
578 | struct ath_gen_timer_table hw_gen_timers; | ||
539 | }; | 579 | }; |
540 | 580 | ||
541 | /* Initialization, Detach, Reset */ | 581 | /* Initialization, Detach, Reset */ |
@@ -611,4 +651,16 @@ bool ath9k_hw_intrpend(struct ath_hw *ah); | |||
611 | bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked); | 651 | bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked); |
612 | enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints); | 652 | enum ath9k_int ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints); |
613 | 653 | ||
654 | /* Generic hw timer primitives */ | ||
655 | struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah, | ||
656 | void (*trigger)(void *), | ||
657 | void (*overflow)(void *), | ||
658 | void *arg, | ||
659 | u8 timer_index); | ||
660 | void ath_gen_timer_start(struct ath_hw *ah, struct ath_gen_timer *timer, | ||
661 | u32 timer_next, u32 timer_period); | ||
662 | void ath_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer); | ||
663 | void ath_gen_timer_free(struct ath_hw *ah, struct ath_gen_timer *timer); | ||
664 | void ath_gen_timer_isr(struct ath_hw *hw); | ||
665 | |||
614 | #endif | 666 | #endif |
diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index c9e1ac92d0e9..1d8e0a8b587c 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h | |||
@@ -234,7 +234,15 @@ | |||
234 | #define AR_IMR_S5 0x00b8 | 234 | #define AR_IMR_S5 0x00b8 |
235 | #define AR_IMR_S5_TIM_TIMER 0x00000010 | 235 | #define AR_IMR_S5_TIM_TIMER 0x00000010 |
236 | #define AR_IMR_S5_DTIM_TIMER 0x00000020 | 236 | #define AR_IMR_S5_DTIM_TIMER 0x00000020 |
237 | 237 | #define AR_ISR_S5_GENTIMER_TRIG 0x0000FF80 | |
238 | #define AR_ISR_S5_GENTIMER_TRIG_S 0 | ||
239 | #define AR_ISR_S5_GENTIMER_THRESH 0xFF800000 | ||
240 | #define AR_ISR_S5_GENTIMER_THRESH_S 16 | ||
241 | #define AR_ISR_S5_S 0x00d8 | ||
242 | #define AR_IMR_S5_GENTIMER_TRIG 0x0000FF80 | ||
243 | #define AR_IMR_S5_GENTIMER_TRIG_S 0 | ||
244 | #define AR_IMR_S5_GENTIMER_THRESH 0xFF800000 | ||
245 | #define AR_IMR_S5_GENTIMER_THRESH_S 16 | ||
238 | 246 | ||
239 | #define AR_IMR 0x00a0 | 247 | #define AR_IMR 0x00a0 |
240 | #define AR_IMR_RXOK 0x00000001 | 248 | #define AR_IMR_RXOK 0x00000001 |
@@ -1516,7 +1524,10 @@ enum { | |||
1516 | #define AR_TXOP_8_11 0x81f8 | 1524 | #define AR_TXOP_8_11 0x81f8 |
1517 | #define AR_TXOP_12_15 0x81fc | 1525 | #define AR_TXOP_12_15 0x81fc |
1518 | 1526 | ||
1519 | 1527 | #define AR_NEXT_NDP2_TIMER 0x8180 | |
1528 | #define AR_FIRST_NDP_TIMER 7 | ||
1529 | #define AR_NDP2_PERIOD 0x81a0 | ||
1530 | #define AR_NDP2_TIMER_MODE 0x81c0 | ||
1520 | #define AR_NEXT_TBTT_TIMER 0x8200 | 1531 | #define AR_NEXT_TBTT_TIMER 0x8200 |
1521 | #define AR_NEXT_DMA_BEACON_ALERT 0x8204 | 1532 | #define AR_NEXT_DMA_BEACON_ALERT 0x8204 |
1522 | #define AR_NEXT_SWBA 0x8208 | 1533 | #define AR_NEXT_SWBA 0x8208 |