aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2012-04-23 17:17:50 -0400
committerJohn W. Linville <linville@tuxdriver.com>2012-04-24 14:14:55 -0400
commit5ef4acd58ab2abd0dd0c8e3cacd61a0dc5d73646 (patch)
tree66f8630a6525dfec3fda774653b4dca98771b9e7 /drivers/net
parent6ead629b27269c553c9092c47cd8f5ab0309ee3b (diff)
iwlwifi: fix hardware queue programming
Newer devices have 20 (5000 series) or 30 (6000 series) hardware queues, rather than the 16 that 4965 had. This was added to the driver a long time ago, but improperly: the queue registers for the higher queues aren't just continuations of the registers for the first 16 queues, they are in other places. Therefore, the hardware would lock up when trying to activate queue 16 or above and the device would have to be restarted. Thanks goes to Emmanuel who identified this and told me how the queue programming should be done. Note that we don't use queues 20 and higher today and doing so needs more work than this. Cc: stable@vger.kernel.org Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-fh.h24
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-prph.h27
2 files changed, 43 insertions, 8 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-fh.h b/drivers/net/wireless/iwlwifi/iwl-fh.h
index 90208094b8eb..74bce97a8600 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fh.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fh.h
@@ -104,15 +104,29 @@
104 * (see struct iwl_tfd_frame). These 16 pointer registers are offset by 0x04 104 * (see struct iwl_tfd_frame). These 16 pointer registers are offset by 0x04
105 * bytes from one another. Each TFD circular buffer in DRAM must be 256-byte 105 * bytes from one another. Each TFD circular buffer in DRAM must be 256-byte
106 * aligned (address bits 0-7 must be 0). 106 * aligned (address bits 0-7 must be 0).
107 * Later devices have 20 (5000 series) or 30 (higher) queues, but the registers
108 * for them are in different places.
107 * 109 *
108 * Bit fields in each pointer register: 110 * Bit fields in each pointer register:
109 * 27-0: TFD CB physical base address [35:8], must be 256-byte aligned 111 * 27-0: TFD CB physical base address [35:8], must be 256-byte aligned
110 */ 112 */
111#define FH_MEM_CBBC_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x9D0) 113#define FH_MEM_CBBC_0_15_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x9D0)
112#define FH_MEM_CBBC_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xA10) 114#define FH_MEM_CBBC_0_15_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xA10)
113 115#define FH_MEM_CBBC_16_19_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xBF0)
114/* Find TFD CB base pointer for given queue (range 0-15). */ 116#define FH_MEM_CBBC_16_19_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xC00)
115#define FH_MEM_CBBC_QUEUE(x) (FH_MEM_CBBC_LOWER_BOUND + (x) * 0x4) 117#define FH_MEM_CBBC_20_31_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xB20)
118#define FH_MEM_CBBC_20_31_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xB80)
119
120/* Find TFD CB base pointer for given queue */
121static inline unsigned int FH_MEM_CBBC_QUEUE(unsigned int chnl)
122{
123 if (chnl < 16)
124 return FH_MEM_CBBC_0_15_LOWER_BOUND + 4 * chnl;
125 if (chnl < 20)
126 return FH_MEM_CBBC_16_19_LOWER_BOUND + 4 * (chnl - 16);
127 WARN_ON_ONCE(chnl >= 32);
128 return FH_MEM_CBBC_20_31_LOWER_BOUND + 4 * (chnl - 20);
129}
116 130
117 131
118/** 132/**
diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h
index 75dc20bd965b..3b1069290fa9 100644
--- a/drivers/net/wireless/iwlwifi/iwl-prph.h
+++ b/drivers/net/wireless/iwlwifi/iwl-prph.h
@@ -223,12 +223,33 @@
223#define SCD_AIT (SCD_BASE + 0x0c) 223#define SCD_AIT (SCD_BASE + 0x0c)
224#define SCD_TXFACT (SCD_BASE + 0x10) 224#define SCD_TXFACT (SCD_BASE + 0x10)
225#define SCD_ACTIVE (SCD_BASE + 0x14) 225#define SCD_ACTIVE (SCD_BASE + 0x14)
226#define SCD_QUEUE_WRPTR(x) (SCD_BASE + 0x18 + (x) * 4)
227#define SCD_QUEUE_RDPTR(x) (SCD_BASE + 0x68 + (x) * 4)
228#define SCD_QUEUECHAIN_SEL (SCD_BASE + 0xe8) 226#define SCD_QUEUECHAIN_SEL (SCD_BASE + 0xe8)
229#define SCD_AGGR_SEL (SCD_BASE + 0x248) 227#define SCD_AGGR_SEL (SCD_BASE + 0x248)
230#define SCD_INTERRUPT_MASK (SCD_BASE + 0x108) 228#define SCD_INTERRUPT_MASK (SCD_BASE + 0x108)
231#define SCD_QUEUE_STATUS_BITS(x) (SCD_BASE + 0x10c + (x) * 4) 229
230static inline unsigned int SCD_QUEUE_WRPTR(unsigned int chnl)
231{
232 if (chnl < 20)
233 return SCD_BASE + 0x18 + chnl * 4;
234 WARN_ON_ONCE(chnl >= 32);
235 return SCD_BASE + 0x284 + (chnl - 20) * 4;
236}
237
238static inline unsigned int SCD_QUEUE_RDPTR(unsigned int chnl)
239{
240 if (chnl < 20)
241 return SCD_BASE + 0x68 + chnl * 4;
242 WARN_ON_ONCE(chnl >= 32);
243 return SCD_BASE + 0x2B4 + (chnl - 20) * 4;
244}
245
246static inline unsigned int SCD_QUEUE_STATUS_BITS(unsigned int chnl)
247{
248 if (chnl < 20)
249 return SCD_BASE + 0x10c + chnl * 4;
250 WARN_ON_ONCE(chnl >= 32);
251 return SCD_BASE + 0x384 + (chnl - 20) * 4;
252}
232 253
233/*********************** END TX SCHEDULER *************************************/ 254/*********************** END TX SCHEDULER *************************************/
234 255