1/****************************************************************************** 2 * 3 * GPL LICENSE SUMMARY 4 * 5 * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved. 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of version 2 of the GNU General Public License as 9 * published by the Free Software Foundation. 10 * 11 * This program is distributed in the hope that it will be useful, but 12 * WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, 19 * USA 20 * 21 * The full GNU General Public License is included in this distribution 22 * in the file called LICENSE.GPL. 23 * 24 * Contact Information: 25 * Intel Linux Wireless <ilw@linux.intel.com> 26 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 27 *****************************************************************************/ 28 29#include <linux/kernel.h> 30#include <linux/module.h> 31#include <linux/etherdevice.h> 32#include <linux/sched.h> 33#include <linux/gfp.h> 34#include <net/mac80211.h> 35 36#include "iwl-dev.h" 37#include "iwl-core.h" 38#include "iwl-agn.h" 39#include "iwl-helpers.h" 40 41#define ICT_COUNT (PAGE_SIZE/sizeof(u32)) 42 43/* Free dram table */ 44void iwl_free_isr_ict(struct iwl_priv *priv) 45{ 46 if (priv->_agn.ict_tbl_vir) { 47 dma_free_coherent(&priv->pci_dev->dev, 48 (sizeof(u32) * ICT_COUNT) + PAGE_SIZE, 49 priv->_agn.ict_tbl_vir, 50 priv->_agn.ict_tbl_dma); 51 priv->_agn.ict_tbl_vir = NULL; 52 } 53} 54 55 56/* allocate dram shared table it is a PAGE_SIZE aligned 57 * also reset all data related to ICT table interrupt. 58 */ 59int iwl_alloc_isr_ict(struct iwl_priv *priv) 60{ 61 62 if (priv->cfg->use_isr_legacy) 63 return 0; 64 /* allocate shrared data table */ 65 priv->_agn.ict_tbl_vir = 66 dma_alloc_coherent(&priv->pci_dev->dev, 67 (sizeof(u32) * ICT_COUNT) + PAGE_SIZE, 68 &priv->_agn.ict_tbl_dma, GFP_KERNEL); 69 if (!priv->_agn.ict_tbl_vir) 70 return -ENOMEM; 71 72 /* align table to PAGE_SIZE boundry */ 73 priv->_agn.aligned_ict_tbl_dma = ALIGN(priv->_agn.ict_tbl_dma, PAGE_SIZE); 74 75 IWL_DEBUG_ISR(priv, "ict dma addr %Lx dma aligned %Lx diff %d\n", 76 (unsigned long long)priv->_agn.ict_tbl_dma, 77 (unsigned long long)priv->_agn.aligned_ict_tbl_dma, 78 (int)(priv->_agn.aligned_ict_tbl_dma - priv->_agn.ict_tbl_dma)); 79 80 priv->_agn.ict_tbl = priv->_agn.ict_tbl_vir + 81 (priv->_agn.aligned_ict_tbl_dma - priv->_agn.ict_tbl_dma); 82 83 IWL_DEBUG_ISR(priv, "ict vir addr %p vir aligned %p diff %d\n", 84 priv->_agn.ict_tbl, priv->_agn.ict_tbl_vir, 85 (int)(priv->_agn.aligned_ict_tbl_dma - priv->_agn.ict_tbl_dma)); 86 87 /* reset table and index to all 0 */ 88 memset(priv->_agn.ict_tbl_vir,0, (sizeof(u32) * ICT_COUNT) + PAGE_SIZE); 89 priv->_agn.ict_index = 0; 90 91 /* add periodic RX interrupt */ 92 priv->inta_mask |= CSR_INT_BIT_RX_PERIODIC; 93 return 0; 94} 95 96/* Device is going up inform it about using ICT interrupt table, 97 * also we need to tell the driver to start using ICT interrupt. 98 */ 99int iwl_reset_ict(struct iwl_priv *priv) 100{ 101 u32 val; 102 unsigned long flags; 103 104 if (!priv->_agn.ict_tbl_vir) 105 return 0; 106 107 spin_lock_irqsave(&priv->lock, flags); 108 iwl_disable_interrupts(priv); 109 110 memset(&priv->_agn.ict_tbl[0], 0, sizeof(u32) * ICT_COUNT); 111 112 val = priv->_agn.aligned_ict_tbl_dma >> PAGE_SHIFT; 113 114 val |= CSR_DRAM_INT_TBL_ENABLE; 115 val |= CSR_DRAM_INIT_TBL_WRAP_CHECK; 116 117 IWL_DEBUG_ISR(priv, "CSR_DRAM_INT_TBL_REG =0x%X " 118 "aligned dma address %Lx\n", 119 val, (unsigned long long)priv->_agn.aligned_ict_tbl_dma); 120 121 iwl_write32(priv, CSR_DRAM_INT_TBL_REG, val); 122 priv->_agn.use_ict = true; 123 priv->_agn.ict_index = 0; 124 iwl_write32(priv, CSR_INT, priv->inta_mask); 125 iwl_enable_interrupts(priv); 126 spin_unlock_irqrestore(&priv->lock, flags); 127 128 return 0; 129} 130 131/* Device is going down disable ict interrupt usage */ 132void iwl_disable_ict(struct iwl_priv *priv) 133{ 134 unsigned long flags; 135 136 spin_lock_irqsave(&priv->lock, flags); 137 priv->_agn.use_ict = false; 138 spin_unlock_irqrestore(&priv->lock, flags); 139} 140 141static irqreturn_t iwl_isr(int irq, void *data) 142{ 143 struct iwl_priv *priv = data; 144 u32 inta, inta_mask; 145 unsigned long flags; 146#ifdef CONFIG_IWLWIFI_DEBUG 147 u32 inta_fh; 148#endif 149 if (!priv) 150 return IRQ_NONE; 151 152 spin_lock_irqsave(&priv->lock, flags); 153 154 /* Disable (but don't clear!) interrupts here to avoid 155 * back-to-back ISRs and sporadic interrupts from our NIC. 156 * If we have something to service, the tasklet will re-enable ints. 157 * If we *don't* have something, we'll re-enable before leaving here. */ 158 inta_mask = iwl_read32(priv, CSR_INT_MASK); /* just for debug */ 159 iwl_write32(priv, CSR_INT_MASK, 0x00000000); 160 161 /* Discover which interrupts are active/pending */ 162 inta = iwl_read32(priv, CSR_INT); 163 164 /* Ignore interrupt if there's nothing in NIC to service. 165 * This may be due to IRQ shared with another device, 166 * or due to sporadic interrupts thrown from our NIC. */ 167 if (!inta) { 168 IWL_DEBUG_ISR(priv, "Ignore interrupt, inta == 0\n"); 169 goto none; 170 } 171 172 if ((inta == 0xFFFFFFFF) || ((inta & 0xFFFFFFF0) == 0xa5a5a5a0)) { 173 /* Hardware disappeared. It might have already raised 174 * an interrupt */ 175 IWL_WARN(priv, "HARDWARE GONE?? INTA == 0x%08x\n", inta); 176 goto unplugged; 177 } 178 179#ifdef CONFIG_IWLWIFI_DEBUG 180 if (iwl_get_debug_level(priv) & (IWL_DL_ISR)) { 181 inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS); 182 IWL_DEBUG_ISR(priv, "ISR inta 0x%08x, enabled 0x%08x, " 183 "fh 0x%08x\n", inta, inta_mask, inta_fh); 184 } 185#endif 186 187 priv->_agn.inta |= inta; 188 /* iwl_irq_tasklet() will service interrupts and re-enable them */ 189 if (likely(inta)) 190 tasklet_schedule(&priv->irq_tasklet); 191 else if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->_agn.inta) 192 iwl_enable_interrupts(priv); 193 194 unplugged: 195 spin_unlock_irqrestore(&priv->lock, flags); 196 return IRQ_HANDLED; 197 198 none: 199 /* re-enable interrupts here since we don't have anything to service. */ 200 /* only Re-enable if diabled by irq and no schedules tasklet. */ 201 if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->_agn.inta) 202 iwl_enable_interrupts(priv); 203 204 spin_unlock_irqrestore(&priv->lock, flags); 205 return IRQ_NONE; 206} 207 208/* interrupt handler using ict table, with this interrupt driver will 209 * stop using INTA register to get device's interrupt, reading this register 210 * is expensive, device will write interrupts in ICT dram table, increment 211 * index then will fire interrupt to driver, driver will OR all ICT table 212 * entries from current index up to table entry with 0 value. the result is 213 * the interrupt we need to service, driver will set the entries back to 0 and 214 * set index. 215 */ 216irqreturn_t iwl_isr_ict(int irq, void *data) 217{ 218 struct iwl_priv *priv = data; 219 u32 inta, inta_mask; 220 u32 val = 0; 221 unsigned long flags; 222 223 if (!priv) 224 return IRQ_NONE; 225 226 /* dram interrupt table not set yet, 227 * use legacy interrupt. 228 */ 229 if (!priv->_agn.use_ict) 230 return iwl_isr(irq, data); 231 232 spin_lock_irqsave(&priv->lock, flags); 233 234 /* Disable (but don't clear!) interrupts here to avoid 235 * back-to-back ISRs and sporadic interrupts from our NIC. 236 * If we have something to service, the tasklet will re-enable ints. 237 * If we *don't* have something, we'll re-enable before leaving here. 238 */ 239 inta_mask = iwl_read32(priv, CSR_INT_MASK); /* just for debug */ 240 iwl_write32(priv, CSR_INT_MASK, 0x00000000); 241 242 243 /* Ignore interrupt if there's nothing in NIC to service. 244 * This may be due to IRQ shared with another device, 245 * or due to sporadic interrupts thrown from our NIC. */ 246 if (!priv->_agn.ict_tbl[priv->_agn.ict_index]) { 247 IWL_DEBUG_ISR(priv, "Ignore interrupt, inta == 0\n"); 248 goto none; 249 } 250 251 /* read all entries that not 0 start with ict_index */ 252 while (priv->_agn.ict_tbl[priv->_agn.ict_index]) { 253 254 val |= le32_to_cpu(priv->_agn.ict_tbl[priv->_agn.ict_index]); 255 IWL_DEBUG_ISR(priv, "ICT index %d value 0x%08X\n", 256 priv->_agn.ict_index, 257 le32_to_cpu(priv->_agn.ict_tbl[priv->_agn.ict_index])); 258 priv->_agn.ict_tbl[priv->_agn.ict_index] = 0; 259 priv->_agn.ict_index = iwl_queue_inc_wrap(priv->_agn.ict_index, 260 ICT_COUNT); 261 262 } 263 264 /* We should not get this value, just ignore it. */ 265 if (val == 0xffffffff) 266 val = 0; 267 268 /* 269 * this is a w/a for a h/w bug. the h/w bug may cause the Rx bit 270 * (bit 15 before shifting it to 31) to clear when using interrupt 271 * coalescing. fortunately, bits 18 and 19 stay set when this happens 272 * so we use them to decide on the real state of the Rx bit. 273 * In order words, bit 15 is set if bit 18 or bit 19 are set. 274 */ 275 if (val & 0xC0000) 276 val |= 0x8000; 277 278 inta = (0xff & val) | ((0xff00 & val) << 16); 279 IWL_DEBUG_ISR(priv, "ISR inta 0x%08x, enabled 0x%08x ict 0x%08x\n", 280 inta, inta_mask, val); 281 282 inta &= priv->inta_mask; 283 priv->_agn.inta |= inta; 284 285 /* iwl_irq_tasklet() will service interrupts and re-enable them */ 286 if (likely(inta)) 287 tasklet_schedule(&priv->irq_tasklet); 288 else if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->_agn.inta) { 289 /* Allow interrupt if was disabled by this handler and 290 * no tasklet was schedules, We should not enable interrupt, 291 * tasklet will enable it. 292 */ 293 iwl_enable_interrupts(priv); 294 } 295 296 spin_unlock_irqrestore(&priv->lock, flags); 297 return IRQ_HANDLED; 298 299 none: 300 /* re-enable interrupts here since we don't have anything to service. 301 * only Re-enable if disabled by irq. 302 */ 303 if (test_bit(STATUS_INT_ENABLED, &priv->status) && !priv->_agn.inta) 304 iwl_enable_interrupts(priv); 305 306 spin_unlock_irqrestore(&priv->lock, flags); 307 return IRQ_NONE; 308} 309