// SPDX-License-Identifier: ISC /* * Copyright (c) 2005-2011 Atheros Communications Inc. * Copyright (c) 2011-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved. * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
*/
void ath10k_core_get_fw_features_str(struct ath10k *ar, char *buf,
size_t buf_len)
{
size_t len = 0; int i;
for (i = 0; i < ATH10K_FW_FEATURE_COUNT; i++) { if (test_bit(i, ar->normal_mode_fw.fw_file.fw_features)) { if (len > 0)
len += scnprintf(buf + len, buf_len - len, ",");
ret = ath10k_bmi_write32(ar, hi_option_flag2, param); if (ret) return ret;
return 0;
}
staticint ath10k_init_configure_target(struct ath10k *ar)
{
u32 param_host; int ret;
/* tell target which HTC version it is used*/
ret = ath10k_bmi_write32(ar, hi_app_host_interest,
HTC_PROTOCOL_VERSION); if (ret) {
ath10k_err(ar, "settings HTC version failed\n"); return ret;
}
/* set the firmware mode to STA/IBSS/AP */
ret = ath10k_bmi_read32(ar, hi_option_flag, ¶m_host); if (ret) {
ath10k_err(ar, "setting firmware mode (1/2) failed\n"); return ret;
}
ret = ath10k_bmi_write32(ar, hi_option_flag, param_host); if (ret) {
ath10k_err(ar, "setting firmware mode (2/2) failed\n"); return ret;
}
/* We do all byte-swapping on the host */
ret = ath10k_bmi_write32(ar, hi_be, 0); if (ret) {
ath10k_err(ar, "setting host CPU BE mode failed\n"); return ret;
}
/* Some devices have a special sanity check that verifies the PCI * Device ID is written to this host interest var. It is known to be * required to boot QCA6164.
*/
ret = ath10k_bmi_write32(ar, hi_hci_uart_pwr_mgmt_params_ext,
ar->dev_id); if (ret) {
ath10k_err(ar, "failed to set pwr_mgmt_params: %d\n", ret); return ret;
}
ret = ath10k_bmi_write_memory(ar, board_ext_data_addr,
data + board_data_size,
board_ext_data_size); if (ret) {
ath10k_err(ar, "could not write board ext data (%d)\n", ret); return ret;
}
ret = ath10k_bmi_write32(ar, hi_board_ext_data_config,
(board_ext_data_size << 16) | 1); if (ret) {
ath10k_err(ar, "could not write board ext data bit (%d)\n",
ret); return ret;
}
if (!ar->normal_mode_fw.fw_file.otp_data ||
!ar->normal_mode_fw.fw_file.otp_len) {
ath10k_warn(ar, "failed to retrieve board id because of invalid otp\n"); return -ENODATA;
}
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot upload otp to 0x%x len %zd for board id\n",
address, ar->normal_mode_fw.fw_file.otp_len);
ret = ath10k_bmi_fast_download(ar, address,
ar->normal_mode_fw.fw_file.otp_data,
ar->normal_mode_fw.fw_file.otp_len); if (ret) {
ath10k_err(ar, "could not write otp for board id check: %d\n",
ret); return ret;
}
ret = ath10k_bmi_execute(ar, address, bmi_board_id_param, &result); if (ret) {
ath10k_err(ar, "could not execute otp for board id check: %d\n",
ret); return ret;
}
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot get otp board id result 0x%08x board_id %d chip_id %d ext_bid_support %d\n",
result, board_id, chip_id, ext_bid_support);
ar->id.ext_bid_supported = ext_bid_support;
if ((result & ATH10K_BMI_BOARD_ID_STATUS_MASK) != 0 ||
(board_id == 0)) {
ath10k_dbg(ar, ATH10K_DBG_BOOT, "board id does not exist in otp, ignore it\n"); return -EOPNOTSUPP;
}
if (hdr->type != ATH10K_SMBIOS_BDF_EXT_TYPE) return;
if (hdr->length != ATH10K_SMBIOS_BDF_EXT_LENGTH) {
ath10k_dbg(ar, ATH10K_DBG_BOOT, "wrong smbios bdf ext type length (%d).\n",
hdr->length); return;
}
bdf_enabled = *((u8 *)hdr + ATH10K_SMBIOS_BDF_EXT_OFFSET); if (!bdf_enabled) {
ath10k_dbg(ar, ATH10K_DBG_BOOT, "bdf variant name not found.\n"); return;
}
/* Only one string exists (per spec) */
bdf_ext = (char *)hdr + hdr->length;
if (memcmp(bdf_ext, magic, strlen(magic)) != 0) {
ath10k_dbg(ar, ATH10K_DBG_BOOT, "bdf variant magic does not match.\n"); return;
}
for (i = 0; i < strlen(bdf_ext); i++) { if (!isascii(bdf_ext[i]) || !isprint(bdf_ext[i])) {
ath10k_dbg(ar, ATH10K_DBG_BOOT, "bdf variant name contains non ascii chars.\n"); return;
}
}
/* Copy extension name without magic suffix */ if (strscpy(ar->id.bdf_ext, bdf_ext + strlen(magic), sizeof(ar->id.bdf_ext)) < 0) {
ath10k_dbg(ar, ATH10K_DBG_BOOT, "bdf variant string is longer than the buffer can accommodate (variant: %s)\n",
bdf_ext); return;
}
node = ar->dev->of_node; if (!node) return -ENOENT;
of_property_read_string(node, "qcom,calibration-variant",
&variant); if (!variant)
of_property_read_string(node, "qcom,ath10k-calibration-variant",
&variant); if (!variant) return -ENODATA;
if (strscpy(ar->id.bdf_ext, variant, sizeof(ar->id.bdf_ext)) < 0)
ath10k_dbg(ar, ATH10K_DBG_BOOT, "bdf variant string is longer than the buffer can accommodate (variant: %s)\n",
variant);
/* Check if device supports to download firmware via * diag copy engine. Downloading firmware via diag CE * greatly reduces the time to download firmware.
*/ if (ar->hw_params.fw_diag_ce_download) {
ret = ath10k_hw_diag_fast_download(ar, address,
data, data_len); if (ret == 0) /* firmware upload via diag ce was successful */ return 0;
ath10k_warn(ar, "failed to upload firmware via diag ce, trying BMI: %d",
ret);
}
ret = memcmp(board_ie_data, boardname, strlen(boardname)); if (ret) break;
name_match_found = true;
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot found match for name '%s'",
boardname); break; case ATH10K_BD_IE_BOARD_DATA: if (!name_match_found) /* no match found */ break;
if (bd_ie_type == ATH10K_BD_IE_BOARD) {
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot found board data for '%s'",
boardname);
ar->normal_mode_fw.board_data = board_ie_data;
ar->normal_mode_fw.board_len = board_ie_len;
} elseif (bd_ie_type == ATH10K_BD_IE_BOARD_EXT) {
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot found eboard data for '%s'",
boardname);
if (len < ALIGN(ie_len, 4)) {
ath10k_err(ar, "invalid length for board ie_id %d ie_len %zu len %zu\n",
ie_id, ie_len, len); return -EINVAL;
}
switch (ie_id) { case ATH10K_BD_IE_BOARD:
ret = ath10k_core_parse_bd_ie_board(ar, data, ie_len,
boardname,
ATH10K_BD_IE_BOARD); if (ret == -ENOENT) /* no match found, continue */ break;
/* either found or error, so stop searching */ goto out; case ATH10K_BD_IE_BOARD_EXT:
ret = ath10k_core_parse_bd_ie_board(ar, data, ie_len,
boardname,
ATH10K_BD_IE_BOARD_EXT); if (ret == -ENOENT) /* no match found, continue */ break;
/* either found or error, so stop searching */ goto out;
}
/* jump over the padding */
ie_len = ALIGN(ie_len, 4);
len -= ie_len;
data += ie_len;
}
out: /* return result of parse_bd_ie_board() or -ENOENT */ return ret;
}
/* Skip if already fetched during board data download */ if (!ar->normal_mode_fw.board)
ar->normal_mode_fw.board = ath10k_fetch_fw_file(ar,
ar->hw_params.fw.dir,
filename); if (IS_ERR(ar->normal_mode_fw.board)) return PTR_ERR(ar->normal_mode_fw.board);
data = ar->normal_mode_fw.board->data;
len = ar->normal_mode_fw.board->size;
/* magic has extra null byte padded */
magic_len = strlen(ATH10K_BOARD_MAGIC) + 1; if (len < magic_len) {
ath10k_err(ar, "failed to find magic value in %s/%s, file too short: %zu\n",
ar->hw_params.fw.dir, filename, len);
ret = -EINVAL; goto err;
}
if (memcmp(data, ATH10K_BOARD_MAGIC, magic_len)) {
ath10k_err(ar, "found invalid board magic\n");
ret = -EINVAL; goto err;
}
/* magic is padded to 4 bytes */
magic_len = ALIGN(magic_len, 4); if (len < magic_len) {
ath10k_err(ar, "failed: %s/%s too small to contain board data, len: %zu\n",
ar->hw_params.fw.dir, filename, len);
ret = -EINVAL; goto err;
}
data += magic_len;
len -= magic_len;
/* attempt to find boardname in the IE list */
ret = ath10k_core_search_bd(ar, boardname, data, len);
/* if we didn't find it and have a fallback name, try that */ if (ret == -ENOENT && fallback_boardname1)
ret = ath10k_core_search_bd(ar, fallback_boardname1, data, len);
if (ret == -ENOENT && fallback_boardname2)
ret = ath10k_core_search_bd(ar, fallback_boardname2, data, len);
if (ret == -ENOENT) {
ath10k_err(ar, "failed to fetch board data for %s from %s/%s\n",
boardname, ar->hw_params.fw.dir, filename);
ret = -ENODATA;
}
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot using eboard name '%s'\n", name); return 0;
} /* Fallback if returned board id is zero */ return -1;
}
int ath10k_core_fetch_board_file(struct ath10k *ar, int bd_ie_type)
{ char boardname[100], fallback_boardname1[100], fallback_boardname2[100]; int ret;
if (bd_ie_type == ATH10K_BD_IE_BOARD) { /* With variant and chip id */
ret = ath10k_core_create_board_name(ar, boardname, sizeof(boardname), true, true); if (ret) {
ath10k_err(ar, "failed to create board name: %d", ret); return ret;
}
/* Without variant and only chip-id */
ret = ath10k_core_create_board_name(ar, fallback_boardname1, sizeof(boardname), false, true); if (ret) {
ath10k_err(ar, "failed to create 1st fallback board name: %d",
ret); return ret;
}
/* Without variant and without chip-id */
ret = ath10k_core_create_board_name(ar, fallback_boardname2, sizeof(boardname), false, false); if (ret) {
ath10k_err(ar, "failed to create 2nd fallback board name: %d",
ret); return ret;
}
} elseif (bd_ie_type == ATH10K_BD_IE_BOARD_EXT) {
ret = ath10k_core_create_eboard_name(ar, boardname, sizeof(boardname)); if (ret) {
ath10k_err(ar, "fallback to eboard.bin since board id 0"); goto fallback;
}
}
ar->bd_api = 2;
ret = ath10k_core_fetch_board_data_api_n(ar, boardname,
fallback_boardname1,
fallback_boardname2,
ATH10K_BOARD_API2_FILE); if (!ret) goto success;
fallback:
ar->bd_api = 1;
ret = ath10k_core_fetch_board_data_api_1(ar, bd_ie_type); if (ret) {
ath10k_err(ar, "failed to fetch board-2.bin or board.bin from %s\n",
ar->hw_params.fw.dir); return ret;
}
if (!ar->normal_mode_fw.fw_file.otp_data ||
!ar->normal_mode_fw.fw_file.otp_len) {
ath10k_warn(ar, "failed to retrieve extended board id due to otp binary missing\n"); return -ENODATA;
}
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot upload otp to 0x%x len %zd for ext board id\n",
address, ar->normal_mode_fw.fw_file.otp_len);
ret = ath10k_bmi_fast_download(ar, address,
ar->normal_mode_fw.fw_file.otp_data,
ar->normal_mode_fw.fw_file.otp_len); if (ret) {
ath10k_err(ar, "could not write otp for ext board id check: %d\n",
ret); return ret;
}
ret = ath10k_bmi_execute(ar, address, BMI_PARAM_GET_EXT_BOARD_ID, &result); if (ret) {
ath10k_err(ar, "could not execute otp for ext board id check: %d\n",
ret); return ret;
}
if (!result) {
ath10k_dbg(ar, ATH10K_DBG_BOOT, "ext board id does not exist in otp, ignore it\n"); return -EOPNOTSUPP;
}
ext_board_id = result & ATH10K_BMI_EBOARD_ID_STATUS_MASK;
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot get otp ext board id result 0x%08x ext_board_id %d\n",
result, ext_board_id);
ret = ath10k_push_board_ext_data(ar, data, data_len); if (ret) {
ath10k_err(ar, "could not push board ext data (%d)\n", ret); gotoexit;
}
ret = ath10k_bmi_read32(ar, hi_board_data, &board_address); if (ret) {
ath10k_err(ar, "could not read board data addr (%d)\n", ret); gotoexit;
}
ret = ath10k_bmi_write_memory(ar, board_address, data,
min_t(u32, board_data_size,
data_len)); if (ret) {
ath10k_err(ar, "could not write board data (%d)\n", ret); gotoexit;
}
ret = ath10k_bmi_write32(ar, hi_board_data_initialized, 1); if (ret) {
ath10k_err(ar, "could not write board data bit (%d)\n", ret); gotoexit;
}
if (!ar->id.ext_bid_supported) gotoexit;
/* Extended board data download */
ret = ath10k_core_get_ext_board_id_from_otp(ar); if (ret == -EOPNOTSUPP) { /* Not fetching ext_board_data if ext board id is 0 */
ath10k_dbg(ar, ATH10K_DBG_BOOT, "otp returned ext board id 0\n"); return 0;
} elseif (ret) {
ath10k_err(ar, "failed to get extended board id: %d\n", ret); gotoexit;
}
ret = ath10k_core_fetch_board_file(ar, ATH10K_BD_IE_BOARD_EXT); if (ret) gotoexit;
if (ar->normal_mode_fw.ext_board_data) {
ext_board_address = board_address + EXT_BOARD_ADDRESS_OFFSET;
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot writing ext board data to addr 0x%x",
ext_board_address);
ret = ath10k_bmi_write_memory(ar, ext_board_address,
ar->normal_mode_fw.ext_board_data,
min_t(u32, eboard_data_size, data_len)); if (ret)
ath10k_err(ar, "failed to write ext board data: %d\n", ret);
}
ret = ath10k_download_board_data(ar,
ar->running_fw->board_data,
ar->running_fw->board_len); if (ret) {
ath10k_err(ar, "failed to download board data: %d\n", ret); return ret;
}
/* OTP is optional */
if (!ar->running_fw->fw_file.otp_data ||
!ar->running_fw->fw_file.otp_len) {
ath10k_warn(ar, "Not running otp, calibration will be incorrect (otp-data %p otp_len %zd)!\n",
ar->running_fw->fw_file.otp_data,
ar->running_fw->fw_file.otp_len); return 0;
}
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot upload otp to 0x%x len %zd\n",
address, ar->running_fw->fw_file.otp_len);
ret = ath10k_bmi_fast_download(ar, address,
ar->running_fw->fw_file.otp_data,
ar->running_fw->fw_file.otp_len); if (ret) {
ath10k_err(ar, "could not write otp (%d)\n", ret); return ret;
}
/* As of now pre-cal is valid for 10_4 variants */ if (ar->cal_mode == ATH10K_PRE_CAL_MODE_DT ||
ar->cal_mode == ATH10K_PRE_CAL_MODE_FILE ||
ar->cal_mode == ATH10K_PRE_CAL_MODE_NVMEM)
bmi_otp_exe_param = BMI_PARAM_FLASH_SECTION_ALL;
ret = ath10k_bmi_execute(ar, address, bmi_otp_exe_param, &result); if (ret) {
ath10k_err(ar, "could not execute otp (%d)\n", ret); return ret;
}
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot otp execute result %d\n", result);
if (!(skip_otp || test_bit(ATH10K_FW_FEATURE_IGNORE_OTP_RESULT,
ar->running_fw->fw_file.fw_features)) &&
result != 0) {
ath10k_err(ar, "otp calibration failed: %d", result); return -EINVAL;
}
return 0;
}
staticint ath10k_download_cal_file(struct ath10k *ar, conststruct firmware *file)
{ int ret;
if (!file) return -ENOENT;
if (IS_ERR(file)) return PTR_ERR(file);
ret = ath10k_download_board_data(ar, file->data, file->size); if (ret) {
ath10k_err(ar, "failed to download cal_file data: %d\n", ret); return ret;
}
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot cal file downloaded\n");
return 0;
}
staticint ath10k_download_cal_dt(struct ath10k *ar, constchar *dt_name)
{ struct device_node *node; int data_len; void *data; int ret;
node = ar->dev->of_node; if (!node) /* Device Tree is optional, don't print any warnings if * there's no node for ath10k.
*/ return -ENOENT;
if (!of_get_property(node, dt_name, &data_len)) { /* The calibration data node is optional */ return -ENOENT;
}
if (data_len != ar->hw_params.cal_data_len) {
ath10k_warn(ar, "invalid calibration data length in DT: %d\n",
data_len);
ret = -EMSGSIZE; goto out;
}
data = kmalloc(data_len, GFP_KERNEL); if (!data) {
ret = -ENOMEM; goto out;
}
ret = of_property_read_u8_array(node, dt_name, data, data_len); if (ret) {
ath10k_warn(ar, "failed to read calibration data from DT: %d\n",
ret); goto out_free;
}
ret = ath10k_download_board_data(ar, data, data_len); if (ret) {
ath10k_warn(ar, "failed to download calibration data from Device Tree: %d\n",
ret); goto out_free;
}
ret = ath10k_hif_fetch_cal_eeprom(ar, &data, &data_len); if (ret) { if (ret != -EOPNOTSUPP)
ath10k_warn(ar, "failed to read calibration data from EEPROM: %d\n",
ret); goto out_free;
}
ret = ath10k_download_board_data(ar, data, data_len); if (ret) {
ath10k_warn(ar, "failed to download calibration data from EEPROM: %d\n",
ret); goto out_free;
}
cell = devm_nvmem_cell_get(ar->dev, cell_name); if (IS_ERR(cell)) {
ret = PTR_ERR(cell); return ret;
}
buf = nvmem_cell_read(cell, &len); if (IS_ERR(buf)) return PTR_ERR(buf);
if (ar->hw_params.cal_data_len != len) {
kfree(buf);
ath10k_warn(ar, "invalid calibration data length in nvmem-cell '%s': %zu != %u\n",
cell_name, len, ar->hw_params.cal_data_len); return -EMSGSIZE;
}
ret = ath10k_download_board_data(ar, buf, len);
kfree(buf); if (ret)
ath10k_warn(ar, "failed to download calibration data from nvmem-cell '%s': %d\n",
cell_name, ret);
return ret;
}
int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, constchar *name, struct ath10k_fw_file *fw_file)
{
size_t magic_len, len, ie_len; int ie_id, i, index, bit, ret; struct ath10k_fw_ie *hdr; const u8 *data;
__le32 *timestamp, *version;
/* first fetch the firmware file (firmware-*.bin) */
fw_file->firmware = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir,
name); if (IS_ERR(fw_file->firmware)) return PTR_ERR(fw_file->firmware);
data = fw_file->firmware->data;
len = fw_file->firmware->size;
/* magic also includes the null byte, check that as well */
magic_len = strlen(ATH10K_FIRMWARE_MAGIC) + 1;
if (len < magic_len) {
ath10k_err(ar, "firmware file '%s/%s' too small to contain magic: %zu\n",
ar->hw_params.fw.dir, name, len);
ret = -EINVAL; goto err;
}
if (memcmp(data, ATH10K_FIRMWARE_MAGIC, magic_len) != 0) {
ath10k_err(ar, "invalid firmware magic\n");
ret = -EINVAL; goto err;
}
/* jump over the padding */
magic_len = ALIGN(magic_len, 4);
len -= magic_len;
data += magic_len;
/* loop elements */ while (len > sizeof(struct ath10k_fw_ie)) {
hdr = (struct ath10k_fw_ie *)data;
break; case ATH10K_FW_IE_WMI_OP_VERSION: if (ie_len != sizeof(u32)) break;
version = (__le32 *)data;
fw_file->wmi_op_version = le32_to_cpup(version);
ath10k_dbg(ar, ATH10K_DBG_BOOT, "found fw ie wmi op version %d\n",
fw_file->wmi_op_version); break; case ATH10K_FW_IE_HTT_OP_VERSION: if (ie_len != sizeof(u32)) break;
/* jump over the padding */
ie_len = ALIGN(ie_len, 4);
len -= ie_len;
data += ie_len;
}
if (!test_bit(ATH10K_FW_FEATURE_NON_BMI, fw_file->fw_features) &&
(!fw_file->firmware_data || !fw_file->firmware_len)) {
ath10k_warn(ar, "No ATH10K_FW_IE_FW_IMAGE found from '%s/%s', skipping\n",
ar->hw_params.fw.dir, name);
ret = -ENOMEDIUM; goto err;
}
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot did not find a pre-calibration nvmem-cell, try file next: %d\n",
ret);
ret = ath10k_download_cal_file(ar, ar->pre_cal_file); if (ret == 0) {
ar->cal_mode = ATH10K_PRE_CAL_MODE_FILE; goto success;
}
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot did not find a pre calibration file, try DT next: %d\n",
ret);
ret = ath10k_download_cal_dt(ar, "qcom,pre-calibration-data"); if (ret == -ENOENT)
ret = ath10k_download_cal_dt(ar, "qcom,ath10k-pre-calibration-data"); if (ret) {
ath10k_dbg(ar, ATH10K_DBG_BOOT, "unable to load pre cal data from DT: %d\n", ret); return ret;
}
ar->cal_mode = ATH10K_PRE_CAL_MODE_DT;
success:
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot using calibration mode %s\n",
ath10k_cal_mode_str(ar->cal_mode));
return 0;
}
staticint ath10k_core_pre_cal_config(struct ath10k *ar)
{ int ret;
ret = ath10k_core_pre_cal_download(ar); if (ret) {
ath10k_dbg(ar, ATH10K_DBG_BOOT, "failed to load pre cal data: %d\n", ret); return ret;
}
ret = ath10k_core_get_board_id_from_otp(ar); if (ret) {
ath10k_err(ar, "failed to get board id: %d\n", ret); return ret;
}
ret = ath10k_download_and_run_otp(ar); if (ret) {
ath10k_err(ar, "failed to run otp: %d\n", ret); return ret;
}
ath10k_dbg(ar, ATH10K_DBG_BOOT, "pre cal configuration done successfully\n");
return 0;
}
staticint ath10k_download_cal_data(struct ath10k *ar)
{ int ret;
ret = ath10k_core_pre_cal_config(ar); if (ret == 0) return 0;
ath10k_dbg(ar, ATH10K_DBG_BOOT, "pre cal download procedure failed, try cal file: %d\n",
ret);
staticint ath10k_init_uart(struct ath10k *ar)
{ int ret;
/* * Explicitly setting UART prints to zero as target turns it on * based on scratch registers.
*/
ret = ath10k_bmi_write32(ar, hi_serial_enable, 0); if (ret) {
ath10k_warn(ar, "could not disable UART prints (%d)\n", ret); return ret;
}
if (!uart_print) { if (ar->hw_params.uart_pin_workaround) {
ret = ath10k_bmi_write32(ar, hi_dbg_uart_txpin,
ar->hw_params.uart_pin); if (ret) {
ath10k_warn(ar, "failed to set UART TX pin: %d",
ret); return ret;
}
}
return 0;
}
ret = ath10k_bmi_write32(ar, hi_dbg_uart_txpin, ar->hw_params.uart_pin); if (ret) {
ath10k_warn(ar, "could not enable UART prints (%d)\n", ret); return ret;
}
ret = ath10k_bmi_write32(ar, hi_serial_enable, 1); if (ret) {
ath10k_warn(ar, "could not enable UART prints (%d)\n", ret); return ret;
}
/* Set the UART baud rate to 19200. */
ret = ath10k_bmi_write32(ar, hi_desired_baud_rate, 19200); if (ret) {
ath10k_warn(ar, "could not set the baud rate (%d)\n", ret); return ret;
}
ath10k_info(ar, "UART prints enabled\n"); return 0;
}
staticint ath10k_init_hw_params(struct ath10k *ar)
{ conststruct ath10k_hw_params *hw_params; int i;
for (i = 0; i < ARRAY_SIZE(ath10k_hw_params_list); i++) {
hw_params = &ath10k_hw_params_list[i];
if (i == ARRAY_SIZE(ath10k_hw_params_list)) {
ath10k_err(ar, "Unsupported hardware version: 0x%x\n",
ar->target_version); return -EINVAL;
}
ar->hw_params = *hw_params;
ath10k_dbg(ar, ATH10K_DBG_BOOT, "Hardware name %s version 0x%x\n",
ar->hw_params.name, ar->target_version);
return 0;
}
staticbool ath10k_core_needs_recovery(struct ath10k *ar)
{ long time_left;
/* Sometimes the recovery will fail and then the next all recovery fail, * so avoid infinite recovery.
*/ if (atomic_read(&ar->fail_cont_count) >= ATH10K_RECOVERY_MAX_FAIL_COUNT) {
ath10k_err(ar, "consecutive fail %d times, will shutdown driver!",
atomic_read(&ar->fail_cont_count));
ar->state = ATH10K_STATE_WEDGED; returnfalse;
}
if (atomic_read(&ar->pending_recovery)) { /* Sometimes it happened another recovery work before the previous one * completed, then the second recovery work will destroy the previous * one, thus below is to avoid that.
*/
time_left = wait_for_completion_timeout(&ar->driver_recovery,
ATH10K_RECOVERY_TIMEOUT_HZ); if (time_left) {
ath10k_warn(ar, "previous recovery succeeded, skip this!\n"); returnfalse;
}
/* Record the continuous recovery fail count when recovery failed. */
atomic_inc(&ar->fail_cont_count);
/* Avoid having multiple recoveries at the same time. */ returnfalse;
}
atomic_inc(&ar->pending_recovery);
returntrue;
}
void ath10k_core_start_recovery(struct ath10k *ar)
{ if (!ath10k_core_needs_recovery(ar)) return;
/* TODO: We can have one instance of cancelling coverage_class_work by * moving it to ath10k_halt(), so that both stop() and restart() would * call that but it takes conf_mutex() and if we call cancel_work_sync() * with conf_mutex it will deadlock.
*/
cancel_work_sync(&ar->set_coverage_class_work);
mutex_lock(&ar->conf_mutex);
switch (ar->state) { case ATH10K_STATE_ON:
ar->state = ATH10K_STATE_RESTARTING;
ath10k_halt(ar);
ath10k_scan_finish(ar);
ieee80211_restart_hw(ar->hw); break; case ATH10K_STATE_OFF: /* this can happen if driver is being unloaded * or if the crash happens during FW probing
*/
ath10k_warn(ar, "cannot restart a device that hasn't been started\n"); break; case ATH10K_STATE_RESTARTING: /* hw restart might be requested from multiple places */ break; case ATH10K_STATE_RESTARTED:
ar->state = ATH10K_STATE_WEDGED;
fallthrough; case ATH10K_STATE_WEDGED:
ath10k_warn(ar, "device is wedged, will not restart\n"); break; case ATH10K_STATE_UTF:
ath10k_warn(ar, "firmware restart in UTF mode not supported\n"); break;
}
mutex_unlock(&ar->conf_mutex);
ret = ath10k_coredump_submit(ar); if (ret)
ath10k_warn(ar, "failed to send firmware crash dump via devcoredump: %d",
ret);
}
if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, fw_file->fw_features) &&
!test_bit(ATH10K_FW_FEATURE_WMI_10X, fw_file->fw_features)) {
ath10k_err(ar, "feature bits corrupted: 10.2 feature requires 10.x feature to be set as well"); return -EINVAL;
}
if (fw_file->wmi_op_version >= ATH10K_FW_WMI_OP_VERSION_MAX) {
ath10k_err(ar, "unsupported WMI OP version (max %d): %d\n",
ATH10K_FW_WMI_OP_VERSION_MAX, fw_file->wmi_op_version); return -EINVAL;
}
ar->wmi.rx_decap_mode = ATH10K_HW_TXRX_NATIVE_WIFI; switch (ath10k_cryptmode_param) { case ATH10K_CRYPT_MODE_HW:
clear_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags);
clear_bit(ATH10K_FLAG_HW_CRYPTO_DISABLED, &ar->dev_flags); break; case ATH10K_CRYPT_MODE_SW: if (!test_bit(ATH10K_FW_FEATURE_RAW_MODE_SUPPORT,
fw_file->fw_features)) {
ath10k_err(ar, "cryptmode > 0 requires raw mode support from firmware"); return -EINVAL;
}
if (ath10k_frame_mode == ATH10K_HW_TXRX_RAW) { if (!test_bit(ATH10K_FW_FEATURE_RAW_MODE_SUPPORT,
fw_file->fw_features)) {
ath10k_err(ar, "rawmode = 1 requires support from firmware"); return -EINVAL;
}
set_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags);
}
if (test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) {
ar->wmi.rx_decap_mode = ATH10K_HW_TXRX_RAW;
/* Workaround: * * Firmware A-MSDU aggregation breaks with RAW Tx encap mode * and causes enormous performance issues (malformed frames, * etc). * * Disabling A-MSDU makes RAW mode stable with heavy traffic * albeit a bit slower compared to regular operation.
*/
ar->htt.max_num_amsdu = 1;
}
/* Backwards compatibility for firmwares without * ATH10K_FW_IE_WMI_OP_VERSION.
*/ if (fw_file->wmi_op_version == ATH10K_FW_WMI_OP_VERSION_UNSET) { if (test_bit(ATH10K_FW_FEATURE_WMI_10X, fw_file->fw_features)) { if (test_bit(ATH10K_FW_FEATURE_WMI_10_2,
fw_file->fw_features))
fw_file->wmi_op_version = ATH10K_FW_WMI_OP_VERSION_10_2; else
fw_file->wmi_op_version = ATH10K_FW_WMI_OP_VERSION_10_1;
} else {
fw_file->wmi_op_version = ATH10K_FW_WMI_OP_VERSION_MAIN;
}
}
if (test_bit(ATH10K_FW_FEATURE_PEER_FLOW_CONTROL,
fw_file->fw_features))
ar->htt.max_num_pending_tx = TARGET_10_4_NUM_MSDU_DESC_PFC; else
ar->htt.max_num_pending_tx = TARGET_10_4_NUM_MSDU_DESC; break; case ATH10K_FW_WMI_OP_VERSION_UNSET: case ATH10K_FW_WMI_OP_VERSION_MAX: default:
WARN_ON(1); return -EINVAL;
}
if (ar->hw_params.num_peers)
ar->max_num_peers = ar->hw_params.num_peers; else
ar->max_num_peers = max_num_peers;
/* Backwards compatibility for firmwares without * ATH10K_FW_IE_HTT_OP_VERSION.
*/ if (fw_file->htt_op_version == ATH10K_FW_HTT_OP_VERSION_UNSET) { switch (fw_file->wmi_op_version) { case ATH10K_FW_WMI_OP_VERSION_MAIN:
fw_file->htt_op_version = ATH10K_FW_HTT_OP_VERSION_MAIN; break; case ATH10K_FW_WMI_OP_VERSION_10_1: case ATH10K_FW_WMI_OP_VERSION_10_2: case ATH10K_FW_WMI_OP_VERSION_10_2_4:
fw_file->htt_op_version = ATH10K_FW_HTT_OP_VERSION_10_1; break; case ATH10K_FW_WMI_OP_VERSION_TLV:
fw_file->htt_op_version = ATH10K_FW_HTT_OP_VERSION_TLV; break; case ATH10K_FW_WMI_OP_VERSION_10_4: case ATH10K_FW_WMI_OP_VERSION_UNSET: case ATH10K_FW_WMI_OP_VERSION_MAX:
ath10k_err(ar, "htt op version not found from fw meta data"); return -EINVAL;
}
}
return 0;
}
staticint ath10k_core_reset_rx_filter(struct ath10k *ar)
{ int ret; int vdev_id; int vdev_type; int vdev_subtype; const u8 *vdev_addr;
ret = ath10k_wmi_vdev_create(ar, vdev_id, vdev_type, vdev_subtype,
vdev_addr); if (ret) {
ath10k_err(ar, "failed to create dummy vdev: %d\n", ret); return ret;
}
ret = ath10k_wmi_vdev_delete(ar, vdev_id); if (ret) {
ath10k_err(ar, "failed to delete dummy vdev: %d\n", ret); return ret;
}
/* WMI and HTT may use separate HIF pipes and are not guaranteed to be * serialized properly implicitly. * * Moreover (most) WMI commands have no explicit acknowledges. It is * possible to infer it implicitly by poking firmware with echo * command - getting a reply means all preceding comments have been * (mostly) processed. * * In case of vdev create/delete this is sufficient. * * Without this it's possible to end up with a race when HTT Rx ring is * started before vdev create/delete hack is complete allowing a short * window of opportunity to receive (and Tx ACK) a bunch of frames.
*/
ret = ath10k_wmi_barrier(ar); if (ret) {
ath10k_err(ar, "failed to ping firmware: %d\n", ret); return ret;
}
/* all 10.x firmware versions support thermal throttling but don't * advertise the support via service flags so we have to hardcode * it here
*/ switch (fw_file->wmi_op_version) { case ATH10K_FW_WMI_OP_VERSION_10_1: case ATH10K_FW_WMI_OP_VERSION_10_2: case ATH10K_FW_WMI_OP_VERSION_10_2_4: case ATH10K_FW_WMI_OP_VERSION_10_4:
set_bit(WMI_SERVICE_THERM_THROT, ar->wmi.svc_map); break; default: break;
}
/* copy target iram feature must work also when * ATH10K_FW_CRASH_DUMP_RAM_DATA is disabled, so * _ath10k_coredump_get_mem_layout() to accomplist that
*/
hw_mem = _ath10k_coredump_get_mem_layout(ar); if (!hw_mem) /* if CONFIG_DEV_COREDUMP is disabled we get NULL, then * just silently disable the feature by doing nothing
*/ return 0;
for (i = 0; i < hw_mem->region_table.size; i++) {
tmp = &hw_mem->region_table.regions[i]; if (tmp->type == ATH10K_MEM_REGION_TYPE_REG) {
mem_region = tmp; break;
}
}
if (!mem_region) return -ENOMEM;
for (i = 0; i < ar->wmi.num_mem_chunks; i++) { if (ar->wmi.mem_chunks[i].req_id ==
WMI_IRAM_RECOVERY_HOST_MEM_REQ_ID) {
vaddr = ar->wmi.mem_chunks[i].vaddr;
len = ar->wmi.mem_chunks[i].len; break;
}
}
if (!vaddr || !len) {
ath10k_warn(ar, "No allocated memory for IRAM back up"); return -ENOMEM;
}
len = (len < mem_region->len) ? len : mem_region->len;
paddr = mem_region->start;
num_read_itr = len / TGT_IRAM_READ_PER_ITR;
remaining_len = len % TGT_IRAM_READ_PER_ITR; for (i = 0; i < num_read_itr; i++) {
ret = ath10k_hif_diag_read(ar, paddr, vaddr,
TGT_IRAM_READ_PER_ITR); if (ret) {
ath10k_warn(ar, "failed to copy firmware IRAM contents: %d",
ret); return ret;
}
if (!test_bit(ATH10K_FW_FEATURE_NON_BMI,
ar->running_fw->fw_file.fw_features)) {
ath10k_bmi_start(ar);
/* Enable hardware clock to speed up firmware download */ if (ar->hw_params.hw_ops->enable_pll_clk) {
status = ar->hw_params.hw_ops->enable_pll_clk(ar);
ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot enable pll ret %d\n",
status);
}
if (ath10k_init_configure_target(ar)) {
status = -EINVAL; goto err;
}
status = ath10k_download_cal_data(ar); if (status) goto err;
/* Some of qca988x solutions are having global reset issue * during target initialization. Bypassing PLL setting before * downloading firmware and letting the SoC run on REF_CLK is * fixing the problem. Corresponding firmware change is also * needed to set the clock source once the target is * initialized.
*/ if (test_bit(ATH10K_FW_FEATURE_SUPPORTS_SKIP_CLOCK_INIT,
ar->running_fw->fw_file.fw_features)) {
status = ath10k_bmi_write32(ar, hi_skip_clock_init, 1); if (status) {
ath10k_err(ar, "could not write to skip_clock_init: %d\n",
status); goto err;
}
}
status = ath10k_download_fw(ar); if (status) goto err;
status = ath10k_init_uart(ar); if (status) goto err;
if (ar->hif.bus == ATH10K_BUS_SDIO) {
status = ath10k_init_sdio(ar, mode); if (status) {
ath10k_err(ar, "failed to init SDIO: %d\n", status); goto err;
}
}
}
status = ath10k_htc_init(ar); if (status) {
ath10k_err(ar, "could not init HTC (%d)\n", status); goto err;
}
if (!test_bit(ATH10K_FW_FEATURE_NON_BMI,
ar->running_fw->fw_file.fw_features)) {
status = ath10k_bmi_done(ar); if (status) goto err;
}
status = ath10k_wmi_attach(ar); if (status) {
ath10k_err(ar, "WMI attach failed: %d\n", status); goto err;
}
status = ath10k_htt_init(ar); if (status) {
ath10k_err(ar, "failed to init htt: %d\n", status); goto err_wmi_detach;
}
status = ath10k_htt_tx_start(&ar->htt); if (status) {
ath10k_err(ar, "failed to alloc htt tx: %d\n", status); goto err_wmi_detach;
}
/* If firmware indicates Full Rx Reorder support it must be used in a * slightly different manner. Let HTT code know.
*/
ar->htt.rx_ring.in_ord_rx = !!(test_bit(WMI_SERVICE_RX_FULL_REORDER,
ar->wmi.svc_map));
status = ath10k_htt_rx_alloc(&ar->htt); if (status) {
ath10k_err(ar, "failed to alloc htt rx: %d\n", status); goto err_htt_tx_detach;
}
status = ath10k_hif_start(ar); if (status) {
ath10k_err(ar, "could not start HIF: %d\n", status); goto err_htt_rx_detach;
}
status = ath10k_htc_wait_target(&ar->htc); if (status) {
ath10k_err(ar, "failed to connect to HTC: %d\n", status); goto err_hif_stop;
}
status = ath10k_hif_start_post(ar); if (status) {
ath10k_err(ar, "failed to swap mailbox: %d\n", status); goto err_hif_stop;
}
if (mode == ATH10K_FIRMWARE_MODE_NORMAL) {
status = ath10k_htt_connect(&ar->htt); if (status) {
ath10k_err(ar, "failed to connect htt (%d)\n", status); goto err_hif_stop;
}
}
status = ath10k_wmi_connect(ar); if (status) {
ath10k_err(ar, "could not connect wmi: %d\n", status); goto err_hif_stop;
}
status = ath10k_htc_start(&ar->htc); if (status) {
ath10k_err(ar, "failed to start htc: %d\n", status); goto err_hif_stop;
}
if (mode == ATH10K_FIRMWARE_MODE_NORMAL) {
status = ath10k_wmi_wait_for_service_ready(ar); if (status) {
ath10k_warn(ar, "wmi service ready event not received"); goto err_hif_stop;
}
}
if (test_bit(ATH10K_FW_FEATURE_IRAM_RECOVERY,
ar->running_fw->fw_file.fw_features)) {
status = ath10k_core_copy_target_iram(ar); if (status) {
ath10k_warn(ar, "failed to copy target iram contents: %d",
status); goto err_hif_stop;
}
}
if (test_bit(WMI_SERVICE_EXT_RES_CFG_SUPPORT, ar->wmi.svc_map) &&
mode == ATH10K_FIRMWARE_MODE_NORMAL) {
val = 0; if (ath10k_peer_stats_enabled(ar))
val = WMI_10_4_PEER_STATS;
/* Enable vdev stats by default */
val |= WMI_10_4_VDEV_STATS;
if (test_bit(WMI_SERVICE_BSS_CHANNEL_INFO_64, ar->wmi.svc_map))
val |= WMI_10_4_BSS_CHANNEL_INFO_64;
ath10k_core_fetch_btcoex_dt(ar);
/* 10.4 firmware supports BT-Coex without reloading firmware * via pdev param. To support Bluetooth coexistence pdev param, * WMI_COEX_GPIO_SUPPORT of extended resource config should be * enabled always. * * We can still enable BTCOEX if firmware has the support * even though btceox_support value is * ATH10K_DT_BTCOEX_NOT_FOUND
*/
if (test_bit(WMI_SERVICE_COEX_GPIO, ar->wmi.svc_map) &&
test_bit(ATH10K_FW_FEATURE_BTCOEX_PARAM,
ar->running_fw->fw_file.fw_features) &&
ar->coex_support)
val |= WMI_10_4_COEX_GPIO_SUPPORT;
if (test_bit(WMI_SERVICE_TDLS_EXPLICIT_MODE_ONLY,
ar->wmi.svc_map))
val |= WMI_10_4_TDLS_EXPLICIT_MODE_ONLY;
if (test_bit(WMI_SERVICE_TDLS_UAPSD_BUFFER_STA,
ar->wmi.svc_map))
val |= WMI_10_4_TDLS_UAPSD_BUFFER_STA;
if (test_bit(WMI_SERVICE_TX_DATA_ACK_RSSI,
ar->wmi.svc_map))
val |= WMI_10_4_TX_DATA_ACK_RSSI;
if (test_bit(WMI_SERVICE_REPORT_AIRTIME, ar->wmi.svc_map))
val |= WMI_10_4_REPORT_AIRTIME;
if (test_bit(WMI_SERVICE_EXT_PEER_TID_CONFIGS_SUPPORT,
ar->wmi.svc_map))
val |= WMI_10_4_EXT_PEER_TID_CONFIGS_SUPPORT;
status = ath10k_mac_ext_resource_config(ar, val); if (status) {
ath10k_err(ar, "failed to send ext resource cfg command : %d\n",
status); goto err_hif_stop;
}
}
status = ath10k_wmi_cmd_init(ar); if (status) {
ath10k_err(ar, "could not send WMI init command (%d)\n",
status); goto err_hif_stop;
}
status = ath10k_wmi_wait_for_unified_ready(ar); if (status) {
ath10k_err(ar, "wmi unified ready event not received\n"); goto err_hif_stop;
}
status = ath10k_core_compat_services(ar); if (status) {
ath10k_err(ar, "compat services failed: %d\n", status); goto err_hif_stop;
}
status = ath10k_wmi_pdev_set_base_macaddr(ar, ar->mac_addr); if (status && status != -EOPNOTSUPP) {
ath10k_err(ar, "failed to set base mac address: %d\n", status); goto err_hif_stop;
}
/* Some firmware revisions do not properly set up hardware rx filter * registers. * * A known example from QCA9880 and 10.2.4 is that MAC_PCU_ADDR1_MASK * is filled with 0s instead of 1s allowing HW to respond with ACKs to * any frames that matches MAC_PCU_RX_FILTER which is also * misconfigured to accept anything. * * The ADDR1 is programmed using internal firmware structure field and * can't be (easily/sanely) reached from the driver explicitly. It is * possible to implicitly make it correct by creating a dummy vdev and * then deleting it.
*/ if (ar->hw_params.hw_filter_reset_required &&
mode == ATH10K_FIRMWARE_MODE_NORMAL) {
status = ath10k_core_reset_rx_filter(ar); if (status) {
ath10k_err(ar, "failed to reset rx filter: %d\n", status); goto err_hif_stop;
}
}
status = ath10k_htt_rx_ring_refill(ar); if (status) {
ath10k_err(ar, "failed to refill htt rx ring: %d\n", status); goto err_hif_stop;
}
/* we don't care about HTT in UTF mode */ if (mode == ATH10K_FIRMWARE_MODE_NORMAL) {
status = ath10k_htt_setup(&ar->htt); if (status) {
ath10k_err(ar, "failed to setup htt: %d\n", status); goto err_hif_stop;
}
}
status = ath10k_debug_start(ar); if (status) goto err_hif_stop;
status = ath10k_hif_set_target_log_mode(ar, fw_diag_log); if (status && status != -EOPNOTSUPP) {
ath10k_warn(ar, "set target log mode failed: %d\n", status); goto err_hif_stop;
}
status = ath10k_leds_start(ar); if (status) goto err_hif_stop;
/* mac80211 manages fw/hw initialization through start/stop hooks. However in * order to know what hw capabilities should be advertised to mac80211 it is * necessary to load the firmware (and tear it down immediately since start * hook will try to init it again) before registering
*/ staticint ath10k_core_probe_fw(struct ath10k *ar)
{ struct bmi_target_info target_info; int ret = 0;
ret = ath10k_hif_power_up(ar, ATH10K_FIRMWARE_MODE_NORMAL); if (ret) {
ath10k_err(ar, "could not power on hif bus (%d)\n", ret); return ret;
}
switch (ar->hif.bus) { case ATH10K_BUS_SDIO:
memset(&target_info, 0, sizeof(target_info));
ret = ath10k_bmi_get_target_info_sdio(ar, &target_info); if (ret) {
ath10k_err(ar, "could not get target info (%d)\n", ret); goto err_power_down;
}
ar->target_version = target_info.version;
ar->hw->wiphy->hw_version = target_info.version; break; case ATH10K_BUS_PCI: case ATH10K_BUS_AHB: case ATH10K_BUS_USB:
memset(&target_info, 0, sizeof(target_info));
ret = ath10k_bmi_get_target_info(ar, &target_info); if (ret) {
ath10k_err(ar, "could not get target info (%d)\n", ret); goto err_power_down;
}
ar->target_version = target_info.version;
ar->hw->wiphy->hw_version = target_info.version; break; case ATH10K_BUS_SNOC:
memset(&target_info, 0, sizeof(target_info));
ret = ath10k_hif_get_target_info(ar, &target_info); if (ret) {
ath10k_err(ar, "could not get target info (%d)\n", ret); goto err_power_down;
}
ar->target_version = target_info.version;
ar->hw->wiphy->hw_version = target_info.version; break; default:
ath10k_err(ar, "incorrect hif bus type: %d\n", ar->hif.bus);
}
ret = ath10k_init_hw_params(ar); if (ret) {
ath10k_err(ar, "could not get hw params (%d)\n", ret); goto err_power_down;
}
ret = ath10k_core_fetch_firmware_files(ar); if (ret) {
ath10k_err(ar, "could not fetch firmware files (%d)\n", ret); goto err_power_down;
}
if (!test_bit(ATH10K_FW_FEATURE_NON_BMI,
ar->normal_mode_fw.fw_file.fw_features)) {
ret = ath10k_core_pre_cal_download(ar); if (ret) { /* pre calibration data download is not necessary * for all the chipsets. Ignore failures and continue.
*/
ath10k_dbg(ar, ATH10K_DBG_BOOT, "could not load pre cal data: %d\n", ret);
}
ret = ath10k_core_get_board_id_from_otp(ar); if (ret && ret != -EOPNOTSUPP) {
ath10k_err(ar, "failed to get board id from otp: %d\n",
ret); goto err_free_firmware_files;
}
ret = ath10k_core_check_smbios(ar); if (ret)
ath10k_dbg(ar, ATH10K_DBG_BOOT, "SMBIOS bdf variant name not set.\n");
ret = ath10k_core_check_dt(ar); if (ret)
ath10k_dbg(ar, ATH10K_DBG_BOOT, "DT bdf variant name not set.\n");
ret = ath10k_core_fetch_board_file(ar, ATH10K_BD_IE_BOARD); if (ret) {
ath10k_err(ar, "failed to fetch board file: %d\n", ret); goto err_free_firmware_files;
}
ath10k_debug_print_board_info(ar);
}
device_get_mac_address(ar->dev, ar->mac_addr);
ret = ath10k_core_init_firmware_features(ar); if (ret) {
ath10k_err(ar, "fatal problem with firmware features: %d\n",
ret); goto err_free_firmware_files;
}
if (!test_bit(ATH10K_FW_FEATURE_NON_BMI,
ar->normal_mode_fw.fw_file.fw_features)) {
ret = ath10k_swap_code_seg_init(ar,
&ar->normal_mode_fw.fw_file); if (ret) {
ath10k_err(ar, "failed to initialize code swap segment: %d\n",
ret); goto err_free_firmware_files;
}
}
mutex_lock(&ar->conf_mutex);
ret = ath10k_core_start(ar, ATH10K_FIRMWARE_MODE_NORMAL,
&ar->normal_mode_fw); if (ret) {
ath10k_err(ar, "could not init core (%d)\n", ret); goto err_unlock;
}
err_thermal_unregister:
ath10k_thermal_unregister(ar);
err_spectral_destroy:
ath10k_spectral_destroy(ar);
err_debug_destroy:
ath10k_debug_destroy(ar);
err_unregister_coredump:
ath10k_coredump_unregister(ar);
err_unregister_mac:
ath10k_mac_unregister(ar);
err_release_fw:
ath10k_core_free_firmware_files(ar);
err: /* TODO: It's probably a good idea to release device from the driver * but calling device_release_driver() here will cause a deadlock.
*/ return;
}
if (!test_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags)) return;
ath10k_leds_unregister(ar);
ath10k_thermal_unregister(ar); /* Stop spectral before unregistering from mac80211 to remove the * relayfs debugfs file cleanly. Otherwise the parent debugfs tree * would be already be free'd recursively, leading to a double free.
*/
ath10k_spectral_destroy(ar);
/* We must unregister from mac80211 before we stop HTC and HIF. * Otherwise we will fail to submit commands to FW and mac80211 will be * unhappy about callback failures.
*/
ath10k_mac_unregister(ar);
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.