162306a36Sopenharmony_ci// SPDX-License-Identifier: ISC 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2005-2011 Atheros Communications Inc. 462306a36Sopenharmony_ci * Copyright (c) 2011-2014,2016-2017 Qualcomm Atheros, Inc. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include "bmi.h" 862306a36Sopenharmony_ci#include "hif.h" 962306a36Sopenharmony_ci#include "debug.h" 1062306a36Sopenharmony_ci#include "htc.h" 1162306a36Sopenharmony_ci#include "hw.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_civoid ath10k_bmi_start(struct ath10k *ar) 1462306a36Sopenharmony_ci{ 1562306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi start\n"); 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci ar->bmi.done_sent = false; 1862306a36Sopenharmony_ci} 1962306a36Sopenharmony_ciEXPORT_SYMBOL(ath10k_bmi_start); 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ciint ath10k_bmi_done(struct ath10k *ar) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci struct bmi_cmd cmd; 2462306a36Sopenharmony_ci u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.done); 2562306a36Sopenharmony_ci int ret; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi done\n"); 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci if (ar->bmi.done_sent) { 3062306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi skipped\n"); 3162306a36Sopenharmony_ci return 0; 3262306a36Sopenharmony_ci } 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci ar->bmi.done_sent = true; 3562306a36Sopenharmony_ci cmd.id = __cpu_to_le32(BMI_DONE); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL); 3862306a36Sopenharmony_ci if (ret) { 3962306a36Sopenharmony_ci ath10k_warn(ar, "unable to write to the device: %d\n", ret); 4062306a36Sopenharmony_ci return ret; 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci return 0; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ciint ath10k_bmi_get_target_info(struct ath10k *ar, 4762306a36Sopenharmony_ci struct bmi_target_info *target_info) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct bmi_cmd cmd; 5062306a36Sopenharmony_ci union bmi_resp resp; 5162306a36Sopenharmony_ci u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.get_target_info); 5262306a36Sopenharmony_ci u32 resplen = sizeof(resp.get_target_info); 5362306a36Sopenharmony_ci int ret; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi get target info\n"); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (ar->bmi.done_sent) { 5862306a36Sopenharmony_ci ath10k_warn(ar, "BMI Get Target Info Command disallowed\n"); 5962306a36Sopenharmony_ci return -EBUSY; 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci cmd.id = __cpu_to_le32(BMI_GET_TARGET_INFO); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen); 6562306a36Sopenharmony_ci if (ret) { 6662306a36Sopenharmony_ci ath10k_warn(ar, "unable to get target info from device\n"); 6762306a36Sopenharmony_ci return ret; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (resplen < sizeof(resp.get_target_info)) { 7162306a36Sopenharmony_ci ath10k_warn(ar, "invalid get_target_info response length (%d)\n", 7262306a36Sopenharmony_ci resplen); 7362306a36Sopenharmony_ci return -EIO; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci target_info->version = __le32_to_cpu(resp.get_target_info.version); 7762306a36Sopenharmony_ci target_info->type = __le32_to_cpu(resp.get_target_info.type); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci return 0; 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci#define TARGET_VERSION_SENTINAL 0xffffffffu 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ciint ath10k_bmi_get_target_info_sdio(struct ath10k *ar, 8562306a36Sopenharmony_ci struct bmi_target_info *target_info) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct bmi_cmd cmd; 8862306a36Sopenharmony_ci union bmi_resp resp; 8962306a36Sopenharmony_ci u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.get_target_info); 9062306a36Sopenharmony_ci u32 resplen, ver_len; 9162306a36Sopenharmony_ci __le32 tmp; 9262306a36Sopenharmony_ci int ret; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi get target info SDIO\n"); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (ar->bmi.done_sent) { 9762306a36Sopenharmony_ci ath10k_warn(ar, "BMI Get Target Info Command disallowed\n"); 9862306a36Sopenharmony_ci return -EBUSY; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci cmd.id = __cpu_to_le32(BMI_GET_TARGET_INFO); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* Step 1: Read 4 bytes of the target info and check if it is 10462306a36Sopenharmony_ci * the special sentinel version word or the first word in the 10562306a36Sopenharmony_ci * version response. 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_ci resplen = sizeof(u32); 10862306a36Sopenharmony_ci ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &tmp, &resplen); 10962306a36Sopenharmony_ci if (ret) { 11062306a36Sopenharmony_ci ath10k_warn(ar, "unable to read from device\n"); 11162306a36Sopenharmony_ci return ret; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* Some SDIO boards have a special sentinel byte before the real 11562306a36Sopenharmony_ci * version response. 11662306a36Sopenharmony_ci */ 11762306a36Sopenharmony_ci if (__le32_to_cpu(tmp) == TARGET_VERSION_SENTINAL) { 11862306a36Sopenharmony_ci /* Step 1b: Read the version length */ 11962306a36Sopenharmony_ci resplen = sizeof(u32); 12062306a36Sopenharmony_ci ret = ath10k_hif_exchange_bmi_msg(ar, NULL, 0, &tmp, 12162306a36Sopenharmony_ci &resplen); 12262306a36Sopenharmony_ci if (ret) { 12362306a36Sopenharmony_ci ath10k_warn(ar, "unable to read from device\n"); 12462306a36Sopenharmony_ci return ret; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci ver_len = __le32_to_cpu(tmp); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* Step 2: Check the target info length */ 13162306a36Sopenharmony_ci if (ver_len != sizeof(resp.get_target_info)) { 13262306a36Sopenharmony_ci ath10k_warn(ar, "Unexpected target info len: %u. Expected: %zu\n", 13362306a36Sopenharmony_ci ver_len, sizeof(resp.get_target_info)); 13462306a36Sopenharmony_ci return -EINVAL; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci /* Step 3: Read the rest of the version response */ 13862306a36Sopenharmony_ci resplen = sizeof(resp.get_target_info) - sizeof(u32); 13962306a36Sopenharmony_ci ret = ath10k_hif_exchange_bmi_msg(ar, NULL, 0, 14062306a36Sopenharmony_ci &resp.get_target_info.version, 14162306a36Sopenharmony_ci &resplen); 14262306a36Sopenharmony_ci if (ret) { 14362306a36Sopenharmony_ci ath10k_warn(ar, "unable to read from device\n"); 14462306a36Sopenharmony_ci return ret; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci target_info->version = __le32_to_cpu(resp.get_target_info.version); 14862306a36Sopenharmony_ci target_info->type = __le32_to_cpu(resp.get_target_info.type); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci return 0; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ciint ath10k_bmi_read_memory(struct ath10k *ar, 15462306a36Sopenharmony_ci u32 address, void *buffer, u32 length) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct bmi_cmd cmd; 15762306a36Sopenharmony_ci union bmi_resp resp; 15862306a36Sopenharmony_ci u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.read_mem); 15962306a36Sopenharmony_ci u32 rxlen; 16062306a36Sopenharmony_ci int ret; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi read address 0x%x length %d\n", 16362306a36Sopenharmony_ci address, length); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (ar->bmi.done_sent) { 16662306a36Sopenharmony_ci ath10k_warn(ar, "command disallowed\n"); 16762306a36Sopenharmony_ci return -EBUSY; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci while (length) { 17162306a36Sopenharmony_ci rxlen = min_t(u32, length, BMI_MAX_DATA_SIZE); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci cmd.id = __cpu_to_le32(BMI_READ_MEMORY); 17462306a36Sopenharmony_ci cmd.read_mem.addr = __cpu_to_le32(address); 17562306a36Sopenharmony_ci cmd.read_mem.len = __cpu_to_le32(rxlen); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, 17862306a36Sopenharmony_ci &resp, &rxlen); 17962306a36Sopenharmony_ci if (ret) { 18062306a36Sopenharmony_ci ath10k_warn(ar, "unable to read from the device (%d)\n", 18162306a36Sopenharmony_ci ret); 18262306a36Sopenharmony_ci return ret; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci memcpy(buffer, resp.read_mem.payload, rxlen); 18662306a36Sopenharmony_ci address += rxlen; 18762306a36Sopenharmony_ci buffer += rxlen; 18862306a36Sopenharmony_ci length -= rxlen; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci return 0; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ciEXPORT_SYMBOL(ath10k_bmi_read_memory); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ciint ath10k_bmi_write_soc_reg(struct ath10k *ar, u32 address, u32 reg_val) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct bmi_cmd cmd; 19862306a36Sopenharmony_ci u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.write_soc_reg); 19962306a36Sopenharmony_ci int ret; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_BMI, 20262306a36Sopenharmony_ci "bmi write soc register 0x%08x val 0x%08x\n", 20362306a36Sopenharmony_ci address, reg_val); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (ar->bmi.done_sent) { 20662306a36Sopenharmony_ci ath10k_warn(ar, "bmi write soc register command in progress\n"); 20762306a36Sopenharmony_ci return -EBUSY; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci cmd.id = __cpu_to_le32(BMI_WRITE_SOC_REGISTER); 21162306a36Sopenharmony_ci cmd.write_soc_reg.addr = __cpu_to_le32(address); 21262306a36Sopenharmony_ci cmd.write_soc_reg.value = __cpu_to_le32(reg_val); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL); 21562306a36Sopenharmony_ci if (ret) { 21662306a36Sopenharmony_ci ath10k_warn(ar, "Unable to write soc register to device: %d\n", 21762306a36Sopenharmony_ci ret); 21862306a36Sopenharmony_ci return ret; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci return 0; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ciint ath10k_bmi_read_soc_reg(struct ath10k *ar, u32 address, u32 *reg_val) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci struct bmi_cmd cmd; 22762306a36Sopenharmony_ci union bmi_resp resp; 22862306a36Sopenharmony_ci u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.read_soc_reg); 22962306a36Sopenharmony_ci u32 resplen = sizeof(resp.read_soc_reg); 23062306a36Sopenharmony_ci int ret; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi read soc register 0x%08x\n", 23362306a36Sopenharmony_ci address); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (ar->bmi.done_sent) { 23662306a36Sopenharmony_ci ath10k_warn(ar, "bmi read soc register command in progress\n"); 23762306a36Sopenharmony_ci return -EBUSY; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci cmd.id = __cpu_to_le32(BMI_READ_SOC_REGISTER); 24162306a36Sopenharmony_ci cmd.read_soc_reg.addr = __cpu_to_le32(address); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen); 24462306a36Sopenharmony_ci if (ret) { 24562306a36Sopenharmony_ci ath10k_warn(ar, "Unable to read soc register from device: %d\n", 24662306a36Sopenharmony_ci ret); 24762306a36Sopenharmony_ci return ret; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci *reg_val = __le32_to_cpu(resp.read_soc_reg.value); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi read soc register value 0x%08x\n", 25362306a36Sopenharmony_ci *reg_val); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci return 0; 25662306a36Sopenharmony_ci} 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ciint ath10k_bmi_write_memory(struct ath10k *ar, 25962306a36Sopenharmony_ci u32 address, const void *buffer, u32 length) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci struct bmi_cmd cmd; 26262306a36Sopenharmony_ci u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.write_mem); 26362306a36Sopenharmony_ci u32 txlen; 26462306a36Sopenharmony_ci int ret; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi write address 0x%x length %d\n", 26762306a36Sopenharmony_ci address, length); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (ar->bmi.done_sent) { 27062306a36Sopenharmony_ci ath10k_warn(ar, "command disallowed\n"); 27162306a36Sopenharmony_ci return -EBUSY; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci while (length) { 27562306a36Sopenharmony_ci txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* copy before roundup to avoid reading beyond buffer*/ 27862306a36Sopenharmony_ci memcpy(cmd.write_mem.payload, buffer, txlen); 27962306a36Sopenharmony_ci txlen = roundup(txlen, 4); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci cmd.id = __cpu_to_le32(BMI_WRITE_MEMORY); 28262306a36Sopenharmony_ci cmd.write_mem.addr = __cpu_to_le32(address); 28362306a36Sopenharmony_ci cmd.write_mem.len = __cpu_to_le32(txlen); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen, 28662306a36Sopenharmony_ci NULL, NULL); 28762306a36Sopenharmony_ci if (ret) { 28862306a36Sopenharmony_ci ath10k_warn(ar, "unable to write to the device (%d)\n", 28962306a36Sopenharmony_ci ret); 29062306a36Sopenharmony_ci return ret; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* fixup roundup() so `length` zeroes out for last chunk */ 29462306a36Sopenharmony_ci txlen = min(txlen, length); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci address += txlen; 29762306a36Sopenharmony_ci buffer += txlen; 29862306a36Sopenharmony_ci length -= txlen; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci return 0; 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ciint ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 param, u32 *result) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct bmi_cmd cmd; 30762306a36Sopenharmony_ci union bmi_resp resp; 30862306a36Sopenharmony_ci u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.execute); 30962306a36Sopenharmony_ci u32 resplen = sizeof(resp.execute); 31062306a36Sopenharmony_ci int ret; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi execute address 0x%x param 0x%x\n", 31362306a36Sopenharmony_ci address, param); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (ar->bmi.done_sent) { 31662306a36Sopenharmony_ci ath10k_warn(ar, "command disallowed\n"); 31762306a36Sopenharmony_ci return -EBUSY; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci cmd.id = __cpu_to_le32(BMI_EXECUTE); 32162306a36Sopenharmony_ci cmd.execute.addr = __cpu_to_le32(address); 32262306a36Sopenharmony_ci cmd.execute.param = __cpu_to_le32(param); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen); 32562306a36Sopenharmony_ci if (ret) { 32662306a36Sopenharmony_ci ath10k_warn(ar, "unable to read from the device\n"); 32762306a36Sopenharmony_ci return ret; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (resplen < sizeof(resp.execute)) { 33162306a36Sopenharmony_ci ath10k_warn(ar, "invalid execute response length (%d)\n", 33262306a36Sopenharmony_ci resplen); 33362306a36Sopenharmony_ci return -EIO; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci *result = __le32_to_cpu(resp.execute.result); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi execute result 0x%x\n", *result); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci return 0; 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistatic int ath10k_bmi_lz_data_large(struct ath10k *ar, const void *buffer, u32 length) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci struct bmi_cmd *cmd; 34662306a36Sopenharmony_ci u32 hdrlen = sizeof(cmd->id) + sizeof(cmd->lz_data); 34762306a36Sopenharmony_ci u32 txlen; 34862306a36Sopenharmony_ci int ret; 34962306a36Sopenharmony_ci size_t buf_len; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_BMI, "large bmi lz data buffer 0x%pK length %d\n", 35262306a36Sopenharmony_ci buffer, length); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci if (ar->bmi.done_sent) { 35562306a36Sopenharmony_ci ath10k_warn(ar, "command disallowed\n"); 35662306a36Sopenharmony_ci return -EBUSY; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci buf_len = sizeof(*cmd) + BMI_MAX_LARGE_DATA_SIZE - BMI_MAX_DATA_SIZE; 36062306a36Sopenharmony_ci cmd = kzalloc(buf_len, GFP_KERNEL); 36162306a36Sopenharmony_ci if (!cmd) 36262306a36Sopenharmony_ci return -ENOMEM; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci while (length) { 36562306a36Sopenharmony_ci txlen = min(length, BMI_MAX_LARGE_DATA_SIZE - hdrlen); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci WARN_ON_ONCE(txlen & 3); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci cmd->id = __cpu_to_le32(BMI_LZ_DATA); 37062306a36Sopenharmony_ci cmd->lz_data.len = __cpu_to_le32(txlen); 37162306a36Sopenharmony_ci memcpy(cmd->lz_data.payload, buffer, txlen); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci ret = ath10k_hif_exchange_bmi_msg(ar, cmd, hdrlen + txlen, 37462306a36Sopenharmony_ci NULL, NULL); 37562306a36Sopenharmony_ci if (ret) { 37662306a36Sopenharmony_ci ath10k_warn(ar, "unable to write to the device\n"); 37762306a36Sopenharmony_ci kfree(cmd); 37862306a36Sopenharmony_ci return ret; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci buffer += txlen; 38262306a36Sopenharmony_ci length -= txlen; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci kfree(cmd); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci return 0; 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ciint ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci struct bmi_cmd cmd; 39362306a36Sopenharmony_ci u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.lz_data); 39462306a36Sopenharmony_ci u32 txlen; 39562306a36Sopenharmony_ci int ret; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi lz data buffer 0x%pK length %d\n", 39862306a36Sopenharmony_ci buffer, length); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (ar->bmi.done_sent) { 40162306a36Sopenharmony_ci ath10k_warn(ar, "command disallowed\n"); 40262306a36Sopenharmony_ci return -EBUSY; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci while (length) { 40662306a36Sopenharmony_ci txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci WARN_ON_ONCE(txlen & 3); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci cmd.id = __cpu_to_le32(BMI_LZ_DATA); 41162306a36Sopenharmony_ci cmd.lz_data.len = __cpu_to_le32(txlen); 41262306a36Sopenharmony_ci memcpy(cmd.lz_data.payload, buffer, txlen); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen, 41562306a36Sopenharmony_ci NULL, NULL); 41662306a36Sopenharmony_ci if (ret) { 41762306a36Sopenharmony_ci ath10k_warn(ar, "unable to write to the device\n"); 41862306a36Sopenharmony_ci return ret; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci buffer += txlen; 42262306a36Sopenharmony_ci length -= txlen; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci return 0; 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ciint ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci struct bmi_cmd cmd; 43162306a36Sopenharmony_ci u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.lz_start); 43262306a36Sopenharmony_ci int ret; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi lz stream start address 0x%x\n", 43562306a36Sopenharmony_ci address); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (ar->bmi.done_sent) { 43862306a36Sopenharmony_ci ath10k_warn(ar, "command disallowed\n"); 43962306a36Sopenharmony_ci return -EBUSY; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci cmd.id = __cpu_to_le32(BMI_LZ_STREAM_START); 44362306a36Sopenharmony_ci cmd.lz_start.addr = __cpu_to_le32(address); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL); 44662306a36Sopenharmony_ci if (ret) { 44762306a36Sopenharmony_ci ath10k_warn(ar, "unable to Start LZ Stream to the device\n"); 44862306a36Sopenharmony_ci return ret; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci return 0; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ciint ath10k_bmi_fast_download(struct ath10k *ar, 45562306a36Sopenharmony_ci u32 address, const void *buffer, u32 length) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci u8 trailer[4] = {}; 45862306a36Sopenharmony_ci u32 head_len = rounddown(length, 4); 45962306a36Sopenharmony_ci u32 trailer_len = length - head_len; 46062306a36Sopenharmony_ci int ret; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci ath10k_dbg(ar, ATH10K_DBG_BMI, 46362306a36Sopenharmony_ci "bmi fast download address 0x%x buffer 0x%pK length %d\n", 46462306a36Sopenharmony_ci address, buffer, length); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci ret = ath10k_bmi_lz_stream_start(ar, address); 46762306a36Sopenharmony_ci if (ret) 46862306a36Sopenharmony_ci return ret; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci /* copy the last word into a zero padded buffer */ 47162306a36Sopenharmony_ci if (trailer_len > 0) 47262306a36Sopenharmony_ci memcpy(trailer, buffer + head_len, trailer_len); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci if (ar->hw_params.bmi_large_size_download) 47562306a36Sopenharmony_ci ret = ath10k_bmi_lz_data_large(ar, buffer, head_len); 47662306a36Sopenharmony_ci else 47762306a36Sopenharmony_ci ret = ath10k_bmi_lz_data(ar, buffer, head_len); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (ret) 48062306a36Sopenharmony_ci return ret; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci if (trailer_len > 0) 48362306a36Sopenharmony_ci ret = ath10k_bmi_lz_data(ar, trailer, 4); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci if (ret != 0) 48662306a36Sopenharmony_ci return ret; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci /* 48962306a36Sopenharmony_ci * Close compressed stream and open a new (fake) one. 49062306a36Sopenharmony_ci * This serves mainly to flush Target caches. 49162306a36Sopenharmony_ci */ 49262306a36Sopenharmony_ci ret = ath10k_bmi_lz_stream_start(ar, 0x00); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci return ret; 49562306a36Sopenharmony_ci} 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ciint ath10k_bmi_set_start(struct ath10k *ar, u32 address) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci struct bmi_cmd cmd; 50062306a36Sopenharmony_ci u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.set_app_start); 50162306a36Sopenharmony_ci int ret; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (ar->bmi.done_sent) { 50462306a36Sopenharmony_ci ath10k_warn(ar, "bmi set start command disallowed\n"); 50562306a36Sopenharmony_ci return -EBUSY; 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci cmd.id = __cpu_to_le32(BMI_SET_APP_START); 50962306a36Sopenharmony_ci cmd.set_app_start.addr = __cpu_to_le32(address); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL); 51262306a36Sopenharmony_ci if (ret) { 51362306a36Sopenharmony_ci ath10k_warn(ar, "unable to set start to the device:%d\n", ret); 51462306a36Sopenharmony_ci return ret; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci return 0; 51862306a36Sopenharmony_ci} 519