iwl-io.c (13409B)
1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2/* 3 * Copyright (C) 2003-2014, 2018-2021 Intel Corporation 4 * Copyright (C) 2015-2016 Intel Deutschland GmbH 5 */ 6#include <linux/delay.h> 7#include <linux/device.h> 8#include <linux/export.h> 9 10#include "iwl-drv.h" 11#include "iwl-io.h" 12#include "iwl-csr.h" 13#include "iwl-debug.h" 14#include "iwl-prph.h" 15#include "iwl-fh.h" 16 17void iwl_write8(struct iwl_trans *trans, u32 ofs, u8 val) 18{ 19 trace_iwlwifi_dev_iowrite8(trans->dev, ofs, val); 20 iwl_trans_write8(trans, ofs, val); 21} 22IWL_EXPORT_SYMBOL(iwl_write8); 23 24void iwl_write32(struct iwl_trans *trans, u32 ofs, u32 val) 25{ 26 trace_iwlwifi_dev_iowrite32(trans->dev, ofs, val); 27 iwl_trans_write32(trans, ofs, val); 28} 29IWL_EXPORT_SYMBOL(iwl_write32); 30 31void iwl_write64(struct iwl_trans *trans, u64 ofs, u64 val) 32{ 33 trace_iwlwifi_dev_iowrite64(trans->dev, ofs, val); 34 iwl_trans_write32(trans, ofs, lower_32_bits(val)); 35 iwl_trans_write32(trans, ofs + 4, upper_32_bits(val)); 36} 37IWL_EXPORT_SYMBOL(iwl_write64); 38 39u32 iwl_read32(struct iwl_trans *trans, u32 ofs) 40{ 41 u32 val = iwl_trans_read32(trans, ofs); 42 43 trace_iwlwifi_dev_ioread32(trans->dev, ofs, val); 44 return val; 45} 46IWL_EXPORT_SYMBOL(iwl_read32); 47 48#define IWL_POLL_INTERVAL 10 /* microseconds */ 49 50int iwl_poll_bit(struct iwl_trans *trans, u32 addr, 51 u32 bits, u32 mask, int timeout) 52{ 53 int t = 0; 54 55 do { 56 if ((iwl_read32(trans, addr) & mask) == (bits & mask)) 57 return t; 58 udelay(IWL_POLL_INTERVAL); 59 t += IWL_POLL_INTERVAL; 60 } while (t < timeout); 61 62 return -ETIMEDOUT; 63} 64IWL_EXPORT_SYMBOL(iwl_poll_bit); 65 66u32 iwl_read_direct32(struct iwl_trans *trans, u32 reg) 67{ 68 if (iwl_trans_grab_nic_access(trans)) { 69 u32 value = iwl_read32(trans, reg); 70 71 iwl_trans_release_nic_access(trans); 72 return value; 73 } 74 75 return 0x5a5a5a5a; 76} 77IWL_EXPORT_SYMBOL(iwl_read_direct32); 78 79void iwl_write_direct32(struct iwl_trans *trans, u32 reg, u32 value) 80{ 81 if (iwl_trans_grab_nic_access(trans)) { 82 iwl_write32(trans, reg, value); 83 iwl_trans_release_nic_access(trans); 84 } 85} 86IWL_EXPORT_SYMBOL(iwl_write_direct32); 87 88void iwl_write_direct64(struct iwl_trans *trans, u64 reg, u64 value) 89{ 90 if (iwl_trans_grab_nic_access(trans)) { 91 iwl_write64(trans, reg, value); 92 iwl_trans_release_nic_access(trans); 93 } 94} 95IWL_EXPORT_SYMBOL(iwl_write_direct64); 96 97int iwl_poll_direct_bit(struct iwl_trans *trans, u32 addr, u32 mask, 98 int timeout) 99{ 100 int t = 0; 101 102 do { 103 if ((iwl_read_direct32(trans, addr) & mask) == mask) 104 return t; 105 udelay(IWL_POLL_INTERVAL); 106 t += IWL_POLL_INTERVAL; 107 } while (t < timeout); 108 109 return -ETIMEDOUT; 110} 111IWL_EXPORT_SYMBOL(iwl_poll_direct_bit); 112 113u32 iwl_read_prph_no_grab(struct iwl_trans *trans, u32 ofs) 114{ 115 u32 val = iwl_trans_read_prph(trans, ofs); 116 trace_iwlwifi_dev_ioread_prph32(trans->dev, ofs, val); 117 return val; 118} 119IWL_EXPORT_SYMBOL(iwl_read_prph_no_grab); 120 121void iwl_write_prph_no_grab(struct iwl_trans *trans, u32 ofs, u32 val) 122{ 123 trace_iwlwifi_dev_iowrite_prph32(trans->dev, ofs, val); 124 iwl_trans_write_prph(trans, ofs, val); 125} 126IWL_EXPORT_SYMBOL(iwl_write_prph_no_grab); 127 128void iwl_write_prph64_no_grab(struct iwl_trans *trans, u64 ofs, u64 val) 129{ 130 trace_iwlwifi_dev_iowrite_prph64(trans->dev, ofs, val); 131 iwl_write_prph_no_grab(trans, ofs, val & 0xffffffff); 132 iwl_write_prph_no_grab(trans, ofs + 4, val >> 32); 133} 134IWL_EXPORT_SYMBOL(iwl_write_prph64_no_grab); 135 136u32 iwl_read_prph(struct iwl_trans *trans, u32 ofs) 137{ 138 if (iwl_trans_grab_nic_access(trans)) { 139 u32 val = iwl_read_prph_no_grab(trans, ofs); 140 141 iwl_trans_release_nic_access(trans); 142 143 return val; 144 } 145 146 return 0x5a5a5a5a; 147} 148IWL_EXPORT_SYMBOL(iwl_read_prph); 149 150void iwl_write_prph_delay(struct iwl_trans *trans, u32 ofs, u32 val, u32 delay_ms) 151{ 152 if (iwl_trans_grab_nic_access(trans)) { 153 mdelay(delay_ms); 154 iwl_write_prph_no_grab(trans, ofs, val); 155 iwl_trans_release_nic_access(trans); 156 } 157} 158IWL_EXPORT_SYMBOL(iwl_write_prph_delay); 159 160int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr, 161 u32 bits, u32 mask, int timeout) 162{ 163 int t = 0; 164 165 do { 166 if ((iwl_read_prph(trans, addr) & mask) == (bits & mask)) 167 return t; 168 udelay(IWL_POLL_INTERVAL); 169 t += IWL_POLL_INTERVAL; 170 } while (t < timeout); 171 172 return -ETIMEDOUT; 173} 174 175void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask) 176{ 177 if (iwl_trans_grab_nic_access(trans)) { 178 iwl_write_prph_no_grab(trans, ofs, 179 iwl_read_prph_no_grab(trans, ofs) | 180 mask); 181 iwl_trans_release_nic_access(trans); 182 } 183} 184IWL_EXPORT_SYMBOL(iwl_set_bits_prph); 185 186void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs, 187 u32 bits, u32 mask) 188{ 189 if (iwl_trans_grab_nic_access(trans)) { 190 iwl_write_prph_no_grab(trans, ofs, 191 (iwl_read_prph_no_grab(trans, ofs) & 192 mask) | bits); 193 iwl_trans_release_nic_access(trans); 194 } 195} 196IWL_EXPORT_SYMBOL(iwl_set_bits_mask_prph); 197 198void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask) 199{ 200 u32 val; 201 202 if (iwl_trans_grab_nic_access(trans)) { 203 val = iwl_read_prph_no_grab(trans, ofs); 204 iwl_write_prph_no_grab(trans, ofs, (val & ~mask)); 205 iwl_trans_release_nic_access(trans); 206 } 207} 208IWL_EXPORT_SYMBOL(iwl_clear_bits_prph); 209 210void iwl_force_nmi(struct iwl_trans *trans) 211{ 212 if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_9000) 213 iwl_write_prph_delay(trans, DEVICE_SET_NMI_REG, 214 DEVICE_SET_NMI_VAL_DRV, 1); 215 else if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) 216 iwl_write_umac_prph(trans, UREG_NIC_SET_NMI_DRIVER, 217 UREG_NIC_SET_NMI_DRIVER_NMI_FROM_DRIVER); 218 else if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_BZ) 219 iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6, 220 UREG_DOORBELL_TO_ISR6_NMI_BIT); 221 else 222 iwl_write32(trans, CSR_DOORBELL_VECTOR, 223 UREG_DOORBELL_TO_ISR6_NMI_BIT); 224} 225IWL_EXPORT_SYMBOL(iwl_force_nmi); 226 227static const char *get_rfh_string(int cmd) 228{ 229#define IWL_CMD(x) case x: return #x 230#define IWL_CMD_MQ(arg, reg, q) { if (arg == reg(q)) return #reg; } 231 232 int i; 233 234 for (i = 0; i < IWL_MAX_RX_HW_QUEUES; i++) { 235 IWL_CMD_MQ(cmd, RFH_Q_FRBDCB_BA_LSB, i); 236 IWL_CMD_MQ(cmd, RFH_Q_FRBDCB_WIDX, i); 237 IWL_CMD_MQ(cmd, RFH_Q_FRBDCB_RIDX, i); 238 IWL_CMD_MQ(cmd, RFH_Q_URBD_STTS_WPTR_LSB, i); 239 } 240 241 switch (cmd) { 242 IWL_CMD(RFH_RXF_DMA_CFG); 243 IWL_CMD(RFH_GEN_CFG); 244 IWL_CMD(RFH_GEN_STATUS); 245 IWL_CMD(FH_TSSR_TX_STATUS_REG); 246 IWL_CMD(FH_TSSR_TX_ERROR_REG); 247 default: 248 return "UNKNOWN"; 249 } 250#undef IWL_CMD_MQ 251} 252 253struct reg { 254 u32 addr; 255 bool is64; 256}; 257 258static int iwl_dump_rfh(struct iwl_trans *trans, char **buf) 259{ 260 int i, q; 261 int num_q = trans->num_rx_queues; 262 static const u32 rfh_tbl[] = { 263 RFH_RXF_DMA_CFG, 264 RFH_GEN_CFG, 265 RFH_GEN_STATUS, 266 FH_TSSR_TX_STATUS_REG, 267 FH_TSSR_TX_ERROR_REG, 268 }; 269 static const struct reg rfh_mq_tbl[] = { 270 { RFH_Q0_FRBDCB_BA_LSB, true }, 271 { RFH_Q0_FRBDCB_WIDX, false }, 272 { RFH_Q0_FRBDCB_RIDX, false }, 273 { RFH_Q0_URBD_STTS_WPTR_LSB, true }, 274 }; 275 276#ifdef CONFIG_IWLWIFI_DEBUGFS 277 if (buf) { 278 int pos = 0; 279 /* 280 * Register (up to 34 for name + 8 blank/q for MQ): 40 chars 281 * Colon + space: 2 characters 282 * 0X%08x: 10 characters 283 * New line: 1 character 284 * Total of 53 characters 285 */ 286 size_t bufsz = ARRAY_SIZE(rfh_tbl) * 53 + 287 ARRAY_SIZE(rfh_mq_tbl) * 53 * num_q + 40; 288 289 *buf = kmalloc(bufsz, GFP_KERNEL); 290 if (!*buf) 291 return -ENOMEM; 292 293 pos += scnprintf(*buf + pos, bufsz - pos, 294 "RFH register values:\n"); 295 296 for (i = 0; i < ARRAY_SIZE(rfh_tbl); i++) 297 pos += scnprintf(*buf + pos, bufsz - pos, 298 "%40s: 0X%08x\n", 299 get_rfh_string(rfh_tbl[i]), 300 iwl_read_prph(trans, rfh_tbl[i])); 301 302 for (i = 0; i < ARRAY_SIZE(rfh_mq_tbl); i++) 303 for (q = 0; q < num_q; q++) { 304 u32 addr = rfh_mq_tbl[i].addr; 305 306 addr += q * (rfh_mq_tbl[i].is64 ? 8 : 4); 307 pos += scnprintf(*buf + pos, bufsz - pos, 308 "%34s(q %2d): 0X%08x\n", 309 get_rfh_string(addr), q, 310 iwl_read_prph(trans, addr)); 311 } 312 313 return pos; 314 } 315#endif 316 317 IWL_ERR(trans, "RFH register values:\n"); 318 for (i = 0; i < ARRAY_SIZE(rfh_tbl); i++) 319 IWL_ERR(trans, " %34s: 0X%08x\n", 320 get_rfh_string(rfh_tbl[i]), 321 iwl_read_prph(trans, rfh_tbl[i])); 322 323 for (i = 0; i < ARRAY_SIZE(rfh_mq_tbl); i++) 324 for (q = 0; q < num_q; q++) { 325 u32 addr = rfh_mq_tbl[i].addr; 326 327 addr += q * (rfh_mq_tbl[i].is64 ? 8 : 4); 328 IWL_ERR(trans, " %34s(q %d): 0X%08x\n", 329 get_rfh_string(addr), q, 330 iwl_read_prph(trans, addr)); 331 } 332 333 return 0; 334} 335 336static const char *get_fh_string(int cmd) 337{ 338 switch (cmd) { 339 IWL_CMD(FH_RSCSR_CHNL0_STTS_WPTR_REG); 340 IWL_CMD(FH_RSCSR_CHNL0_RBDCB_BASE_REG); 341 IWL_CMD(FH_RSCSR_CHNL0_WPTR); 342 IWL_CMD(FH_MEM_RCSR_CHNL0_CONFIG_REG); 343 IWL_CMD(FH_MEM_RSSR_SHARED_CTRL_REG); 344 IWL_CMD(FH_MEM_RSSR_RX_STATUS_REG); 345 IWL_CMD(FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV); 346 IWL_CMD(FH_TSSR_TX_STATUS_REG); 347 IWL_CMD(FH_TSSR_TX_ERROR_REG); 348 default: 349 return "UNKNOWN"; 350 } 351#undef IWL_CMD 352} 353 354int iwl_dump_fh(struct iwl_trans *trans, char **buf) 355{ 356 int i; 357 static const u32 fh_tbl[] = { 358 FH_RSCSR_CHNL0_STTS_WPTR_REG, 359 FH_RSCSR_CHNL0_RBDCB_BASE_REG, 360 FH_RSCSR_CHNL0_WPTR, 361 FH_MEM_RCSR_CHNL0_CONFIG_REG, 362 FH_MEM_RSSR_SHARED_CTRL_REG, 363 FH_MEM_RSSR_RX_STATUS_REG, 364 FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV, 365 FH_TSSR_TX_STATUS_REG, 366 FH_TSSR_TX_ERROR_REG 367 }; 368 369 if (trans->trans_cfg->mq_rx_supported) 370 return iwl_dump_rfh(trans, buf); 371 372#ifdef CONFIG_IWLWIFI_DEBUGFS 373 if (buf) { 374 int pos = 0; 375 size_t bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40; 376 377 *buf = kmalloc(bufsz, GFP_KERNEL); 378 if (!*buf) 379 return -ENOMEM; 380 381 pos += scnprintf(*buf + pos, bufsz - pos, 382 "FH register values:\n"); 383 384 for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) 385 pos += scnprintf(*buf + pos, bufsz - pos, 386 " %34s: 0X%08x\n", 387 get_fh_string(fh_tbl[i]), 388 iwl_read_direct32(trans, fh_tbl[i])); 389 390 return pos; 391 } 392#endif 393 394 IWL_ERR(trans, "FH register values:\n"); 395 for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) 396 IWL_ERR(trans, " %34s: 0X%08x\n", 397 get_fh_string(fh_tbl[i]), 398 iwl_read_direct32(trans, fh_tbl[i])); 399 400 return 0; 401} 402 403#define IWL_HOST_MON_BLOCK_PEMON 0x00 404#define IWL_HOST_MON_BLOCK_HIPM 0x22 405 406#define IWL_HOST_MON_BLOCK_PEMON_VEC0 0x00 407#define IWL_HOST_MON_BLOCK_PEMON_VEC1 0x01 408#define IWL_HOST_MON_BLOCK_PEMON_WFPM 0x06 409 410static void iwl_dump_host_monitor_block(struct iwl_trans *trans, 411 u32 block, u32 vec, u32 iter) 412{ 413 int i; 414 415 IWL_ERR(trans, "Host monitor block 0x%x vector 0x%x\n", block, vec); 416 iwl_write32(trans, CSR_MONITOR_CFG_REG, (block << 8) | vec); 417 for (i = 0; i < iter; i++) 418 IWL_ERR(trans, " value [iter %d]: 0x%08x\n", 419 i, iwl_read32(trans, CSR_MONITOR_STATUS_REG)); 420} 421 422static void iwl_dump_host_monitor(struct iwl_trans *trans) 423{ 424 switch (trans->trans_cfg->device_family) { 425 case IWL_DEVICE_FAMILY_22000: 426 case IWL_DEVICE_FAMILY_AX210: 427 IWL_ERR(trans, "CSR_RESET = 0x%x\n", 428 iwl_read32(trans, CSR_RESET)); 429 iwl_dump_host_monitor_block(trans, IWL_HOST_MON_BLOCK_PEMON, 430 IWL_HOST_MON_BLOCK_PEMON_VEC0, 15); 431 iwl_dump_host_monitor_block(trans, IWL_HOST_MON_BLOCK_PEMON, 432 IWL_HOST_MON_BLOCK_PEMON_VEC1, 15); 433 iwl_dump_host_monitor_block(trans, IWL_HOST_MON_BLOCK_PEMON, 434 IWL_HOST_MON_BLOCK_PEMON_WFPM, 15); 435 iwl_dump_host_monitor_block(trans, IWL_HOST_MON_BLOCK_HIPM, 436 IWL_HOST_MON_BLOCK_PEMON_VEC0, 1); 437 break; 438 default: 439 /* not supported yet */ 440 return; 441 } 442} 443 444int iwl_finish_nic_init(struct iwl_trans *trans) 445{ 446 const struct iwl_cfg_trans_params *cfg_trans = trans->trans_cfg; 447 u32 poll_ready; 448 int err; 449 450 if (cfg_trans->bisr_workaround) { 451 /* ensure the TOP FSM isn't still in previous reset */ 452 mdelay(2); 453 } 454 455 /* 456 * Set "initialization complete" bit to move adapter from 457 * D0U* --> D0A* (powered-up active) state. 458 */ 459 if (cfg_trans->device_family >= IWL_DEVICE_FAMILY_BZ) { 460 iwl_set_bit(trans, CSR_GP_CNTRL, 461 CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY | 462 CSR_GP_CNTRL_REG_FLAG_MAC_INIT); 463 poll_ready = CSR_GP_CNTRL_REG_FLAG_MAC_STATUS; 464 } else { 465 iwl_set_bit(trans, CSR_GP_CNTRL, 466 CSR_GP_CNTRL_REG_FLAG_INIT_DONE); 467 poll_ready = CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY; 468 } 469 470 if (cfg_trans->device_family == IWL_DEVICE_FAMILY_8000) 471 udelay(2); 472 473 /* 474 * Wait for clock stabilization; once stabilized, access to 475 * device-internal resources is supported, e.g. iwl_write_prph() 476 * and accesses to uCode SRAM. 477 */ 478 err = iwl_poll_bit(trans, CSR_GP_CNTRL, poll_ready, poll_ready, 25000); 479 if (err < 0) { 480 IWL_DEBUG_INFO(trans, "Failed to wake NIC\n"); 481 482 iwl_dump_host_monitor(trans); 483 } 484 485 if (cfg_trans->bisr_workaround) { 486 /* ensure BISR shift has finished */ 487 udelay(200); 488 } 489 490 return err < 0 ? err : 0; 491} 492IWL_EXPORT_SYMBOL(iwl_finish_nic_init); 493 494void iwl_trans_sync_nmi_with_addr(struct iwl_trans *trans, u32 inta_addr, 495 u32 sw_err_bit) 496{ 497 unsigned long timeout = jiffies + IWL_TRANS_NMI_TIMEOUT; 498 bool interrupts_enabled = test_bit(STATUS_INT_ENABLED, &trans->status); 499 500 /* if the interrupts were already disabled, there is no point in 501 * calling iwl_disable_interrupts 502 */ 503 if (interrupts_enabled) 504 iwl_trans_interrupts(trans, false); 505 506 iwl_force_nmi(trans); 507 while (time_after(timeout, jiffies)) { 508 u32 inta_hw = iwl_read32(trans, inta_addr); 509 510 /* Error detected by uCode */ 511 if (inta_hw & sw_err_bit) { 512 /* Clear causes register */ 513 iwl_write32(trans, inta_addr, inta_hw & sw_err_bit); 514 break; 515 } 516 517 mdelay(1); 518 } 519 520 /* enable interrupts only if there were already enabled before this 521 * function to avoid a case were the driver enable interrupts before 522 * proper configurations were made 523 */ 524 if (interrupts_enabled) 525 iwl_trans_interrupts(trans, true); 526 527 iwl_trans_fw_error(trans, false); 528}