// 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;
}
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.