diff options
-rw-r--r-- | drivers/net/wireless/b43legacy/main.c | 88 |
1 files changed, 30 insertions, 58 deletions
diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index 069abe5fb8ee..1daa5aa0ba25 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c | |||
@@ -1797,6 +1797,7 @@ void b43legacy_mac_enable(struct b43legacy_wldev *dev) | |||
1797 | { | 1797 | { |
1798 | dev->mac_suspended--; | 1798 | dev->mac_suspended--; |
1799 | B43legacy_WARN_ON(dev->mac_suspended < 0); | 1799 | B43legacy_WARN_ON(dev->mac_suspended < 0); |
1800 | B43legacy_WARN_ON(irqs_disabled()); | ||
1800 | if (dev->mac_suspended == 0) { | 1801 | if (dev->mac_suspended == 0) { |
1801 | b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD, | 1802 | b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD, |
1802 | b43legacy_read32(dev, | 1803 | b43legacy_read32(dev, |
@@ -1808,6 +1809,11 @@ void b43legacy_mac_enable(struct b43legacy_wldev *dev) | |||
1808 | b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD); | 1809 | b43legacy_read32(dev, B43legacy_MMIO_STATUS_BITFIELD); |
1809 | b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON); | 1810 | b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON); |
1810 | b43legacy_power_saving_ctl_bits(dev, -1, -1); | 1811 | b43legacy_power_saving_ctl_bits(dev, -1, -1); |
1812 | |||
1813 | /* Re-enable IRQs. */ | ||
1814 | spin_lock_irq(&dev->wl->irq_lock); | ||
1815 | b43legacy_interrupt_enable(dev, dev->irq_savedstate); | ||
1816 | spin_unlock_irq(&dev->wl->irq_lock); | ||
1811 | } | 1817 | } |
1812 | } | 1818 | } |
1813 | 1819 | ||
@@ -1817,20 +1823,31 @@ void b43legacy_mac_suspend(struct b43legacy_wldev *dev) | |||
1817 | int i; | 1823 | int i; |
1818 | u32 tmp; | 1824 | u32 tmp; |
1819 | 1825 | ||
1826 | might_sleep(); | ||
1827 | B43legacy_WARN_ON(irqs_disabled()); | ||
1820 | B43legacy_WARN_ON(dev->mac_suspended < 0); | 1828 | B43legacy_WARN_ON(dev->mac_suspended < 0); |
1829 | |||
1821 | if (dev->mac_suspended == 0) { | 1830 | if (dev->mac_suspended == 0) { |
1831 | /* Mask IRQs before suspending MAC. Otherwise | ||
1832 | * the MAC stays busy and won't suspend. */ | ||
1833 | spin_lock_irq(&dev->wl->irq_lock); | ||
1834 | tmp = b43legacy_interrupt_disable(dev, B43legacy_IRQ_ALL); | ||
1835 | spin_unlock_irq(&dev->wl->irq_lock); | ||
1836 | b43legacy_synchronize_irq(dev); | ||
1837 | dev->irq_savedstate = tmp; | ||
1838 | |||
1822 | b43legacy_power_saving_ctl_bits(dev, -1, 1); | 1839 | b43legacy_power_saving_ctl_bits(dev, -1, 1); |
1823 | b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD, | 1840 | b43legacy_write32(dev, B43legacy_MMIO_STATUS_BITFIELD, |
1824 | b43legacy_read32(dev, | 1841 | b43legacy_read32(dev, |
1825 | B43legacy_MMIO_STATUS_BITFIELD) | 1842 | B43legacy_MMIO_STATUS_BITFIELD) |
1826 | & ~B43legacy_SBF_MAC_ENABLED); | 1843 | & ~B43legacy_SBF_MAC_ENABLED); |
1827 | b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON); | 1844 | b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_REASON); |
1828 | for (i = 10000; i; i--) { | 1845 | for (i = 40; i; i--) { |
1829 | tmp = b43legacy_read32(dev, | 1846 | tmp = b43legacy_read32(dev, |
1830 | B43legacy_MMIO_GEN_IRQ_REASON); | 1847 | B43legacy_MMIO_GEN_IRQ_REASON); |
1831 | if (tmp & B43legacy_IRQ_MAC_SUSPENDED) | 1848 | if (tmp & B43legacy_IRQ_MAC_SUSPENDED) |
1832 | goto out; | 1849 | goto out; |
1833 | udelay(1); | 1850 | msleep(1); |
1834 | } | 1851 | } |
1835 | b43legacyerr(dev->wl, "MAC suspend failed\n"); | 1852 | b43legacyerr(dev->wl, "MAC suspend failed\n"); |
1836 | } | 1853 | } |
@@ -2145,81 +2162,36 @@ static void do_periodic_work(struct b43legacy_wldev *dev) | |||
2145 | b43legacy_periodic_every15sec(dev); | 2162 | b43legacy_periodic_every15sec(dev); |
2146 | } | 2163 | } |
2147 | 2164 | ||
2148 | /* Estimate a "Badness" value based on the periodic work | 2165 | /* Periodic work locking policy: |
2149 | * state-machine state. "Badness" is worse (bigger), if the | 2166 | * The whole periodic work handler is protected by |
2150 | * periodic work will take longer. | 2167 | * wl->mutex. If another lock is needed somewhere in the |
2168 | * pwork callchain, it's aquired in-place, where it's needed. | ||
2151 | */ | 2169 | */ |
2152 | static int estimate_periodic_work_badness(unsigned int state) | ||
2153 | { | ||
2154 | int badness = 0; | ||
2155 | |||
2156 | if (state % 8 == 0) /* every 120 sec */ | ||
2157 | badness += 10; | ||
2158 | if (state % 4 == 0) /* every 60 sec */ | ||
2159 | badness += 5; | ||
2160 | if (state % 2 == 0) /* every 30 sec */ | ||
2161 | badness += 1; | ||
2162 | |||
2163 | #define BADNESS_LIMIT 4 | ||
2164 | return badness; | ||
2165 | } | ||
2166 | |||
2167 | static void b43legacy_periodic_work_handler(struct work_struct *work) | 2170 | static void b43legacy_periodic_work_handler(struct work_struct *work) |
2168 | { | 2171 | { |
2169 | struct b43legacy_wldev *dev = | 2172 | struct b43legacy_wldev *dev = container_of(work, struct b43legacy_wldev, |
2170 | container_of(work, struct b43legacy_wldev, | 2173 | periodic_work.work); |
2171 | periodic_work.work); | 2174 | struct b43legacy_wl *wl = dev->wl; |
2172 | unsigned long flags; | ||
2173 | unsigned long delay; | 2175 | unsigned long delay; |
2174 | u32 savedirqs = 0; | ||
2175 | int badness; | ||
2176 | 2176 | ||
2177 | mutex_lock(&dev->wl->mutex); | 2177 | mutex_lock(&wl->mutex); |
2178 | 2178 | ||
2179 | if (unlikely(b43legacy_status(dev) != B43legacy_STAT_STARTED)) | 2179 | if (unlikely(b43legacy_status(dev) != B43legacy_STAT_STARTED)) |
2180 | goto out; | 2180 | goto out; |
2181 | if (b43legacy_debug(dev, B43legacy_DBG_PWORK_STOP)) | 2181 | if (b43legacy_debug(dev, B43legacy_DBG_PWORK_STOP)) |
2182 | goto out_requeue; | 2182 | goto out_requeue; |
2183 | 2183 | ||
2184 | badness = estimate_periodic_work_badness(dev->periodic_state); | 2184 | do_periodic_work(dev); |
2185 | if (badness > BADNESS_LIMIT) { | ||
2186 | spin_lock_irqsave(&dev->wl->irq_lock, flags); | ||
2187 | /* Suspend TX as we don't want to transmit packets while | ||
2188 | * we recalibrate the hardware. */ | ||
2189 | b43legacy_tx_suspend(dev); | ||
2190 | savedirqs = b43legacy_interrupt_disable(dev, | ||
2191 | B43legacy_IRQ_ALL); | ||
2192 | /* Periodic work will take a long time, so we want it to | ||
2193 | * be preemtible and release the spinlock. */ | ||
2194 | spin_unlock_irqrestore(&dev->wl->irq_lock, flags); | ||
2195 | b43legacy_synchronize_irq(dev); | ||
2196 | |||
2197 | do_periodic_work(dev); | ||
2198 | |||
2199 | spin_lock_irqsave(&dev->wl->irq_lock, flags); | ||
2200 | b43legacy_interrupt_enable(dev, savedirqs); | ||
2201 | b43legacy_tx_resume(dev); | ||
2202 | mmiowb(); | ||
2203 | spin_unlock_irqrestore(&dev->wl->irq_lock, flags); | ||
2204 | } else { | ||
2205 | /* Take the global driver lock. This will lock any operation. */ | ||
2206 | spin_lock_irqsave(&dev->wl->irq_lock, flags); | ||
2207 | |||
2208 | do_periodic_work(dev); | ||
2209 | 2185 | ||
2210 | mmiowb(); | ||
2211 | spin_unlock_irqrestore(&dev->wl->irq_lock, flags); | ||
2212 | } | ||
2213 | dev->periodic_state++; | 2186 | dev->periodic_state++; |
2214 | out_requeue: | 2187 | out_requeue: |
2215 | if (b43legacy_debug(dev, B43legacy_DBG_PWORK_FAST)) | 2188 | if (b43legacy_debug(dev, B43legacy_DBG_PWORK_FAST)) |
2216 | delay = msecs_to_jiffies(50); | 2189 | delay = msecs_to_jiffies(50); |
2217 | else | 2190 | else |
2218 | delay = round_jiffies_relative(HZ * 15); | 2191 | delay = round_jiffies_relative(HZ * 15); |
2219 | queue_delayed_work(dev->wl->hw->workqueue, | 2192 | queue_delayed_work(wl->hw->workqueue, &dev->periodic_work, delay); |
2220 | &dev->periodic_work, delay); | ||
2221 | out: | 2193 | out: |
2222 | mutex_unlock(&dev->wl->mutex); | 2194 | mutex_unlock(&wl->mutex); |
2223 | } | 2195 | } |
2224 | 2196 | ||
2225 | static void b43legacy_periodic_tasks_setup(struct b43legacy_wldev *dev) | 2197 | static void b43legacy_periodic_tasks_setup(struct b43legacy_wldev *dev) |