diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/pcie/trans.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/pcie/trans.c | 410 |
1 files changed, 298 insertions, 112 deletions
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index f9507807b486..dcfd6d866d09 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -75,6 +75,20 @@ #include "iwl-agn-hw.h" #include "internal.h" +static u32 iwl_trans_pcie_read_shr(struct iwl_trans *trans, u32 reg) +{ + iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_CTRL_REG, + ((reg & 0x0000ffff) | (2 << 28))); + return iwl_read32(trans, HEEP_CTRL_WRD_PCIEX_DATA_REG); +} + +static void iwl_trans_pcie_write_shr(struct iwl_trans *trans, u32 reg, u32 val) +{ + iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_DATA_REG, val); + iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_CTRL_REG, + ((reg & 0x0000ffff) | (3 << 28))); +} + static void iwl_pcie_set_pwr(struct iwl_trans *trans, bool vaux) { if (vaux && pci_pme_capable(to_pci_dev(trans->dev), PCI_D3cold)) @@ -89,6 +103,7 @@ static void iwl_pcie_set_pwr(struct iwl_trans *trans, bool vaux) /* PCI registers */ #define PCI_CFG_RETRY_TIMEOUT 0x041 +#define CPU1_CPU2_SEPARATOR_SECTION 0xFFFFCCCC static void iwl_pcie_apm_config(struct iwl_trans *trans) { @@ -132,8 +147,9 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans) */ /* Disable L0S exit timer (platform NMI Work/Around) */ - iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS, - CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); + if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) + iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS, + CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); /* * Disable L0s without affecting L1; @@ -203,19 +219,23 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans) /* * Enable DMA clock and wait for it to stabilize. * - * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0" bits - * do not disable clocks. This preserves any hardware bits already - * set by default in "CLK_CTRL_REG" after reset. + * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0" + * bits do not disable clocks. This preserves any hardware + * bits already set by default in "CLK_CTRL_REG" after reset. */ - iwl_write_prph(trans, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT); - udelay(20); + if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) { + iwl_write_prph(trans, APMG_CLK_EN_REG, + APMG_CLK_VAL_DMA_CLK_RQT); + udelay(20); - /* Disable L1-Active */ - iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG, - APMG_PCIDEV_STT_VAL_L1_ACT_DIS); + /* Disable L1-Active */ + iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG, + APMG_PCIDEV_STT_VAL_L1_ACT_DIS); - /* Clear the interrupt in APMG if the NIC is in RFKILL */ - iwl_write_prph(trans, APMG_RTC_INT_STT_REG, APMG_RTC_INT_STT_RFKILL); + /* Clear the interrupt in APMG if the NIC is in RFKILL */ + iwl_write_prph(trans, APMG_RTC_INT_STT_REG, + APMG_RTC_INT_STT_RFKILL); + } set_bit(STATUS_DEVICE_ENABLED, &trans->status); @@ -223,6 +243,116 @@ out: return ret; } +/* + * Enable LP XTAL to avoid HW bug where device may consume much power if + * FW is not loaded after device reset. LP XTAL is disabled by default + * after device HW reset. Do it only if XTAL is fed by internal source. + * Configure device's "persistence" mode to avoid resetting XTAL again when + * SHRD_HW_RST occurs in S3. + */ +static void iwl_pcie_apm_lp_xtal_enable(struct iwl_trans *trans) +{ + int ret; + u32 apmg_gp1_reg; + u32 apmg_xtal_cfg_reg; + u32 dl_cfg_reg; + + /* Force XTAL ON */ + __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_XTAL_ON); + + /* Reset entire device - do controller reset (results in SHRD_HW_RST) */ + iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); + + udelay(10); + + /* + * Set "initialization complete" bit to move adapter from + * D0U* --> D0A* (powered-up active) state. + */ + iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + + /* + * Wait for clock stabilization; once stabilized, access to + * device-internal resources is possible. + */ + ret = iwl_poll_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + 25000); + if (WARN_ON(ret < 0)) { + IWL_ERR(trans, "Access time out - failed to enable LP XTAL\n"); + /* Release XTAL ON request */ + __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_XTAL_ON); + return; + } + + /* + * Clear "disable persistence" to avoid LP XTAL resetting when + * SHRD_HW_RST is applied in S3. + */ + iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG, + APMG_PCIDEV_STT_VAL_PERSIST_DIS); + + /* + * Force APMG XTAL to be active to prevent its disabling by HW + * caused by APMG idle state. + */ + apmg_xtal_cfg_reg = iwl_trans_pcie_read_shr(trans, + SHR_APMG_XTAL_CFG_REG); + iwl_trans_pcie_write_shr(trans, SHR_APMG_XTAL_CFG_REG, + apmg_xtal_cfg_reg | + SHR_APMG_XTAL_CFG_XTAL_ON_REQ); + + /* + * Reset entire device again - do controller reset (results in + * SHRD_HW_RST). Turn MAC off before proceeding. + */ + iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); + + udelay(10); + + /* Enable LP XTAL by indirect access through CSR */ + apmg_gp1_reg = iwl_trans_pcie_read_shr(trans, SHR_APMG_GP1_REG); + iwl_trans_pcie_write_shr(trans, SHR_APMG_GP1_REG, apmg_gp1_reg | + SHR_APMG_GP1_WF_XTAL_LP_EN | + SHR_APMG_GP1_CHICKEN_BIT_SELECT); + + /* Clear delay line clock power up */ + dl_cfg_reg = iwl_trans_pcie_read_shr(trans, SHR_APMG_DL_CFG_REG); + iwl_trans_pcie_write_shr(trans, SHR_APMG_DL_CFG_REG, dl_cfg_reg & + ~SHR_APMG_DL_CFG_DL_CLOCK_POWER_UP); + + /* + * Enable persistence mode to avoid LP XTAL resetting when + * SHRD_HW_RST is applied in S3. + */ + iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_PERSIST_MODE); + + /* + * Clear "initialization complete" bit to move adapter from + * D0A* (powered-up Active) --> D0U* (Uninitialized) state. + */ + iwl_clear_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + + /* Activates XTAL resources monitor */ + __iwl_trans_pcie_set_bit(trans, CSR_MONITOR_CFG_REG, + CSR_MONITOR_XTAL_RESOURCES); + + /* Release XTAL ON request */ + __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_XTAL_ON); + udelay(10); + + /* Release APMG XTAL */ + iwl_trans_pcie_write_shr(trans, SHR_APMG_XTAL_CFG_REG, + apmg_xtal_cfg_reg & + ~SHR_APMG_XTAL_CFG_XTAL_ON_REQ); +} + static int iwl_pcie_apm_stop_master(struct iwl_trans *trans) { int ret = 0; @@ -250,6 +380,11 @@ static void iwl_pcie_apm_stop(struct iwl_trans *trans) /* Stop device's DMA activity */ iwl_pcie_apm_stop_master(trans); + if (trans->cfg->lp_xtal_workaround) { + iwl_pcie_apm_lp_xtal_enable(trans); + return; + } + /* Reset the entire device */ iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); @@ -273,7 +408,8 @@ static int iwl_pcie_nic_init(struct iwl_trans *trans) spin_unlock(&trans_pcie->irq_lock); - iwl_pcie_set_pwr(trans, false); + if (trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) + iwl_pcie_set_pwr(trans, false); iwl_op_mode_nic_config(trans->op_mode); @@ -435,78 +571,106 @@ static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num, return ret; } -static int iwl_pcie_secure_set(struct iwl_trans *trans, int cpu) +static int iwl_pcie_load_cpu_secured_sections(struct iwl_trans *trans, + const struct fw_img *image, + int cpu, + int *first_ucode_section) { int shift_param; - u32 address; - int ret = 0; + int i, ret = 0; + u32 last_read_idx = 0; if (cpu == 1) { shift_param = 0; - address = CSR_SECURE_BOOT_CPU1_STATUS_ADDR; + *first_ucode_section = 0; } else { shift_param = 16; - address = CSR_SECURE_BOOT_CPU2_STATUS_ADDR; + (*first_ucode_section)++; } - /* set CPU to started */ - iwl_trans_set_bits_mask(trans, - CSR_UCODE_LOAD_STATUS_ADDR, - CSR_CPU_STATUS_LOADING_STARTED << shift_param, - 1); - - /* set last complete descriptor number */ - iwl_trans_set_bits_mask(trans, - CSR_UCODE_LOAD_STATUS_ADDR, - CSR_CPU_STATUS_NUM_OF_LAST_COMPLETED - << shift_param, - 1); - - /* set last loaded block */ - iwl_trans_set_bits_mask(trans, - CSR_UCODE_LOAD_STATUS_ADDR, - CSR_CPU_STATUS_NUM_OF_LAST_LOADED_BLOCK - << shift_param, - 1); + for (i = *first_ucode_section; i < IWL_UCODE_SECTION_MAX; i++) { + last_read_idx = i; + if (!image->sec[i].data || + image->sec[i].offset == CPU1_CPU2_SEPARATOR_SECTION) { + IWL_DEBUG_FW(trans, + "Break since Data not valid or Empty section, sec = %d\n", + i); + break; + } + + if (i == (*first_ucode_section) + 1) + /* set CPU to started */ + iwl_set_bits_prph(trans, + CSR_UCODE_LOAD_STATUS_ADDR, + LMPM_CPU_HDRS_LOADING_COMPLETED + << shift_param); + + ret = iwl_pcie_load_section(trans, i, &image->sec[i]); + if (ret) + return ret; + } /* image loading complete */ - iwl_trans_set_bits_mask(trans, - CSR_UCODE_LOAD_STATUS_ADDR, - CSR_CPU_STATUS_LOADING_COMPLETED - << shift_param, - 1); - - /* set FH_TCSR_0_REG */ - iwl_trans_set_bits_mask(trans, FH_TCSR_0_REG0, 0x00400000, 1); - - /* verify image verification started */ - ret = iwl_poll_bit(trans, address, - CSR_SECURE_BOOT_CPU_STATUS_VERF_STATUS, - CSR_SECURE_BOOT_CPU_STATUS_VERF_STATUS, - CSR_SECURE_TIME_OUT); - if (ret < 0) { - IWL_ERR(trans, "secure boot process didn't start\n"); - return ret; + iwl_set_bits_prph(trans, + CSR_UCODE_LOAD_STATUS_ADDR, + LMPM_CPU_UCODE_LOADING_COMPLETED << shift_param); + + *first_ucode_section = last_read_idx; + + return 0; +} + +static int iwl_pcie_load_cpu_sections(struct iwl_trans *trans, + const struct fw_img *image, + int cpu, + int *first_ucode_section) +{ + int shift_param; + int i, ret = 0; + u32 last_read_idx = 0; + + if (cpu == 1) { + shift_param = 0; + *first_ucode_section = 0; + } else { + shift_param = 16; + (*first_ucode_section)++; } - /* wait for image verification to complete */ - ret = iwl_poll_bit(trans, address, - CSR_SECURE_BOOT_CPU_STATUS_VERF_COMPLETED, - CSR_SECURE_BOOT_CPU_STATUS_VERF_COMPLETED, - CSR_SECURE_TIME_OUT); + for (i = *first_ucode_section; i < IWL_UCODE_SECTION_MAX; i++) { + last_read_idx = i; - if (ret < 0) { - IWL_ERR(trans, "Time out on secure boot process\n"); - return ret; + if (!image->sec[i].data || + image->sec[i].offset == CPU1_CPU2_SEPARATOR_SECTION) { + IWL_DEBUG_FW(trans, + "Break since Data not valid or Empty section, sec = %d\n", + i); + break; + } + + ret = iwl_pcie_load_section(trans, i, &image->sec[i]); + if (ret) + return ret; } + if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) + iwl_set_bits_prph(trans, + CSR_UCODE_LOAD_STATUS_ADDR, + (LMPM_CPU_UCODE_LOADING_COMPLETED | + LMPM_CPU_HDRS_LOADING_COMPLETED | + LMPM_CPU_UCODE_LOADING_STARTED) << + shift_param); + + *first_ucode_section = last_read_idx; + return 0; } static int iwl_pcie_load_given_ucode(struct iwl_trans *trans, const struct fw_img *image) { - int i, ret = 0; + int ret = 0; + int first_ucode_section; IWL_DEBUG_FW(trans, "working with %s image\n", @@ -518,53 +682,68 @@ static int iwl_pcie_load_given_ucode(struct iwl_trans *trans, /* configure the ucode to be ready to get the secured image */ if (image->is_secure) { /* set secure boot inspector addresses */ - iwl_write32(trans, CSR_SECURE_INSPECTOR_CODE_ADDR, 0); - iwl_write32(trans, CSR_SECURE_INSPECTOR_DATA_ADDR, 0); - - /* release CPU1 reset if secure inspector image burned in OTP */ - iwl_write32(trans, CSR_RESET, 0); - } - - /* load to FW the binary sections of CPU1 */ - IWL_DEBUG_INFO(trans, "Loading CPU1\n"); - for (i = 0; - i < IWL_UCODE_FIRST_SECTION_OF_SECOND_CPU; - i++) { - if (!image->sec[i].data) - break; - ret = iwl_pcie_load_section(trans, i, &image->sec[i]); + iwl_write_prph(trans, + LMPM_SECURE_INSPECTOR_CODE_ADDR, + LMPM_SECURE_INSPECTOR_CODE_MEM_SPACE); + + iwl_write_prph(trans, + LMPM_SECURE_INSPECTOR_DATA_ADDR, + LMPM_SECURE_INSPECTOR_DATA_MEM_SPACE); + + /* set CPU1 header address */ + iwl_write_prph(trans, + LMPM_SECURE_UCODE_LOAD_CPU1_HDR_ADDR, + LMPM_SECURE_CPU1_HDR_MEM_SPACE); + + /* load to FW the binary Secured sections of CPU1 */ + ret = iwl_pcie_load_cpu_secured_sections(trans, image, 1, + &first_ucode_section); if (ret) return ret; - } - /* configure the ucode to start secure process on CPU1 */ - if (image->is_secure) { - /* config CPU1 to start secure protocol */ - ret = iwl_pcie_secure_set(trans, 1); + } else { + /* load to FW the binary Non secured sections of CPU1 */ + ret = iwl_pcie_load_cpu_sections(trans, image, 1, + &first_ucode_section); if (ret) return ret; - } else { - /* Remove all resets to allow NIC to operate */ - iwl_write32(trans, CSR_RESET, 0); } if (image->is_dual_cpus) { + /* set CPU2 header address */ + iwl_write_prph(trans, + LMPM_SECURE_UCODE_LOAD_CPU2_HDR_ADDR, + LMPM_SECURE_CPU2_HDR_MEM_SPACE); + /* load to FW the binary sections of CPU2 */ - IWL_DEBUG_INFO(trans, "working w/ DUAL CPUs - Loading CPU2\n"); - for (i = IWL_UCODE_FIRST_SECTION_OF_SECOND_CPU; - i < IWL_UCODE_SECTION_MAX; i++) { - if (!image->sec[i].data) - break; - ret = iwl_pcie_load_section(trans, i, &image->sec[i]); - if (ret) - return ret; - } + if (image->is_secure) + ret = iwl_pcie_load_cpu_secured_sections( + trans, image, 2, + &first_ucode_section); + else + ret = iwl_pcie_load_cpu_sections(trans, image, 2, + &first_ucode_section); + if (ret) + return ret; + } + + /* release CPU reset */ + if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) + iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT); + else + iwl_write32(trans, CSR_RESET, 0); - if (image->is_secure) { - /* set CPU2 for secure protocol */ - ret = iwl_pcie_secure_set(trans, 2); - if (ret) - return ret; + if (image->is_secure) { + /* wait for image verification to complete */ + ret = iwl_poll_prph_bit(trans, + LMPM_SECURE_BOOT_CPU1_STATUS_ADDR, + LMPM_SECURE_BOOT_STATUS_SUCCESS, + LMPM_SECURE_BOOT_STATUS_SUCCESS, + LMPM_SECURE_TIME_OUT); + + if (ret < 0) { + IWL_ERR(trans, "Time out on secure boot process\n"); + return ret; } } @@ -591,7 +770,7 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, set_bit(STATUS_RFKILL, &trans->status); else clear_bit(STATUS_RFKILL, &trans->status); - iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); + iwl_trans_pcie_rf_kill(trans, hw_rfkill); if (hw_rfkill && !run_in_rfkill) return -ERFKILL; @@ -706,7 +885,13 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) else clear_bit(STATUS_RFKILL, &trans->status); if (hw_rfkill != was_hw_rfkill) - iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); + iwl_trans_pcie_rf_kill(trans, hw_rfkill); +} + +void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state) +{ + if (iwl_op_mode_hw_rf_kill(trans->op_mode, state)) + iwl_trans_pcie_stop_device(trans); } static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test) @@ -815,7 +1000,7 @@ static int iwl_trans_pcie_start_hw(struct iwl_trans *trans) set_bit(STATUS_RFKILL, &trans->status); else clear_bit(STATUS_RFKILL, &trans->status); - iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); + iwl_trans_pcie_rf_kill(trans, hw_rfkill); return 0; } @@ -1158,6 +1343,7 @@ static const char *get_csr_string(int cmd) IWL_CMD(CSR_GIO_CHICKEN_BITS); IWL_CMD(CSR_ANA_PLL_CFG); IWL_CMD(CSR_HW_REV_WA_REG); + IWL_CMD(CSR_MONITOR_STATUS_REG); IWL_CMD(CSR_DBG_HPET_MEM_REG); default: return "UNKNOWN"; @@ -1190,6 +1376,7 @@ void iwl_pcie_dump_csr(struct iwl_trans *trans) CSR_DRAM_INT_TBL_REG, CSR_GIO_CHICKEN_BITS, CSR_ANA_PLL_CFG, + CSR_MONITOR_STATUS_REG, CSR_HW_REV_WA_REG, CSR_DBG_HPET_MEM_REG }; @@ -1407,16 +1594,15 @@ static ssize_t iwl_dbgfs_fh_reg_read(struct file *file, { struct iwl_trans *trans = file->private_data; char *buf = NULL; - int pos = 0; - ssize_t ret = -EFAULT; - - ret = pos = iwl_dump_fh(trans, &buf); - if (buf) { - ret = simple_read_from_buffer(user_buf, - count, ppos, buf, pos); - kfree(buf); - } + ssize_t ret; + ret = iwl_dump_fh(trans, &buf); + if (ret < 0) + return ret; + if (!buf) + return -EINVAL; + ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); + kfree(buf); return ret; } |