aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Buesch <mb@bu3sch.de>2006-06-05 14:24:21 -0400
committerJohn W. Linville <linville@tuxdriver.com>2006-06-15 15:48:13 -0400
commit91769e7dd9cef7988dc4280f74ed168351beb5b8 (patch)
treeef854d83ec881882b94a3e88b580e2468f62bac1
parent78ff56a06edc3407996173daf63e48f6b90c7062 (diff)
[PATCH] bcm43xx: preemptible periodic work
Make the heavy periodic work preemptible to avoid disabling local IRQs for several msecs. Signed-off-by: Michael Buesch <mb@buesch.de> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_main.c97
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_phy.c7
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_pio.c39
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_pio.h13
4 files changed, 143 insertions, 13 deletions
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.c b/drivers/net/wireless/bcm43xx/bcm43xx_main.c
index 835a2df1fe30..77d0e390b021 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_main.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.c
@@ -498,11 +498,21 @@ static inline u32 bcm43xx_interrupt_disable(struct bcm43xx_private *bcm, u32 mas
498 return old_mask; 498 return old_mask;
499} 499}
500 500
501/* Synchronize IRQ top- and bottom-half.
502 * IRQs must be masked before calling this.
503 * This must not be called with the irq_lock held.
504 */
505static void bcm43xx_synchronize_irq(struct bcm43xx_private *bcm)
506{
507 synchronize_irq(bcm->irq);
508 tasklet_disable(&bcm->isr_tasklet);
509}
510
501/* Make sure we don't receive more data from the device. */ 511/* Make sure we don't receive more data from the device. */
502static int bcm43xx_disable_interrupts_sync(struct bcm43xx_private *bcm, u32 *oldstate) 512static int bcm43xx_disable_interrupts_sync(struct bcm43xx_private *bcm, u32 *oldstate)
503{ 513{
504 u32 old;
505 unsigned long flags; 514 unsigned long flags;
515 u32 old;
506 516
507 bcm43xx_lock_irqonly(bcm, flags); 517 bcm43xx_lock_irqonly(bcm, flags);
508 if (unlikely(bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED)) { 518 if (unlikely(bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED)) {
@@ -510,8 +520,9 @@ static int bcm43xx_disable_interrupts_sync(struct bcm43xx_private *bcm, u32 *old
510 return -EBUSY; 520 return -EBUSY;
511 } 521 }
512 old = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL); 522 old = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
513 tasklet_disable(&bcm->isr_tasklet);
514 bcm43xx_unlock_irqonly(bcm, flags); 523 bcm43xx_unlock_irqonly(bcm, flags);
524 bcm43xx_synchronize_irq(bcm);
525
515 if (oldstate) 526 if (oldstate)
516 *oldstate = old; 527 *oldstate = old;
517 528
@@ -3108,14 +3119,10 @@ static void bcm43xx_periodic_every15sec(struct bcm43xx_private *bcm)
3108 //TODO for APHY (temperature?) 3119 //TODO for APHY (temperature?)
3109} 3120}
3110 3121
3111static void bcm43xx_periodic_work_handler(void *d) 3122static void do_periodic_work(struct bcm43xx_private *bcm)
3112{ 3123{
3113 struct bcm43xx_private *bcm = d;
3114 unsigned long flags;
3115 unsigned int state; 3124 unsigned int state;
3116 3125
3117 bcm43xx_lock_irqsafe(bcm, flags);
3118
3119 state = bcm->periodic_state; 3126 state = bcm->periodic_state;
3120 if (state % 8 == 0) 3127 if (state % 8 == 0)
3121 bcm43xx_periodic_every120sec(bcm); 3128 bcm43xx_periodic_every120sec(bcm);
@@ -3123,13 +3130,79 @@ static void bcm43xx_periodic_work_handler(void *d)
3123 bcm43xx_periodic_every60sec(bcm); 3130 bcm43xx_periodic_every60sec(bcm);
3124 if (state % 2 == 0) 3131 if (state % 2 == 0)
3125 bcm43xx_periodic_every30sec(bcm); 3132 bcm43xx_periodic_every30sec(bcm);
3126 bcm43xx_periodic_every15sec(bcm); 3133 if (state % 1 == 0)
3134 bcm43xx_periodic_every15sec(bcm);
3127 bcm->periodic_state = state + 1; 3135 bcm->periodic_state = state + 1;
3128 3136
3129 schedule_delayed_work(&bcm->periodic_work, HZ * 15); 3137 schedule_delayed_work(&bcm->periodic_work, HZ * 15);
3138}
3130 3139
3131 mmiowb(); 3140/* Estimate a "Badness" value based on the periodic work
3132 bcm43xx_unlock_irqsafe(bcm, flags); 3141 * state-machine state. "Badness" is worse (bigger), if the
3142 * periodic work will take longer.
3143 */
3144static int estimate_periodic_work_badness(unsigned int state)
3145{
3146 int badness = 0;
3147
3148 if (state % 8 == 0) /* every 120 sec */
3149 badness += 10;
3150 if (state % 4 == 0) /* every 60 sec */
3151 badness += 5;
3152 if (state % 2 == 0) /* every 30 sec */
3153 badness += 1;
3154 if (state % 1 == 0) /* every 15 sec */
3155 badness += 1;
3156
3157#define BADNESS_LIMIT 4
3158 return badness;
3159}
3160
3161static void bcm43xx_periodic_work_handler(void *d)
3162{
3163 struct bcm43xx_private *bcm = d;
3164 unsigned long flags;
3165 u32 savedirqs = 0;
3166 int badness;
3167
3168 badness = estimate_periodic_work_badness(bcm->periodic_state);
3169 if (badness > BADNESS_LIMIT) {
3170 /* Periodic work will take a long time, so we want it to
3171 * be preemtible.
3172 */
3173 bcm43xx_lock_irqonly(bcm, flags);
3174 netif_stop_queue(bcm->net_dev);
3175 if (bcm43xx_using_pio(bcm))
3176 bcm43xx_pio_freeze_txqueues(bcm);
3177 savedirqs = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
3178 bcm43xx_unlock_irqonly(bcm, flags);
3179 bcm43xx_lock_noirq(bcm);
3180 bcm43xx_synchronize_irq(bcm);
3181 } else {
3182 /* Periodic work should take short time, so we want low
3183 * locking overhead.
3184 */
3185 bcm43xx_lock_irqsafe(bcm, flags);
3186 }
3187
3188 do_periodic_work(bcm);
3189
3190 if (badness > BADNESS_LIMIT) {
3191 bcm43xx_lock_irqonly(bcm, flags);
3192 if (likely(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED)) {
3193 tasklet_enable(&bcm->isr_tasklet);
3194 bcm43xx_interrupt_enable(bcm, savedirqs);
3195 if (bcm43xx_using_pio(bcm))
3196 bcm43xx_pio_thaw_txqueues(bcm);
3197 }
3198 netif_wake_queue(bcm->net_dev);
3199 mmiowb();
3200 bcm43xx_unlock_irqonly(bcm, flags);
3201 bcm43xx_unlock_noirq(bcm);
3202 } else {
3203 mmiowb();
3204 bcm43xx_unlock_irqsafe(bcm, flags);
3205 }
3133} 3206}
3134 3207
3135static void bcm43xx_periodic_tasks_delete(struct bcm43xx_private *bcm) 3208static void bcm43xx_periodic_tasks_delete(struct bcm43xx_private *bcm)
@@ -3670,9 +3743,11 @@ static int bcm43xx_net_open(struct net_device *net_dev)
3670static int bcm43xx_net_stop(struct net_device *net_dev) 3743static int bcm43xx_net_stop(struct net_device *net_dev)
3671{ 3744{
3672 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); 3745 struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
3746 int err;
3673 3747
3674 ieee80211softmac_stop(net_dev); 3748 ieee80211softmac_stop(net_dev);
3675 bcm43xx_disable_interrupts_sync(bcm, NULL); 3749 err = bcm43xx_disable_interrupts_sync(bcm, NULL);
3750 assert(!err);
3676 bcm43xx_free_board(bcm); 3751 bcm43xx_free_board(bcm);
3677 3752
3678 return 0; 3753 return 0;
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_phy.c b/drivers/net/wireless/bcm43xx/bcm43xx_phy.c
index 952c2f8b8b5e..f8200deecc8a 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_phy.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_phy.c
@@ -1410,7 +1410,10 @@ static inline
1410u16 bcm43xx_phy_lo_g_deviation_subval(struct bcm43xx_private *bcm, u16 control) 1410u16 bcm43xx_phy_lo_g_deviation_subval(struct bcm43xx_private *bcm, u16 control)
1411{ 1411{
1412 struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); 1412 struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
1413 u16 ret;
1414 unsigned long flags;
1413 1415
1416 local_irq_save(flags);
1414 if (phy->connected) { 1417 if (phy->connected) {
1415 bcm43xx_phy_write(bcm, 0x15, 0xE300); 1418 bcm43xx_phy_write(bcm, 0x15, 0xE300);
1416 control <<= 8; 1419 control <<= 8;
@@ -1430,8 +1433,10 @@ u16 bcm43xx_phy_lo_g_deviation_subval(struct bcm43xx_private *bcm, u16 control)
1430 bcm43xx_phy_write(bcm, 0x0015, control | 0xFFE0); 1433 bcm43xx_phy_write(bcm, 0x0015, control | 0xFFE0);
1431 udelay(8); 1434 udelay(8);
1432 } 1435 }
1436 ret = bcm43xx_phy_read(bcm, 0x002D);
1437 local_irq_restore(flags);
1433 1438
1434 return bcm43xx_phy_read(bcm, 0x002D); 1439 return ret;
1435} 1440}
1436 1441
1437static u32 bcm43xx_phy_lo_g_singledeviation(struct bcm43xx_private *bcm, u16 control) 1442static u32 bcm43xx_phy_lo_g_singledeviation(struct bcm43xx_private *bcm, u16 control)
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_pio.c b/drivers/net/wireless/bcm43xx/bcm43xx_pio.c
index e77e9d859f87..574085c46152 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_pio.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_pio.c
@@ -264,6 +264,8 @@ static void tx_tasklet(unsigned long d)
264 264
265 bcm43xx_lock_irqonly(bcm, flags); 265 bcm43xx_lock_irqonly(bcm, flags);
266 266
267 if (queue->tx_frozen)
268 goto out_unlock;
267 txctl = bcm43xx_pio_read(queue, BCM43xx_PIO_TXCTL); 269 txctl = bcm43xx_pio_read(queue, BCM43xx_PIO_TXCTL);
268 if (txctl & BCM43xx_PIO_TXCTL_SUSPEND) 270 if (txctl & BCM43xx_PIO_TXCTL_SUSPEND)
269 goto out_unlock; 271 goto out_unlock;
@@ -633,5 +635,40 @@ void bcm43xx_pio_tx_resume(struct bcm43xx_pioqueue *queue)
633 bcm43xx_pio_read(queue, BCM43xx_PIO_TXCTL) 635 bcm43xx_pio_read(queue, BCM43xx_PIO_TXCTL)
634 & ~BCM43xx_PIO_TXCTL_SUSPEND); 636 & ~BCM43xx_PIO_TXCTL_SUSPEND);
635 bcm43xx_power_saving_ctl_bits(queue->bcm, -1, -1); 637 bcm43xx_power_saving_ctl_bits(queue->bcm, -1, -1);
636 tasklet_schedule(&queue->txtask); 638 if (!list_empty(&queue->txqueue))
639 tasklet_schedule(&queue->txtask);
640}
641
642void bcm43xx_pio_freeze_txqueues(struct bcm43xx_private *bcm)
643{
644 struct bcm43xx_pio *pio;
645
646 assert(bcm43xx_using_pio(bcm));
647 pio = bcm43xx_current_pio(bcm);
648 pio->queue0->tx_frozen = 1;
649 pio->queue1->tx_frozen = 1;
650 pio->queue2->tx_frozen = 1;
651 pio->queue3->tx_frozen = 1;
637} 652}
653
654void bcm43xx_pio_thaw_txqueues(struct bcm43xx_private *bcm)
655{
656 struct bcm43xx_pio *pio;
657
658 assert(bcm43xx_using_pio(bcm));
659 pio = bcm43xx_current_pio(bcm);
660 pio->queue0->tx_frozen = 0;
661 pio->queue1->tx_frozen = 0;
662 pio->queue2->tx_frozen = 0;
663 pio->queue3->tx_frozen = 0;
664 if (!list_empty(&pio->queue0->txqueue))
665 tasklet_schedule(&pio->queue0->txtask);
666 if (!list_empty(&pio->queue1->txqueue))
667 tasklet_schedule(&pio->queue1->txtask);
668 if (!list_empty(&pio->queue2->txqueue))
669 tasklet_schedule(&pio->queue2->txtask);
670 if (!list_empty(&pio->queue3->txqueue))
671 tasklet_schedule(&pio->queue3->txtask);
672}
673
674
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_pio.h b/drivers/net/wireless/bcm43xx/bcm43xx_pio.h
index dfc78209e3a3..bc78a3c2cafb 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_pio.h
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_pio.h
@@ -54,6 +54,7 @@ struct bcm43xx_pioqueue {
54 u16 mmio_base; 54 u16 mmio_base;
55 55
56 u8 tx_suspended:1, 56 u8 tx_suspended:1,
57 tx_frozen:1,
57 need_workarounds:1; /* Workarounds needed for core.rev < 3 */ 58 need_workarounds:1; /* Workarounds needed for core.rev < 3 */
58 59
59 /* Adjusted size of the device internal TX buffer. */ 60 /* Adjusted size of the device internal TX buffer. */
@@ -108,8 +109,12 @@ void bcm43xx_pio_handle_xmitstatus(struct bcm43xx_private *bcm,
108 struct bcm43xx_xmitstatus *status); 109 struct bcm43xx_xmitstatus *status);
109void bcm43xx_pio_rx(struct bcm43xx_pioqueue *queue); 110void bcm43xx_pio_rx(struct bcm43xx_pioqueue *queue);
110 111
112/* Suspend a TX queue on hardware level. */
111void bcm43xx_pio_tx_suspend(struct bcm43xx_pioqueue *queue); 113void bcm43xx_pio_tx_suspend(struct bcm43xx_pioqueue *queue);
112void bcm43xx_pio_tx_resume(struct bcm43xx_pioqueue *queue); 114void bcm43xx_pio_tx_resume(struct bcm43xx_pioqueue *queue);
115/* Suspend (freeze) the TX tasklet (software level). */
116void bcm43xx_pio_freeze_txqueues(struct bcm43xx_private *bcm);
117void bcm43xx_pio_thaw_txqueues(struct bcm43xx_private *bcm);
113 118
114#else /* CONFIG_BCM43XX_PIO */ 119#else /* CONFIG_BCM43XX_PIO */
115 120
@@ -145,6 +150,14 @@ static inline
145void bcm43xx_pio_tx_resume(struct bcm43xx_pioqueue *queue) 150void bcm43xx_pio_tx_resume(struct bcm43xx_pioqueue *queue)
146{ 151{
147} 152}
153static inline
154void bcm43xx_pio_freeze_txqueues(struct bcm43xx_private *bcm)
155{
156}
157static inline
158void bcm43xx_pio_thaw_txqueues(struct bcm43xx_private *bcm)
159{
160}
148 161
149#endif /* CONFIG_BCM43XX_PIO */ 162#endif /* CONFIG_BCM43XX_PIO */
150#endif /* BCM43xx_PIO_H_ */ 163#endif /* BCM43xx_PIO_H_ */