Re: Anyone got a 4306 working properly?

2006-06-13 Thread Mattias Nissler
On Mon, 2006-06-12 at 14:19 -0500, Larry Finger wrote:
 Michael Buesch wrote:
  On Sunday 04 June 2006 21:49, Mattias Nissler wrote:
  On Sun, 2006-06-04 at 21:44 +0200, Michael Buesch wrote:
  On Sunday 04 June 2006 21:30, Mattias Nissler wrote:
  Well, mine is a mini-PCI. I'd replace mine by one of a newer rev and
  donate the old one if somebody can tell me where to buy a new mini-PCI
  card for my laptop.
  http://cgi.ebay.de/Broadcom-miniPCI-WLAN-64-Mbit-802-11b-g_W0QQitemZ9734722753QQcategoryZ79510QQrdZ1QQcmdZViewItem
  Seems to be a 4318. Keep in mind that this is not 100% supported, too.
  What about a new airport extreme card from apple? I'll check whether it
  fits and go for that if it does.
  
  The airport has a special connector that only fits into apple hardware.
  You could also buy an ipw2200. That works good, too.
  
 
 FWIW, now that I found the problem associating with a Linksys WRT54G V5, I 
 have been able to run my 
 Linksys WPC54G again using WPA-PSK. At startup, the following details are 
 reported:
 
 bcm43xx: Chip ID 0x4306, rev 0x2
 bcm43xx: Number of cores: 6
 bcm43xx: Core 0: ID 0x800, rev 0x2, vendor 0x4243, enabled
 bcm43xx: Core 1: ID 0x812, rev 0x4, vendor 0x4243, disabled
 bcm43xx: Core 2: ID 0x80d, rev 0x1, vendor 0x4243, enabled
 bcm43xx: Core 3: ID 0x807, rev 0x1, vendor 0x4243, disabled
 bcm43xx: Core 4: ID 0x804, rev 0x7, vendor 0x4243, enabled
 bcm43xx: Core 5: ID 0x812, rev 0x4, vendor 0x4243, disabled
 bcm43xx: Ignoring additional 802.11 core.
 bcm43xx: Detected PHY: Version: 1, Type 2, Revision 1
 bcm43xx: Detected Radio: ID: 2205017f (Manuf: 17f Ver: 2050 Rev: 2)
 
 With the June 5 versions of the bcm43xx: redesign locking and bcm43xx: 
 preemptible periodic work 
 patches applied to the 2.6.17-rc6 kernel, I am running with no problems. I 
 ran 50,000 pings to my AP 
 with no losses and no duplicates. The only thing being logged is an 
 occasional TKIP: replay 
 detected message, but they don't seem to do any harm.

That's interesting. I applied the latest patches here (redesign locking,
preemptible periodic work, the 2 softmac patches by joe and MAC
suspending) and still have DUPs and losses. This is even though my
revision numbers are exactly the same as yours. I even had one lockup
that forced me to reboot. However, I don't have any log information (I
was in X when the machine locked up).

Anyway, my new airport extreme card has arrived, I'm gonna try it the
next days.

Mattias


___
Bcm43xx-dev mailing list
Bcm43xx-dev@lists.berlios.de
http://lists.berlios.de/mailman/listinfo/bcm43xx-dev


Success w FC5 on PowerBook G4

2006-06-13 Thread Dean Jefferson
Thanks for all your hard work on the driver!I have been following your progress since last fall when I unsuccessfully tried to get the Airport Extreme cards working on a lab of iBook G4's running Yellow Dog Linux. We use them for our LAMP classes.
Last week I installed Fedora Core 5 PPC on my PowerBook G4, updated to the latest kernel available and installed the firmware. I had to put a modprobe command in my rc.local because the driver is not activated by default in FC5 yet. But it works great both on my home and college wireless networks, with no tweeking. I think you should activate it by default in FC5 if you have not already done so.
Thanks again! I look forward to getting my iBook lab machines working on wireless this summer.Dean Jefferson, InstructorMadison Area Technical College


Re: [PATCH] AP (master) mode fixed (resubmit)

2006-06-13 Thread Francois Barre

Hello Alex, all,

First Alex, thanks for that great work !


3) cpu_to_be/le32 were added in the places that seem appropriate for that
(but I still not sure about that enough; my CPU is LE)



Well guess what : mine is BE. And you know what ? Maybe I shall not
have left the x86 world afterall..

Here are the facts :
dmesg is spammed with
bcm43xx_d80211: FATAL ERROR: BCM43xx_IRQ_XMIT_ERROR
from bcm43xx_interrupt_tasklet(). This error is said to come from
endianness issues.

My (dmesg-) templates look like this :
bcm43xx_d80211: Template size: 3c
bcm43xx_d80211: Writing PLCP value: a040002
bcm43xx_d80211: Writing template value: 80
bcm43xx_d80211: Writing template value: 
bcm43xx_d80211: Writing template value: 
bcm43xx_d80211: Writing template value: 93241100
bcm43xx_d80211: Writing template value: 110069f7
bcm43xx_d80211: Writing template value: 69f79324
bcm43xx_d80211: Writing template value: 0
bcm43xx_d80211: Writing template value: 0
bcm43xx_d80211: Writing template value: 64
bcm43xx_d80211: Writing template value: 701
bcm43xx_d80211: Writing template value: 6e65706f
bcm43xx_d80211: Writing template value: 1747257
bcm43xx_d80211: Writing template value: b848204
bcm43xx_d80211: Writing template value: 1010316
bcm43xx_d80211: Writing template value: 2000405
bcm43xx_d80211: Writing template value: 0
bcm43xx_d80211: Template size: 36
bcm43xx_d80211: Writing PLCP value: 6e842b00
bcm43xx_d80211: Writing template value: 5000
bcm43xx_d80211: Writing template value: d700
bcm43xx_d80211: Writing template value: 
bcm43xx_d80211: Writing template value: 93241100
bcm43xx_d80211: Writing template value: 110069f7
bcm43xx_d80211: Writing template value: 69f79324
bcm43xx_d80211: Writing template value: 0
bcm43xx_d80211: Writing template value: 0
bcm43xx_d80211: Writing template value: 64
bcm43xx_d80211: Writing template value: 701
bcm43xx_d80211: Writing template value: 6e65706f
bcm43xx_d80211: Writing template value: 1747257
bcm43xx_d80211: Writing template value: b848204
bcm43xx_d80211: Writing template value: 1010316
bcm43xx_d80211: Templates updated

And there is no other relevant info I could give you upon this.
Would you be kind enough to comment and/or give me clues on what you'd
be expecting here, I'll be glad to correct that for BE machines...

My setup ? Apple Mini G4 with a bcm4306.

Best regards,
___
Bcm43xx-dev mailing list
Bcm43xx-dev@lists.berlios.de
http://lists.berlios.de/mailman/listinfo/bcm43xx-dev


[PATCH 1/4] bcm43xx: Port new locking scheme to d80211

2006-06-13 Thread Michael Buesch
Port the new locking scheme from bcm43xx-softmac to bcm43xx-d80211.

Signed-off-by: Michael Buesch [EMAIL PROTECTED]
Index: wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx.h
===
--- wireless-dev-dscapeports.orig/drivers/net/wireless/d80211/bcm43xx/bcm43xx.h 
2006-06-13 21:11:05.0 +0200
+++ wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx.h  
2006-06-13 21:11:08.0 +0200
@@ -649,6 +649,17 @@
unsigned int promisc:1;
 };
 
+/* Driver initialization status. */
+enum {
+   BCM43xx_STAT_UNINIT,/* Uninitialized. */
+   BCM43xx_STAT_INITIALIZING,  /* init_board() in progress. */
+   BCM43xx_STAT_INITIALIZED,   /* Fully operational. */
+   BCM43xx_STAT_SHUTTINGDOWN,  /* free_board() in progress. */
+   BCM43xx_STAT_RESTARTING,/* controller_restart() called. */
+};
+#define bcm43xx_status(bcm)atomic_read((bcm)-init_status)
+#define bcm43xx_set_status(bcm, stat)  atomic_set((bcm)-init_status, (stat))
+
 struct bcm43xx_private {
struct ieee80211_hw *ieee;
struct ieee80211_low_level_stats ieee_stats;
@@ -659,18 +670,17 @@
 
void __iomem *mmio_addr;
 
-   /* Do not use the lock directly. Use the bcm43xx_lock* helper
-* functions, to be MMIO-safe. */
-   spinlock_t _lock;
-
-   /* Driver status flags. */
-   u32 initialized:1,  /* init_board() succeed */
-   was_initialized:1,  /* for PCI suspend/resume. */
-   shutting_down:1,/* free_board() in progress */
+   /* Locking, see theory of locking text below. */
+   spinlock_t irq_lock;
+   struct mutex mutex;
+
+   /* Driver initialization status BCM43xx_STAT_*** */
+   atomic_t init_status;
+
+   u16 was_initialized:1,  /* for PCI suspend/resume. */
__using_pio:1,  /* Internal, use bcm43xx_using_pio(). */
bad_frames_preempt:1,   /* Use Bad Frames Preemption (default 
off) */
reg124_set_0x4:1,   /* Some variable to keep track of IRQ 
stuff. */
-   powersaving:1,  /* TRUE if we are in PowerSaving mode. 
FALSE otherwise. */
short_preamble:1,   /* TRUE, if short preamble is enabled. 
*/
short_slot:1,   /* TRUE, if short slot timing is 
enabled. */
firmware_norelease:1;   /* Do not release the firmware. Used on 
suspend. */
@@ -738,7 +748,7 @@
struct tasklet_struct isr_tasklet;
 
/* Periodic tasks */
-   struct timer_list periodic_tasks;
+   struct work_struct periodic_work;
unsigned int periodic_state;
 
struct work_struct restart_work;
@@ -766,21 +776,55 @@
 #endif
 };
 
-/* bcm43xx_(un)lock() protect struct bcm43xx_private.
- * Note that _NO_ MMIO writes are allowed. If you want to
- * write to the device through MMIO in the critical section, use
- * the *_mmio lock functions.
- * MMIO read-access is allowed, though.
- */
-#define bcm43xx_lock(bcm, flags)   spin_lock_irqsave((bcm)-_lock, flags)
-#define bcm43xx_unlock(bcm, flags) spin_unlock_irqrestore((bcm)-_lock, 
flags)
-/* bcm43xx_(un)lock_mmio() protect struct bcm43xx_private and MMIO.
- * MMIO write-access to the device is allowed.
- * All MMIO writes are flushed on unlock, so it is guaranteed to not
- * interfere with other threads writing MMIO registers.
+
+/**** THEORY OF LOCKING ***
+ *
+ * We have two different locks in the bcm43xx driver.
+ * = bcm-mutex:General sleeping mutex. Protects struct bcm43xx_private
+ *   and the device registers.
+ * = bcm-irq_lock: IRQ spinlock. Protects against IRQ handler concurrency.
+ *
+ * We have three types of helper function pairs to utilize these locks.
+ * (Always use the helper functions.)
+ * 1) bcm43xx_{un}lock_noirq():
+ * Takes bcm-mutex. Does _not_ protect against IRQ concurrency,
+ * so it is almost always unsafe, if device IRQs are enabled.
+ * So only use this, if device IRQs are masked.
+ * Locking may sleep.
+ * You can sleep within the critical section.
+ * 2) bcm43xx_{un}lock_irqonly():
+ * Takes bcm-irq_lock. Does _not_ protect against
+ * bcm43xx_lock_noirq() critical sections.
+ * Does only protect against the IRQ handler path and other
+ * irqonly() critical sections.
+ * Locking does not sleep.
+ * You must not sleep within the critical section.
+ * 3) bcm43xx_{un}lock_irqsafe():
+ * This is the cummulative lock and takes both, mutex and irq_lock.
+ * Protects against noirq() and irqonly() critical sections (and
+ * the IRQ handler path).
+ * Locking may sleep.
+ * You must not sleep within the critical section.
  */
-#define bcm43xx_lock_mmio(bcm, flags)  bcm43xx_lock(bcm, flags)
-#define bcm43xx_unlock_mmio(bcm, flags)do { mmiowb(); 
bcm43xx_unlock(bcm, 

[PATCH 3/4] bcm43xx: Port suspend-mac-in-long-pwork from d80211

2006-06-13 Thread Michael Buesch
Port the Suspend MAC In Long Periodic Work patch from bcm43xx-softmac to 
bcm43xx-d80211.

Signed-off-by: Michael Buesch [EMAIL PROTECTED]
Index: wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx.h
===
--- wireless-dev-dscapeports.orig/drivers/net/wireless/d80211/bcm43xx/bcm43xx.h 
2006-06-13 21:11:08.0 +0200
+++ wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx.h  
2006-06-13 21:11:37.0 +0200
@@ -743,6 +743,8 @@
u32 irq_savedstate;
/* Link Quality calculation context. */
struct bcm43xx_noise_calculation noisecalc;
+   /* if  0 MAC is suspended. if == 0 MAC is enabled. */
+   int mac_suspended;
 
/* Interrupt Service Routine tasklet (bottom-half) */
struct tasklet_struct isr_tasklet;
Index: 
wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.c
===
--- 
wireless-dev-dscapeports.orig/drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.c
2006-06-13 21:11:24.0 +0200
+++ wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.c 
2006-06-13 21:11:37.0 +0200
@@ -2169,13 +2169,17 @@
 /* http://bcm-specs.sipsolutions.net/EnableMac */
 void bcm43xx_mac_enable(struct bcm43xx_private *bcm)
 {
-   bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD,
-   bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD)
-   | BCM43xx_SBF_MAC_ENABLED);
-   bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, BCM43xx_IRQ_READY);
-   bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); /* dummy read */
-   bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read */
-   bcm43xx_power_saving_ctl_bits(bcm, -1, -1);
+   bcm-mac_suspended--;
+   assert(bcm-mac_suspended = 0);
+   if (bcm-mac_suspended == 0) {
+   bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD,
+   bcm43xx_read32(bcm, 
BCM43xx_MMIO_STATUS_BITFIELD)
+   | BCM43xx_SBF_MAC_ENABLED);
+   bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, 
BCM43xx_IRQ_READY);
+   bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); /* dummy 
read */
+   bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read 
*/
+   bcm43xx_power_saving_ctl_bits(bcm, -1, -1);
+   }
 }
 
 /* http://bcm-specs.sipsolutions.net/SuspendMAC */
@@ -2184,18 +2188,23 @@
int i;
u32 tmp;
 
-   bcm43xx_power_saving_ctl_bits(bcm, -1, 1);
-   bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD,
-   bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD)
-~BCM43xx_SBF_MAC_ENABLED);
-   bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read */
-   for (i = 10; i; i--) {
-   tmp = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON);
-   if (tmp  BCM43xx_IRQ_READY)
-   return;
-   udelay(10);
+   assert(bcm-mac_suspended = 0);
+   if (bcm-mac_suspended == 0) {
+   bcm43xx_power_saving_ctl_bits(bcm, -1, 1);
+   bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD,
+   bcm43xx_read32(bcm, 
BCM43xx_MMIO_STATUS_BITFIELD)
+~BCM43xx_SBF_MAC_ENABLED);
+   bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read 
*/
+   for (i = 10; i; i--) {
+   tmp = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON);
+   if (tmp  BCM43xx_IRQ_READY)
+   goto out;
+   udelay(10);
+   }
+   printkl(KERN_ERR PFX MAC suspend failed\n);
}
-   printkl(KERN_ERR PFX MAC suspend failed\n);
+out:
+   bcm-mac_suspended++;
 }
 
 static void bcm43xx_select_opmode(struct bcm43xx_private *bcm)
@@ -3013,8 +3022,10 @@
/* Periodic work will take a long time, so we want it to
 * be preemtible.
 */
-   bcm43xx_lock_irqonly(bcm, flags);
netif_stop_queue(bcm-net_dev);
+   synchronize_net();
+   bcm43xx_lock_irqonly(bcm, flags);
+   bcm43xx_mac_suspend(bcm);
if (bcm43xx_using_pio(bcm))
bcm43xx_pio_freeze_txqueues(bcm);
savedirqs = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
@@ -3037,6 +3048,7 @@
bcm43xx_interrupt_enable(bcm, savedirqs);
if (bcm43xx_using_pio(bcm))
bcm43xx_pio_thaw_txqueues(bcm);
+   bcm43xx_mac_enable(bcm);
}
netif_wake_queue(bcm-net_dev);
mmiowb();
@@ -4371,6 +4383,7 @@
 

[PATCH 4/4] bcm43xx: Port PIO fixes to d80211

2006-06-13 Thread Michael Buesch
Port the PIO fixes from bcm43xx-softmac to bcm43xx-d80211.

Signed-off-by: Michael Buesch [EMAIL PROTECTED]
Index: 
wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx_dma.h
===
--- 
wireless-dev-dscapeports.orig/drivers/net/wireless/d80211/bcm43xx/bcm43xx_dma.h 
2006-06-13 21:11:05.0 +0200
+++ wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx_dma.h  
2006-06-13 21:11:48.0 +0200
@@ -223,6 +223,14 @@
 void bcm43xx_dma_rx(struct bcm43xx_dmaring *ring)
 {
 }
+static inline
+void bcm43xx_dma_tx_suspend(struct bcm43xx_dmaring *ring)
+{
+}
+static inline
+void bcm43xx_dma_tx_resume(struct bcm43xx_dmaring *ring)
+{
+}
 
 #endif /* CONFIG_BCM43XX_D80211_DMA */
 #endif /* BCM43xx_DMA_H_ */
Index: 
wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx_pio.c
===
--- 
wireless-dev-dscapeports.orig/drivers/net/wireless/d80211/bcm43xx/bcm43xx_pio.c 
2006-06-13 21:11:24.0 +0200
+++ wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx_pio.c  
2006-06-13 21:11:48.0 +0200
@@ -27,6 +27,7 @@
 #include bcm43xx_pio.h
 #include bcm43xx_main.h
 #include bcm43xx_xmit.h
+#include bcm43xx_power.h
 
 #include linux/delay.h
 
@@ -44,10 +45,10 @@
bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA,
  octet);
bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL,
- BCM43xx_PIO_TXCTL_WRITEHI);
+ BCM43xx_PIO_TXCTL_WRITELO);
} else {
bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL,
- BCM43xx_PIO_TXCTL_WRITEHI);
+ BCM43xx_PIO_TXCTL_WRITELO);
bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA,
  octet);
}
@@ -103,7 +104,7 @@
bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA,
  skb-data[skb-len - 1]);
bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL,
- BCM43xx_PIO_TXCTL_WRITEHI |
+ BCM43xx_PIO_TXCTL_WRITELO |
  BCM43xx_PIO_TXCTL_COMPLETE);
} else {
bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL,
@@ -112,9 +113,10 @@
 }
 
 static u16 generate_cookie(struct bcm43xx_pioqueue *queue,
-  int packetindex)
+  struct bcm43xx_pio_txpacket *packet)
 {
u16 cookie = 0x;
+   int packetindex;
 
/* We use the upper 4 bits for the PIO
 * controller ID and the lower 12 bits
@@ -135,6 +137,7 @@
default:
assert(0);
}
+   packetindex = pio_txpacket_getindex(packet);
assert(((u16)packetindex  0xF000) == 0x);
cookie |= (u16)packetindex;
 
@@ -184,8 +187,8 @@
bcm43xx_generate_txhdr(queue-bcm,
   txhdr, skb-data, skb-len,
   1,//FIXME
-  generate_cookie(queue, 
pio_txpacket_getindex(packet)),
-  packet-ctl);
+  generate_cookie(queue, packet),
+  packet-txstat.control);
 
tx_start(queue);
octets = skb-len + sizeof(txhdr);
@@ -200,10 +203,12 @@
 {
struct bcm43xx_pioqueue *queue = packet-queue;
 
-   if (irq_context)
-   dev_kfree_skb_irq(packet-skb);
-   else
-   dev_kfree_skb(packet-skb);
+   if (packet-skb) {
+   if (irq_context)
+   dev_kfree_skb_irq(packet-skb);
+   else
+   dev_kfree_skb(packet-skb);
+   }
list_move(packet-list, queue-txfree);
queue-nr_txfree++;
 }
@@ -216,8 +221,8 @@
 
octets = (u16)skb-len + sizeof(struct bcm43xx_txhdr);
if (queue-tx_devq_size  octets) {
-   dprintkl(KERN_WARNING PFX PIO queue too small. 
- Dropping packet.\n);
+   printkl(KERN_WARNING PFX PIO queue too small. 
+Dropping packet.\n);
/* Drop it silently (return success) */
free_txpacket(packet, 1);
return 0;
@@ -256,10 +261,14 @@
unsigned long flags;
struct bcm43xx_pio_txpacket *packet, *tmp_packet;
int err;
+   u16 txctl;
 
bcm43xx_lock_irqonly(bcm, flags);
if (queue-tx_frozen)
goto out_unlock;
+   txctl = bcm43xx_pio_read(queue, BCM43xx_PIO_TXCTL);
+   if (txctl  BCM43xx_PIO_TXCTL_SUSPEND)
+   goto out_unlock;
 
list_for_each_entry_safe(packet, tmp_packet, queue-txqueue, list) {
/* Try to transmit the packet. This can fail, if
@@ 

[PATCH 2/4] bcm43xx: Port preemptible-periodic-work to d80211

2006-06-13 Thread Michael Buesch
Port the preemptible periodic work patch from bcm43xx-softmac to bcm43xx-d80211.

Signed-off-by: Michael Buesch [EMAIL PROTECTED]
Index: 
wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.c
===
--- 
wireless-dev-dscapeports.orig/drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.c
2006-06-13 21:11:08.0 +0200
+++ wireless-dev-dscapeports/drivers/net/wireless/d80211/bcm43xx/bcm43xx_main.c 
2006-06-13 21:11:24.0 +0200
@@ -529,11 +529,21 @@
return old_mask;
 }
 
+/* Synchronize IRQ top- and bottom-half.
+ * IRQs must be masked before calling this.
+ * This must not be called with the irq_lock held.
+ */
+static void bcm43xx_synchronize_irq(struct bcm43xx_private *bcm)
+{
+   synchronize_irq(bcm-irq);
+   tasklet_disable(bcm-isr_tasklet);
+}
+
 /* Make sure we don't receive more data from the device. */
 static int bcm43xx_disable_interrupts_sync(struct bcm43xx_private *bcm, u32 
*oldstate)
 {
-   u32 old;
unsigned long flags;
+   u32 old;
 
bcm43xx_lock_irqonly(bcm, flags);
if (unlikely(bcm43xx_status(bcm) != BCM43xx_STAT_INITIALIZED)) {
@@ -541,8 +551,9 @@
return -EBUSY;
}
old = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
-   tasklet_disable(bcm-isr_tasklet);
bcm43xx_unlock_irqonly(bcm, flags);
+   bcm43xx_synchronize_irq(bcm);
+
if (oldstate)
*oldstate = old;
 
@@ -2951,14 +2962,10 @@
//TODO for APHY (temperature?)
 }
 
-static void bcm43xx_periodic_work_handler(void *d)
+static void do_periodic_work(struct bcm43xx_private *bcm)
 {
-   struct bcm43xx_private *bcm = d;
-   unsigned long flags;
unsigned int state;
 
-   bcm43xx_lock_irqsafe(bcm, flags);
-
state = bcm-periodic_state;
if (state % 8 == 0)
bcm43xx_periodic_every120sec(bcm);
@@ -2966,13 +2973,79 @@
bcm43xx_periodic_every60sec(bcm);
if (state % 2 == 0)
bcm43xx_periodic_every30sec(bcm);
-   bcm43xx_periodic_every15sec(bcm);
+   if (state % 1 == 0)
+   bcm43xx_periodic_every15sec(bcm);
bcm-periodic_state = state + 1;
 
schedule_delayed_work(bcm-periodic_work, HZ * 15);
+}
 
-   mmiowb();
-   bcm43xx_unlock_irqsafe(bcm, flags);
+/* Estimate a Badness value based on the periodic work
+ * state-machine state. Badness is worse (bigger), if the
+ * periodic work will take longer.
+ */
+static int estimate_periodic_work_badness(unsigned int state)
+{
+   int badness = 0;
+
+   if (state % 8 == 0) /* every 120 sec */
+   badness += 10;
+   if (state % 4 == 0) /* every 60 sec */
+   badness += 5;
+   if (state % 2 == 0) /* every 30 sec */
+   badness += 1;
+   if (state % 1 == 0) /* every 15 sec */
+   badness += 1;
+
+#define BADNESS_LIMIT  4
+   return badness;
+}
+
+static void bcm43xx_periodic_work_handler(void *d)
+{
+   struct bcm43xx_private *bcm = d;
+   unsigned long flags;
+   u32 savedirqs = 0;
+   int badness;
+
+   badness = estimate_periodic_work_badness(bcm-periodic_state);
+   if (badness  BADNESS_LIMIT) {
+   /* Periodic work will take a long time, so we want it to
+* be preemtible.
+*/
+   bcm43xx_lock_irqonly(bcm, flags);
+   netif_stop_queue(bcm-net_dev);
+   if (bcm43xx_using_pio(bcm))
+   bcm43xx_pio_freeze_txqueues(bcm);
+   savedirqs = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
+   bcm43xx_unlock_irqonly(bcm, flags);
+   bcm43xx_lock_noirq(bcm);
+   bcm43xx_synchronize_irq(bcm);
+   } else {
+   /* Periodic work should take short time, so we want low
+* locking overhead.
+*/
+   bcm43xx_lock_irqsafe(bcm, flags);
+   }
+
+   do_periodic_work(bcm);
+
+   if (badness  BADNESS_LIMIT) {
+   bcm43xx_lock_irqonly(bcm, flags);
+   if (likely(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED)) {
+   tasklet_enable(bcm-isr_tasklet);
+   bcm43xx_interrupt_enable(bcm, savedirqs);
+   if (bcm43xx_using_pio(bcm))
+   bcm43xx_pio_thaw_txqueues(bcm);
+   }
+   netif_wake_queue(bcm-net_dev);
+   mmiowb();
+   bcm43xx_unlock_irqonly(bcm, flags);
+   bcm43xx_unlock_noirq(bcm);
+   } else {
+   mmiowb();
+   bcm43xx_unlock_irqsafe(bcm, flags);
+   }
 }
 
 static void bcm43xx_periodic_tasks_delete(struct bcm43xx_private *bcm)
@@ -4178,9 +4251,11 @@
 static int bcm43xx_net_stop(struct net_device *net_dev)
 {
struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+