Path: blob/main/sys/contrib/dev/athk/ath10k/bmi.c
107787 views
// SPDX-License-Identifier: ISC1/*2* Copyright (c) 2005-2011 Atheros Communications Inc.3* Copyright (c) 2011-2014,2016-2017 Qualcomm Atheros, Inc.4* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.5* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.6*/78#include <linux/export.h>9#include "bmi.h"10#include "hif.h"11#include "debug.h"12#include "htc.h"13#include "hw.h"1415void ath10k_bmi_start(struct ath10k *ar)16{17ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi start\n");1819ar->bmi.done_sent = false;20}21EXPORT_SYMBOL(ath10k_bmi_start);2223int ath10k_bmi_done(struct ath10k *ar)24{25struct bmi_cmd cmd;26u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.done);27int ret;2829ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi done\n");3031if (ar->bmi.done_sent) {32ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi skipped\n");33return 0;34}3536ar->bmi.done_sent = true;37cmd.id = __cpu_to_le32(BMI_DONE);3839ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);40if (ret) {41ath10k_warn(ar, "unable to write to the device: %d\n", ret);42return ret;43}4445return 0;46}4748int ath10k_bmi_get_target_info(struct ath10k *ar,49struct bmi_target_info *target_info)50{51struct bmi_cmd cmd;52union bmi_resp resp;53u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.get_target_info);54u32 resplen = sizeof(resp.get_target_info);55int ret;5657ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi get target info\n");5859if (ar->bmi.done_sent) {60ath10k_warn(ar, "BMI Get Target Info Command disallowed\n");61return -EBUSY;62}6364cmd.id = __cpu_to_le32(BMI_GET_TARGET_INFO);6566ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);67if (ret) {68#if defined(__linux__)69ath10k_warn(ar, "unable to get target info from device\n");70#elif defined(__FreeBSD__)71ath10k_warn(ar, "unable to get target info from device: %d\n",72ret);73#endif74return ret;75}7677if (resplen < sizeof(resp.get_target_info)) {78ath10k_warn(ar, "invalid get_target_info response length (%d)\n",79resplen);80return -EIO;81}8283target_info->version = __le32_to_cpu(resp.get_target_info.version);84target_info->type = __le32_to_cpu(resp.get_target_info.type);8586return 0;87}8889#define TARGET_VERSION_SENTINAL 0xffffffffu9091int ath10k_bmi_get_target_info_sdio(struct ath10k *ar,92struct bmi_target_info *target_info)93{94struct bmi_cmd cmd;95union bmi_resp resp;96u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.get_target_info);97u32 resplen, ver_len;98__le32 tmp;99int ret;100101ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi get target info SDIO\n");102103if (ar->bmi.done_sent) {104ath10k_warn(ar, "BMI Get Target Info Command disallowed\n");105return -EBUSY;106}107108cmd.id = __cpu_to_le32(BMI_GET_TARGET_INFO);109110/* Step 1: Read 4 bytes of the target info and check if it is111* the special sentinel version word or the first word in the112* version response.113*/114resplen = sizeof(u32);115ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &tmp, &resplen);116if (ret) {117ath10k_warn(ar, "unable to read from device\n");118return ret;119}120121/* Some SDIO boards have a special sentinel byte before the real122* version response.123*/124if (__le32_to_cpu(tmp) == TARGET_VERSION_SENTINAL) {125/* Step 1b: Read the version length */126resplen = sizeof(u32);127ret = ath10k_hif_exchange_bmi_msg(ar, NULL, 0, &tmp,128&resplen);129if (ret) {130ath10k_warn(ar, "unable to read from device\n");131return ret;132}133}134135ver_len = __le32_to_cpu(tmp);136137/* Step 2: Check the target info length */138if (ver_len != sizeof(resp.get_target_info)) {139ath10k_warn(ar, "Unexpected target info len: %u. Expected: %zu\n",140ver_len, sizeof(resp.get_target_info));141return -EINVAL;142}143144/* Step 3: Read the rest of the version response */145resplen = sizeof(resp.get_target_info) - sizeof(u32);146ret = ath10k_hif_exchange_bmi_msg(ar, NULL, 0,147&resp.get_target_info.version,148&resplen);149if (ret) {150ath10k_warn(ar, "unable to read from device\n");151return ret;152}153154target_info->version = __le32_to_cpu(resp.get_target_info.version);155target_info->type = __le32_to_cpu(resp.get_target_info.type);156157return 0;158}159160int ath10k_bmi_read_memory(struct ath10k *ar,161#if defined(__linux__)162u32 address, void *buffer, u32 length)163#elif defined(__FreeBSD__)164u32 address, u8 *buffer, u32 length)165#endif166{167struct bmi_cmd cmd;168union bmi_resp resp;169u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.read_mem);170u32 rxlen;171int ret;172173ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi read address 0x%x length %d\n",174address, length);175176if (ar->bmi.done_sent) {177ath10k_warn(ar, "command disallowed\n");178return -EBUSY;179}180181while (length) {182rxlen = min_t(u32, length, BMI_MAX_DATA_SIZE);183184cmd.id = __cpu_to_le32(BMI_READ_MEMORY);185cmd.read_mem.addr = __cpu_to_le32(address);186cmd.read_mem.len = __cpu_to_le32(rxlen);187188ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen,189&resp, &rxlen);190if (ret) {191ath10k_warn(ar, "unable to read from the device (%d)\n",192ret);193return ret;194}195196memcpy(buffer, resp.read_mem.payload, rxlen);197address += rxlen;198buffer += rxlen;199length -= rxlen;200}201202return 0;203}204EXPORT_SYMBOL(ath10k_bmi_read_memory);205206int ath10k_bmi_write_soc_reg(struct ath10k *ar, u32 address, u32 reg_val)207{208struct bmi_cmd cmd;209u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.write_soc_reg);210int ret;211212ath10k_dbg(ar, ATH10K_DBG_BMI,213"bmi write soc register 0x%08x val 0x%08x\n",214address, reg_val);215216if (ar->bmi.done_sent) {217ath10k_warn(ar, "bmi write soc register command in progress\n");218return -EBUSY;219}220221cmd.id = __cpu_to_le32(BMI_WRITE_SOC_REGISTER);222cmd.write_soc_reg.addr = __cpu_to_le32(address);223cmd.write_soc_reg.value = __cpu_to_le32(reg_val);224225ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);226if (ret) {227ath10k_warn(ar, "Unable to write soc register to device: %d\n",228ret);229return ret;230}231232return 0;233}234235int ath10k_bmi_read_soc_reg(struct ath10k *ar, u32 address, u32 *reg_val)236{237struct bmi_cmd cmd;238union bmi_resp resp;239u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.read_soc_reg);240u32 resplen = sizeof(resp.read_soc_reg);241int ret;242243ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi read soc register 0x%08x\n",244address);245246if (ar->bmi.done_sent) {247ath10k_warn(ar, "bmi read soc register command in progress\n");248return -EBUSY;249}250251cmd.id = __cpu_to_le32(BMI_READ_SOC_REGISTER);252cmd.read_soc_reg.addr = __cpu_to_le32(address);253254ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);255if (ret) {256ath10k_warn(ar, "Unable to read soc register from device: %d\n",257ret);258return ret;259}260261*reg_val = __le32_to_cpu(resp.read_soc_reg.value);262263ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi read soc register value 0x%08x\n",264*reg_val);265266return 0;267}268269int ath10k_bmi_write_memory(struct ath10k *ar,270#if defined(__linux__)271u32 address, const void *buffer, u32 length)272#elif defined(__FreeBSD__)273u32 address, const u8 *buffer, u32 length)274#endif275{276struct bmi_cmd cmd;277u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.write_mem);278u32 txlen;279int ret;280281ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi write address 0x%x length %d\n",282address, length);283284if (ar->bmi.done_sent) {285ath10k_warn(ar, "command disallowed\n");286return -EBUSY;287}288289while (length) {290txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen);291292/* copy before roundup to avoid reading beyond buffer*/293memcpy(cmd.write_mem.payload, buffer, txlen);294txlen = roundup(txlen, 4);295296cmd.id = __cpu_to_le32(BMI_WRITE_MEMORY);297cmd.write_mem.addr = __cpu_to_le32(address);298cmd.write_mem.len = __cpu_to_le32(txlen);299300ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,301NULL, NULL);302if (ret) {303ath10k_warn(ar, "unable to write to the device (%d)\n",304ret);305return ret;306}307308/* fixup roundup() so `length` zeroes out for last chunk */309txlen = min(txlen, length);310311address += txlen;312buffer += txlen;313length -= txlen;314}315316return 0;317}318319int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 param, u32 *result)320{321struct bmi_cmd cmd;322union bmi_resp resp;323u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.execute);324u32 resplen = sizeof(resp.execute);325int ret;326327ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi execute address 0x%x param 0x%x\n",328address, param);329330if (ar->bmi.done_sent) {331ath10k_warn(ar, "command disallowed\n");332return -EBUSY;333}334335cmd.id = __cpu_to_le32(BMI_EXECUTE);336cmd.execute.addr = __cpu_to_le32(address);337cmd.execute.param = __cpu_to_le32(param);338339ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);340if (ret) {341ath10k_warn(ar, "unable to read from the device\n");342return ret;343}344345if (resplen < sizeof(resp.execute)) {346ath10k_warn(ar, "invalid execute response length (%d)\n",347resplen);348return -EIO;349}350351*result = __le32_to_cpu(resp.execute.result);352353ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi execute result 0x%x\n", *result);354355return 0;356}357358#if defined(__linux__)359static int ath10k_bmi_lz_data_large(struct ath10k *ar, const void *buffer, u32 length)360#elif defined(__FreeBSD__)361static int ath10k_bmi_lz_data_large(struct ath10k *ar, const u8 *buffer, u32 length)362#endif363{364struct bmi_cmd *cmd;365u32 hdrlen = sizeof(cmd->id) + sizeof(cmd->lz_data);366u32 txlen;367int ret;368size_t buf_len;369370ath10k_dbg(ar, ATH10K_DBG_BMI, "large bmi lz data buffer 0x%p length %d\n",371buffer, length);372373if (ar->bmi.done_sent) {374ath10k_warn(ar, "command disallowed\n");375return -EBUSY;376}377378buf_len = sizeof(*cmd) + BMI_MAX_LARGE_DATA_SIZE - BMI_MAX_DATA_SIZE;379cmd = kzalloc(buf_len, GFP_KERNEL);380if (!cmd)381return -ENOMEM;382383while (length) {384txlen = min(length, BMI_MAX_LARGE_DATA_SIZE - hdrlen);385386WARN_ON_ONCE(txlen & 3);387388cmd->id = __cpu_to_le32(BMI_LZ_DATA);389cmd->lz_data.len = __cpu_to_le32(txlen);390memcpy(cmd->lz_data.payload, buffer, txlen);391392ret = ath10k_hif_exchange_bmi_msg(ar, cmd, hdrlen + txlen,393NULL, NULL);394if (ret) {395ath10k_warn(ar, "unable to write to the device\n");396kfree(cmd);397return ret;398}399400buffer += txlen;401length -= txlen;402}403404kfree(cmd);405406return 0;407}408409#if defined(__linux__)410int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length)411#elif defined(__FreeBSD__)412static413int ath10k_bmi_lz_data(struct ath10k *ar, const u8 *buffer, u32 length)414#endif415{416struct bmi_cmd cmd;417u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.lz_data);418u32 txlen;419int ret;420421ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi lz data buffer 0x%p length %d\n",422buffer, length);423424if (ar->bmi.done_sent) {425ath10k_warn(ar, "command disallowed\n");426return -EBUSY;427}428429while (length) {430txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen);431432WARN_ON_ONCE(txlen & 3);433434cmd.id = __cpu_to_le32(BMI_LZ_DATA);435cmd.lz_data.len = __cpu_to_le32(txlen);436memcpy(cmd.lz_data.payload, buffer, txlen);437438ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,439NULL, NULL);440if (ret) {441ath10k_warn(ar, "unable to write to the device\n");442return ret;443}444445buffer += txlen;446length -= txlen;447}448449return 0;450}451452int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address)453{454struct bmi_cmd cmd;455u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.lz_start);456int ret;457458ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi lz stream start address 0x%x\n",459address);460461if (ar->bmi.done_sent) {462ath10k_warn(ar, "command disallowed\n");463return -EBUSY;464}465466cmd.id = __cpu_to_le32(BMI_LZ_STREAM_START);467cmd.lz_start.addr = __cpu_to_le32(address);468469ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);470if (ret) {471ath10k_warn(ar, "unable to Start LZ Stream to the device\n");472return ret;473}474475return 0;476}477478int ath10k_bmi_fast_download(struct ath10k *ar,479u32 address, const void *buffer, u32 length)480{481u8 trailer[4] = {};482u32 head_len = rounddown(length, 4);483u32 trailer_len = length - head_len;484int ret;485486ath10k_dbg(ar, ATH10K_DBG_BMI,487"bmi fast download address 0x%x buffer 0x%p length %d\n",488address, buffer, length);489490ret = ath10k_bmi_lz_stream_start(ar, address);491if (ret)492return ret;493494/* copy the last word into a zero padded buffer */495if (trailer_len > 0)496#if defined(__linux__)497memcpy(trailer, buffer + head_len, trailer_len);498#elif defined(__FreeBSD__)499memcpy(trailer, (const u8 *)buffer + head_len, trailer_len);500#endif501502if (ar->hw_params.bmi_large_size_download)503ret = ath10k_bmi_lz_data_large(ar, buffer, head_len);504else505ret = ath10k_bmi_lz_data(ar, buffer, head_len);506507if (ret)508return ret;509510if (trailer_len > 0)511ret = ath10k_bmi_lz_data(ar, trailer, 4);512513if (ret != 0)514return ret;515516/*517* Close compressed stream and open a new (fake) one.518* This serves mainly to flush Target caches.519*/520ret = ath10k_bmi_lz_stream_start(ar, 0x00);521522return ret;523}524525int ath10k_bmi_set_start(struct ath10k *ar, u32 address)526{527struct bmi_cmd cmd;528u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.set_app_start);529int ret;530531if (ar->bmi.done_sent) {532ath10k_warn(ar, "bmi set start command disallowed\n");533return -EBUSY;534}535536cmd.id = __cpu_to_le32(BMI_SET_APP_START);537cmd.set_app_start.addr = __cpu_to_le32(address);538539ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);540if (ret) {541ath10k_warn(ar, "unable to set start to the device:%d\n", ret);542return ret;543}544545return 0;546}547548549