1/****************************************************************************** 2 * 3 * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. 4 * 5 * Portions of this file are derived from the ipw3945 project. 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * 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 WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14 * more details. 15 * 16 * You should have received a copy of the GNU General Public License along with 17 * this program; if not, write to the Free Software Foundation, Inc., 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA 19 * 20 * The full GNU General Public License is included in this distribution in the 21 * file called LICENSE. 22 * 23 * Contact Information: 24 * Intel Linux Wireless <ilw@linux.intel.com> 25 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 26 * 27 *****************************************************************************/ 28#include <linux/delay.h> 29#include <linux/device.h> 30#include <linux/export.h> 31 32#include "iwl-drv.h" 33#include "iwl-io.h" 34#include "iwl-csr.h" 35#include "iwl-debug.h" 36#include "iwl-prph.h" 37#include "iwl-fh.h" 38 39#define IWL_POLL_INTERVAL 10 /* microseconds */ 40 41int iwl_poll_bit(struct iwl_trans *trans, u32 addr, 42 u32 bits, u32 mask, int timeout) 43{ 44 int t = 0; 45 46 do { 47 if ((iwl_read32(trans, addr) & mask) == (bits & mask)) 48 return t; 49 udelay(IWL_POLL_INTERVAL); 50 t += IWL_POLL_INTERVAL; 51 } while (t < timeout); 52 53 return -ETIMEDOUT; 54} 55IWL_EXPORT_SYMBOL(iwl_poll_bit); 56 57u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg) 58{ 59 u32 value = 0x5a5a5a5a; 60 unsigned long flags; 61 if (iwl_trans_grab_nic_access(trans, false, &flags)) { 62 value = iwl_read32(trans, reg); 63 iwl_trans_release_nic_access(trans, &flags); 64 } 65 66 return value; 67} 68IWL_EXPORT_SYMBOL(iwl_read_direct32); 69 70void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value) 71{ 72 unsigned long flags; 73 74 if (iwl_trans_grab_nic_access(trans, false, &flags)) { 75 iwl_write32(trans, reg, value); 76 iwl_trans_release_nic_access(trans, &flags); 77 } 78} 79IWL_EXPORT_SYMBOL(iwl_write_direct32); 80 81int iwl_poll_direct_bit(struct iwl_trans *trans, u32 addr, u32 mask, 82 int timeout) 83{ 84 int t = 0; 85 86 do { 87 if ((iwl_read_direct32(trans, addr) & mask) == mask) 88 return t; 89 udelay(IWL_POLL_INTERVAL); 90 t += IWL_POLL_INTERVAL; 91 } while (t < timeout); 92 93 return -ETIMEDOUT; 94} 95IWL_EXPORT_SYMBOL(iwl_poll_direct_bit); 96 97u32 __iwl_read_prph(struct iwl_trans *trans, u32 ofs) 98{ 99 u32 val = iwl_trans_read_prph(trans, ofs); 100 trace_iwlwifi_dev_ioread_prph32(trans->dev, ofs, val); 101 return val; 102} 103 104void __iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val) 105{ 106 trace_iwlwifi_dev_iowrite_prph32(trans->dev, ofs, val); 107 iwl_trans_write_prph(trans, ofs, val); 108} 109 110u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs) 111{ 112 unsigned long flags; 113 u32 val = 0x5a5a5a5a; 114 115 if (iwl_trans_grab_nic_access(trans, false, &flags)) { 116 val = __iwl_read_prph(trans, ofs); 117 iwl_trans_release_nic_access(trans, &flags); 118 } 119 return val; 120} 121IWL_EXPORT_SYMBOL(iwl_read_prph); 122 123void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val) 124{ 125 unsigned long flags; 126 127 if (iwl_trans_grab_nic_access(trans, false, &flags)) { 128 __iwl_write_prph(trans, ofs, val); 129 iwl_trans_release_nic_access(trans, &flags); 130 } 131} 132IWL_EXPORT_SYMBOL(iwl_write_prph); 133 134int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr, 135 u32 bits, u32 mask, int timeout) 136{ 137 int t = 0; 138 139 do { 140 if ((iwl_read_prph(trans, addr) & mask) == (bits & mask)) 141 return t; 142 udelay(IWL_POLL_INTERVAL); 143 t += IWL_POLL_INTERVAL; 144 } while (t < timeout); 145 146 return -ETIMEDOUT; 147} 148 149void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask) 150{ 151 unsigned long flags; 152 153 if (iwl_trans_grab_nic_access(trans, false, &flags)) { 154 __iwl_write_prph(trans, ofs, 155 __iwl_read_prph(trans, ofs) | mask); 156 iwl_trans_release_nic_access(trans, &flags); 157 } 158} 159IWL_EXPORT_SYMBOL(iwl_set_bits_prph); 160 161void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs, 162 u32 bits, u32 mask) 163{ 164 unsigned long flags; 165 166 if (iwl_trans_grab_nic_access(trans, false, &flags)) { 167 __iwl_write_prph(trans, ofs, 168 (__iwl_read_prph(trans, ofs) & mask) | bits); 169 iwl_trans_release_nic_access(trans, &flags); 170 } 171} 172IWL_EXPORT_SYMBOL(iwl_set_bits_mask_prph); 173 174void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask) 175{ 176 unsigned long flags; 177 u32 val; 178 179 if (iwl_trans_grab_nic_access(trans, false, &flags)) { 180 val = __iwl_read_prph(trans, ofs); 181 __iwl_write_prph(trans, ofs, (val & ~mask)); 182 iwl_trans_release_nic_access(trans, &flags); 183 } 184} 185IWL_EXPORT_SYMBOL(iwl_clear_bits_prph); 186 187void iwl_force_nmi(struct iwl_trans *trans) 188{ 189 if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) { 190 iwl_write_prph(trans, DEVICE_SET_NMI_REG, 191 DEVICE_SET_NMI_VAL_DRV); 192 iwl_write_prph(trans, DEVICE_SET_NMI_REG, 193 DEVICE_SET_NMI_VAL_HW); 194 } else { 195 iwl_write_prph(trans, DEVICE_SET_NMI_8000_REG, 196 DEVICE_SET_NMI_8000_VAL); 197 iwl_write_prph(trans, DEVICE_SET_NMI_REG, 198 DEVICE_SET_NMI_VAL_DRV); 199 } 200} 201IWL_EXPORT_SYMBOL(iwl_force_nmi); 202 203static const char *get_fh_string(int cmd) 204{ 205#define IWL_CMD(x) case x: return #x 206 switch (cmd) { 207 IWL_CMD(FH_RSCSR_CHNL0_STTS_WPTR_REG); 208 IWL_CMD(FH_RSCSR_CHNL0_RBDCB_BASE_REG); 209 IWL_CMD(FH_RSCSR_CHNL0_WPTR); 210 IWL_CMD(FH_MEM_RCSR_CHNL0_CONFIG_REG); 211 IWL_CMD(FH_MEM_RSSR_SHARED_CTRL_REG); 212 IWL_CMD(FH_MEM_RSSR_RX_STATUS_REG); 213 IWL_CMD(FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV); 214 IWL_CMD(FH_TSSR_TX_STATUS_REG); 215 IWL_CMD(FH_TSSR_TX_ERROR_REG); 216 default: 217 return "UNKNOWN"; 218 } 219#undef IWL_CMD 220} 221 222int iwl_dump_fh(struct iwl_trans *trans, char **buf) 223{ 224 int i; 225 static const u32 fh_tbl[] = { 226 FH_RSCSR_CHNL0_STTS_WPTR_REG, 227 FH_RSCSR_CHNL0_RBDCB_BASE_REG, 228 FH_RSCSR_CHNL0_WPTR, 229 FH_MEM_RCSR_CHNL0_CONFIG_REG, 230 FH_MEM_RSSR_SHARED_CTRL_REG, 231 FH_MEM_RSSR_RX_STATUS_REG, 232 FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV, 233 FH_TSSR_TX_STATUS_REG, 234 FH_TSSR_TX_ERROR_REG 235 }; 236 237#ifdef CONFIG_IWLWIFI_DEBUGFS 238 if (buf) { 239 int pos = 0; 240 size_t bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40; 241 242 *buf = kmalloc(bufsz, GFP_KERNEL); 243 if (!*buf) 244 return -ENOMEM; 245 246 pos += scnprintf(*buf + pos, bufsz - pos, 247 "FH register values:\n"); 248 249 for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) 250 pos += scnprintf(*buf + pos, bufsz - pos, 251 " %34s: 0X%08x\n", 252 get_fh_string(fh_tbl[i]), 253 iwl_read_direct32(trans, fh_tbl[i])); 254 255 return pos; 256 } 257#endif 258 259 IWL_ERR(trans, "FH register values:\n"); 260 for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) 261 IWL_ERR(trans, " %34s: 0X%08x\n", 262 get_fh_string(fh_tbl[i]), 263 iwl_read_direct32(trans, fh_tbl[i])); 264 265 return 0; 266} 267