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