// SPDX-License-Identifier: ISC /* * Copyright (c) 2014-2017 Qualcomm Atheros, Inc. * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
/* The firmware does not support setting the coverage class. Instead this * function monitors and modifies the corresponding MAC registers.
*/ staticvoid ath10k_hw_qca988x_set_coverage_class(struct ath10k *ar, int radio_idx,
s16 value)
{
u32 slottime_reg;
u32 slottime;
u32 timeout_reg;
u32 ack_timeout;
u32 cts_timeout;
u32 phyclk_reg;
u32 phyclk;
u64 fw_dbglog_mask;
u32 fw_dbglog_level;
mutex_lock(&ar->conf_mutex);
/* Only modify registers if the core is started. */ if ((ar->state != ATH10K_STATE_ON) &&
(ar->state != ATH10K_STATE_RESTARTED)) {
spin_lock_bh(&ar->data_lock); /* Store config value for when radio boots up */
ar->fw_coverage.coverage_class = value;
spin_unlock_bh(&ar->data_lock); goto unlock;
}
/* Retrieve the current values of the two registers that need to be * adjusted.
*/
slottime_reg = ath10k_hif_read32(ar, WLAN_MAC_BASE_ADDRESS +
WAVE1_PCU_GBL_IFS_SLOT);
timeout_reg = ath10k_hif_read32(ar, WLAN_MAC_BASE_ADDRESS +
WAVE1_PCU_ACK_CTS_TIMEOUT);
phyclk_reg = ath10k_hif_read32(ar, WLAN_MAC_BASE_ADDRESS +
WAVE1_PHYCLK);
phyclk = MS(phyclk_reg, WAVE1_PHYCLK_USEC) + 1;
if (value < 0)
value = ar->fw_coverage.coverage_class;
/* Break out if the coverage class and registers have the expected * value.
*/ if (value == ar->fw_coverage.coverage_class &&
slottime_reg == ar->fw_coverage.reg_slottime_conf &&
timeout_reg == ar->fw_coverage.reg_ack_cts_timeout_conf &&
phyclk_reg == ar->fw_coverage.reg_phyclk) goto unlock;
/* Store new initial register values from the firmware. */ if (slottime_reg != ar->fw_coverage.reg_slottime_conf)
ar->fw_coverage.reg_slottime_orig = slottime_reg; if (timeout_reg != ar->fw_coverage.reg_ack_cts_timeout_conf)
ar->fw_coverage.reg_ack_cts_timeout_orig = timeout_reg;
ar->fw_coverage.reg_phyclk = phyclk_reg;
/* Calculate new value based on the (original) firmware calculation. */
slottime_reg = ar->fw_coverage.reg_slottime_orig;
timeout_reg = ar->fw_coverage.reg_ack_cts_timeout_orig;
/* Do some sanity checks on the slottime register. */ if (slottime_reg % phyclk) {
ath10k_warn(ar, "failed to set coverage class: expected integer microsecond value in register\n");
goto store_regs;
}
slottime = MS(slottime_reg, WAVE1_PCU_GBL_IFS_SLOT);
slottime = slottime / phyclk; if (slottime != 9 && slottime != 20) {
ath10k_warn(ar, "failed to set coverage class: expected slot time of 9 or 20us in HW register. It is %uus.\n",
slottime);
goto store_regs;
}
/* Recalculate the register values by adding the additional propagation * delay (3us per coverage class).
*/
/* Ensure we have a debug level of WARN set for the case that the * coverage class is larger than 0. This is important as we need to * set the registers again if the firmware does an internal reset and * this way we will be notified of the event.
*/
fw_dbglog_mask = ath10k_debug_get_fw_dbglog_mask(ar);
fw_dbglog_level = ath10k_debug_get_fw_dbglog_level(ar);
if (value > 0) { if (fw_dbglog_level > ATH10K_DBGLOG_LEVEL_WARN)
fw_dbglog_level = ATH10K_DBGLOG_LEVEL_WARN;
fw_dbglog_mask = ~0;
}
store_regs: /* After an error we will not retry setting the coverage class. */
spin_lock_bh(&ar->data_lock);
ar->fw_coverage.coverage_class = value;
spin_unlock_bh(&ar->data_lock);
/** * ath10k_hw_qca6174_enable_pll_clock() - enable the qca6174 hw pll clock * @ar: the ath10k blob * * This function is very hardware specific, the clock initialization * steps is very sensitive and could lead to unknown crash, so they * should be done in sequence. * * *** Be aware if you planned to refactor them. *** * * Return: 0 if successfully enable the pll, otherwise EINVAL
*/ staticint ath10k_hw_qca6174_enable_pll_clock(struct ath10k *ar)
{ int ret, wait_limit;
u32 clk_div_addr, pll_init_addr, speed_addr;
u32 addr, reg_val, mem_val; struct ath10k_hw_params *hw; conststruct ath10k_hw_clk_params *hw_clk;
/* Read efuse register to find out the right hw clock configuration */
addr = (RTC_SOC_BASE_ADDRESS | EFUSE_OFFSET);
ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); if (ret) return -EINVAL;
/* sanitize if the hw refclk index is out of the boundary */ if (MS(reg_val, EFUSE_XTAL_SEL) > ATH10K_HW_REFCLK_COUNT) return -EINVAL;
/* Set the rnfrac and outdiv params to bb_pll register */
addr = (RTC_SOC_BASE_ADDRESS | BB_PLL_CONFIG_OFFSET);
ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); if (ret) return -EINVAL;
/* Set the correct settle time value to pll_settle register */
addr = (RTC_WMAC_BASE_ADDRESS | WLAN_PLL_SETTLE_OFFSET);
ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); if (ret) return -EINVAL;
reg_val &= ~WLAN_PLL_SETTLE_TIME_MASK;
reg_val |= SM(hw_clk->settle_time, WLAN_PLL_SETTLE_TIME);
ret = ath10k_bmi_write_soc_reg(ar, addr, reg_val); if (ret) return -EINVAL;
/* Set the clock_ctrl div to core_clk_ctrl register */
addr = (RTC_SOC_BASE_ADDRESS | SOC_CORE_CLK_CTRL_OFFSET);
ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); if (ret) return -EINVAL;
reg_val &= ~SOC_CORE_CLK_CTRL_DIV_MASK;
reg_val |= SM(1, SOC_CORE_CLK_CTRL_DIV);
ret = ath10k_bmi_write_soc_reg(ar, addr, reg_val); if (ret) return -EINVAL;
/* Set the clock_div register */
mem_val = 1;
ret = ath10k_bmi_write_memory(ar, clk_div_addr, &mem_val, sizeof(mem_val)); if (ret) return -EINVAL;
/* Configure the pll_control register */
addr = (RTC_WMAC_BASE_ADDRESS | WLAN_PLL_CONTROL_OFFSET);
ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); if (ret) return -EINVAL;
reg_val |= (SM(hw_clk->refdiv, WLAN_PLL_CONTROL_REFDIV) |
SM(hw_clk->div, WLAN_PLL_CONTROL_DIV) |
SM(1, WLAN_PLL_CONTROL_NOPWD));
ret = ath10k_bmi_write_soc_reg(ar, addr, reg_val); if (ret) return -EINVAL;
/* busy wait (max 1s) the rtc_sync status register indicate ready */
wait_limit = 100000;
addr = (RTC_WMAC_BASE_ADDRESS | RTC_SYNC_STATUS_OFFSET); do {
ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); if (ret) return -EINVAL;
if (!MS(reg_val, RTC_SYNC_STATUS_PLL_CHANGING)) break;
wait_limit--;
udelay(10);
} while (wait_limit > 0);
if (MS(reg_val, RTC_SYNC_STATUS_PLL_CHANGING)) return -EINVAL;
/* Unset the pll_bypass in pll_control register */
addr = (RTC_WMAC_BASE_ADDRESS | WLAN_PLL_CONTROL_OFFSET);
ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); if (ret) return -EINVAL;
reg_val &= ~WLAN_PLL_CONTROL_BYPASS_MASK;
reg_val |= SM(0, WLAN_PLL_CONTROL_BYPASS);
ret = ath10k_bmi_write_soc_reg(ar, addr, reg_val); if (ret) return -EINVAL;
/* busy wait (max 1s) the rtc_sync status register indicate ready */
wait_limit = 100000;
addr = (RTC_WMAC_BASE_ADDRESS | RTC_SYNC_STATUS_OFFSET); do {
ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); if (ret) return -EINVAL;
if (!MS(reg_val, RTC_SYNC_STATUS_PLL_CHANGING)) break;
wait_limit--;
udelay(10);
} while (wait_limit > 0);
if (MS(reg_val, RTC_SYNC_STATUS_PLL_CHANGING)) return -EINVAL;
/* Enable the hardware cpu clock register */
addr = (RTC_SOC_BASE_ADDRESS | SOC_CPU_CLOCK_OFFSET);
ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); if (ret) return -EINVAL;
reg_val &= ~SOC_CPU_CLOCK_STANDARD_MASK;
reg_val |= SM(1, SOC_CPU_CLOCK_STANDARD);
ret = ath10k_bmi_write_soc_reg(ar, addr, reg_val); if (ret) return -EINVAL;
/* unset the nopwd from pll_control register */
addr = (RTC_WMAC_BASE_ADDRESS | WLAN_PLL_CONTROL_OFFSET);
ret = ath10k_bmi_read_soc_reg(ar, addr, ®_val); if (ret) return -EINVAL;
reg_val &= ~WLAN_PLL_CONTROL_NOPWD_MASK;
ret = ath10k_bmi_write_soc_reg(ar, addr, reg_val); if (ret) return -EINVAL;
/* enable the pll_init register */
mem_val = 1;
ret = ath10k_bmi_write_memory(ar, pll_init_addr, &mem_val, sizeof(mem_val)); if (ret) return -EINVAL;
/* set the target clock frequency to speed register */
ret = ath10k_bmi_write_memory(ar, speed_addr, &hw->target_cpu_freq, sizeof(hw->target_cpu_freq)); if (ret) return -EINVAL;
return 0;
}
/* Program CPU_ADDR_MSB to allow different memory * region access.
*/ staticvoid ath10k_hw_map_target_mem(struct ath10k *ar, u32 msb)
{
u32 address = SOC_CORE_BASE_ADDRESS + FW_RAM_CONFIG_ADDRESS;
ath10k_hif_write32(ar, address, msb);
}
/* 1. Write to memory region of target, such as IRAM and DRAM. * 2. Target address( 0 ~ 00100000 & 0x00400000~0x00500000) * can be written directly. See ath10k_pci_targ_cpu_to_ce_addr() too. * 3. In order to access the region other than the above, * we need to set the value of register CPU_ADDR_MSB. * 4. Target memory access space is limited to 1M size. If the size is larger * than 1M, need to split it and program CPU_ADDR_MSB accordingly.
*/ staticint ath10k_hw_diag_segment_msb_download(struct ath10k *ar, constvoid *buffer,
u32 address,
u32 length)
{
u32 addr = address & REGION_ACCESS_SIZE_MASK; int ret, remain_size, size; const u8 *buf;
ret = ath10k_hif_diag_write(ar, address, buffer, size); if (ret) {
ath10k_warn(ar, "failed to download the first %d bytes segment to address:0x%x: %d\n",
size, address, ret); goto done;
}
/* Change msb to the next memory region*/
ath10k_hw_map_target_mem(ar,
CPU_ADDR_MSB_REGION_VAL(address) + 1);
buf = buffer + size;
ret = ath10k_hif_diag_write(ar,
address & ~REGION_ACCESS_SIZE_MASK,
buf, remain_size); if (ret) {
ath10k_warn(ar, "failed to download the second %d bytes segment to address:0x%x: %d\n",
remain_size,
address & ~REGION_ACCESS_SIZE_MASK,
ret); goto done;
}
} else {
ret = ath10k_hif_diag_write(ar, address, buffer, length); if (ret) {
ath10k_warn(ar, "failed to download the only %d bytes segment to address:0x%x: %d\n",
length, address, ret); goto done;
}
}
done: /* Change msb to DRAM */
ath10k_hw_map_target_mem(ar,
CPU_ADDR_MSB_REGION_VAL(DRAM_BASE_ADDRESS)); return ret;
}
/* check firmware header. If it has no correct magic number * or it's compressed, returns error.
*/
hdr = (struct bmi_segmented_file_header *)buf; if (__le32_to_cpu(hdr->magic_num) != BMI_SGMTFILE_MAGIC_NUM) {
ath10k_dbg(ar, ATH10K_DBG_BOOT, "Not a supported firmware, magic_num:0x%x\n",
hdr->magic_num); return -EINVAL;
}
if (hdr->file_flags != 0) {
ath10k_dbg(ar, ATH10K_DBG_BOOT, "Not a supported firmware, file_flags:0x%x\n",
hdr->file_flags); return -EINVAL;
}
metadata = (struct bmi_segmented_metadata *)hdr->data;
left = length - sizeof(*hdr);
while (left > 0) { if (left < sizeof(*metadata)) {
ath10k_warn(ar, "firmware segment is truncated: %d\n",
left);
ret = -EINVAL; break;
}
base_addr = __le32_to_cpu(metadata->addr);
base_len = __le32_to_cpu(metadata->length);
buf = metadata->data;
left -= sizeof(*metadata);
switch (base_len) { case BMI_SGMTFILE_BEGINADDR: /* base_addr is the start address to run */
ret = ath10k_bmi_set_start(ar, base_addr);
base_len = 0; break; case BMI_SGMTFILE_DONE: /* no more segment */
base_len = 0;
sgmt_end = true;
ret = 0; break; case BMI_SGMTFILE_BDDATA: case BMI_SGMTFILE_EXEC:
ath10k_warn(ar, "firmware has unsupported segment:%d\n",
base_len);
ret = -EINVAL; break; default: if (base_len > left) { /* sanity check */
ath10k_warn(ar, "firmware has invalid segment length, %d > %d\n",
base_len, left);
ret = -EINVAL; break;
}
ret = ath10k_hw_diag_segment_download(ar,
buf,
base_addr,
base_len);
if (ret)
ath10k_warn(ar, "failed to download firmware via diag interface:%d\n",
ret); break;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.