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/ath9k/hw.c | |
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/ath9k/hw.c')
-rw-r--r-- | drivers/net/wireless/ath/ath9k/hw.c | 212 |
1 files changed, 212 insertions, 0 deletions
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 | } | ||