brintos

brintos / linux-shallow public Read only

0
0
Text · 12.1 KiB · 9a4f8e8 Raw
520 lines · c
1// SPDX-License-Identifier: ISC2/*3 * Copyright (c) 2005-2011 Atheros Communications Inc.4 * Copyright (c) 2011-2014,2016-2017 Qualcomm Atheros, Inc.5 * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.6 */7 8#include "bmi.h"9#include "hif.h"10#include "debug.h"11#include "htc.h"12#include "hw.h"13 14void ath10k_bmi_start(struct ath10k *ar)15{16	ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi start\n");17 18	ar->bmi.done_sent = false;19}20EXPORT_SYMBOL(ath10k_bmi_start);21 22int ath10k_bmi_done(struct ath10k *ar)23{24	struct bmi_cmd cmd;25	u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.done);26	int ret;27 28	ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi done\n");29 30	if (ar->bmi.done_sent) {31		ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi skipped\n");32		return 0;33	}34 35	ar->bmi.done_sent = true;36	cmd.id = __cpu_to_le32(BMI_DONE);37 38	ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);39	if (ret) {40		ath10k_warn(ar, "unable to write to the device: %d\n", ret);41		return ret;42	}43 44	return 0;45}46 47int ath10k_bmi_get_target_info(struct ath10k *ar,48			       struct bmi_target_info *target_info)49{50	struct bmi_cmd cmd;51	union bmi_resp resp;52	u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.get_target_info);53	u32 resplen = sizeof(resp.get_target_info);54	int ret;55 56	ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi get target info\n");57 58	if (ar->bmi.done_sent) {59		ath10k_warn(ar, "BMI Get Target Info Command disallowed\n");60		return -EBUSY;61	}62 63	cmd.id = __cpu_to_le32(BMI_GET_TARGET_INFO);64 65	ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);66	if (ret) {67		ath10k_warn(ar, "unable to get target info from device\n");68		return ret;69	}70 71	if (resplen < sizeof(resp.get_target_info)) {72		ath10k_warn(ar, "invalid get_target_info response length (%d)\n",73			    resplen);74		return -EIO;75	}76 77	target_info->version = __le32_to_cpu(resp.get_target_info.version);78	target_info->type    = __le32_to_cpu(resp.get_target_info.type);79 80	return 0;81}82 83#define TARGET_VERSION_SENTINAL 0xffffffffu84 85int ath10k_bmi_get_target_info_sdio(struct ath10k *ar,86				    struct bmi_target_info *target_info)87{88	struct bmi_cmd cmd;89	union bmi_resp resp;90	u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.get_target_info);91	u32 resplen, ver_len;92	__le32 tmp;93	int ret;94 95	ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi get target info SDIO\n");96 97	if (ar->bmi.done_sent) {98		ath10k_warn(ar, "BMI Get Target Info Command disallowed\n");99		return -EBUSY;100	}101 102	cmd.id = __cpu_to_le32(BMI_GET_TARGET_INFO);103 104	/* Step 1: Read 4 bytes of the target info and check if it is105	 * the special sentinel version word or the first word in the106	 * version response.107	 */108	resplen = sizeof(u32);109	ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &tmp, &resplen);110	if (ret) {111		ath10k_warn(ar, "unable to read from device\n");112		return ret;113	}114 115	/* Some SDIO boards have a special sentinel byte before the real116	 * version response.117	 */118	if (__le32_to_cpu(tmp) == TARGET_VERSION_SENTINAL) {119		/* Step 1b: Read the version length */120		resplen = sizeof(u32);121		ret = ath10k_hif_exchange_bmi_msg(ar, NULL, 0, &tmp,122						  &resplen);123		if (ret) {124			ath10k_warn(ar, "unable to read from device\n");125			return ret;126		}127	}128 129	ver_len = __le32_to_cpu(tmp);130 131	/* Step 2: Check the target info length */132	if (ver_len != sizeof(resp.get_target_info)) {133		ath10k_warn(ar, "Unexpected target info len: %u. Expected: %zu\n",134			    ver_len, sizeof(resp.get_target_info));135		return -EINVAL;136	}137 138	/* Step 3: Read the rest of the version response */139	resplen = sizeof(resp.get_target_info) - sizeof(u32);140	ret = ath10k_hif_exchange_bmi_msg(ar, NULL, 0,141					  &resp.get_target_info.version,142					  &resplen);143	if (ret) {144		ath10k_warn(ar, "unable to read from device\n");145		return ret;146	}147 148	target_info->version = __le32_to_cpu(resp.get_target_info.version);149	target_info->type    = __le32_to_cpu(resp.get_target_info.type);150 151	return 0;152}153 154int ath10k_bmi_read_memory(struct ath10k *ar,155			   u32 address, void *buffer, u32 length)156{157	struct bmi_cmd cmd;158	union bmi_resp resp;159	u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.read_mem);160	u32 rxlen;161	int ret;162 163	ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi read address 0x%x length %d\n",164		   address, length);165 166	if (ar->bmi.done_sent) {167		ath10k_warn(ar, "command disallowed\n");168		return -EBUSY;169	}170 171	while (length) {172		rxlen = min_t(u32, length, BMI_MAX_DATA_SIZE);173 174		cmd.id            = __cpu_to_le32(BMI_READ_MEMORY);175		cmd.read_mem.addr = __cpu_to_le32(address);176		cmd.read_mem.len  = __cpu_to_le32(rxlen);177 178		ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen,179						  &resp, &rxlen);180		if (ret) {181			ath10k_warn(ar, "unable to read from the device (%d)\n",182				    ret);183			return ret;184		}185 186		memcpy(buffer, resp.read_mem.payload, rxlen);187		address += rxlen;188		buffer  += rxlen;189		length  -= rxlen;190	}191 192	return 0;193}194EXPORT_SYMBOL(ath10k_bmi_read_memory);195 196int ath10k_bmi_write_soc_reg(struct ath10k *ar, u32 address, u32 reg_val)197{198	struct bmi_cmd cmd;199	u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.write_soc_reg);200	int ret;201 202	ath10k_dbg(ar, ATH10K_DBG_BMI,203		   "bmi write soc register 0x%08x val 0x%08x\n",204		   address, reg_val);205 206	if (ar->bmi.done_sent) {207		ath10k_warn(ar, "bmi write soc register command in progress\n");208		return -EBUSY;209	}210 211	cmd.id = __cpu_to_le32(BMI_WRITE_SOC_REGISTER);212	cmd.write_soc_reg.addr = __cpu_to_le32(address);213	cmd.write_soc_reg.value = __cpu_to_le32(reg_val);214 215	ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);216	if (ret) {217		ath10k_warn(ar, "Unable to write soc register to device: %d\n",218			    ret);219		return ret;220	}221 222	return 0;223}224 225int ath10k_bmi_read_soc_reg(struct ath10k *ar, u32 address, u32 *reg_val)226{227	struct bmi_cmd cmd;228	union bmi_resp resp;229	u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.read_soc_reg);230	u32 resplen = sizeof(resp.read_soc_reg);231	int ret;232 233	ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi read soc register 0x%08x\n",234		   address);235 236	if (ar->bmi.done_sent) {237		ath10k_warn(ar, "bmi read soc register command in progress\n");238		return -EBUSY;239	}240 241	cmd.id = __cpu_to_le32(BMI_READ_SOC_REGISTER);242	cmd.read_soc_reg.addr = __cpu_to_le32(address);243 244	ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);245	if (ret) {246		ath10k_warn(ar, "Unable to read soc register from device: %d\n",247			    ret);248		return ret;249	}250 251	*reg_val = __le32_to_cpu(resp.read_soc_reg.value);252 253	ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi read soc register value 0x%08x\n",254		   *reg_val);255 256	return 0;257}258 259int ath10k_bmi_write_memory(struct ath10k *ar,260			    u32 address, const void *buffer, u32 length)261{262	struct bmi_cmd cmd;263	u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.write_mem);264	u32 txlen;265	int ret;266 267	ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi write address 0x%x length %d\n",268		   address, length);269 270	if (ar->bmi.done_sent) {271		ath10k_warn(ar, "command disallowed\n");272		return -EBUSY;273	}274 275	while (length) {276		txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen);277 278		/* copy before roundup to avoid reading beyond buffer*/279		memcpy(cmd.write_mem.payload, buffer, txlen);280		txlen = roundup(txlen, 4);281 282		cmd.id             = __cpu_to_le32(BMI_WRITE_MEMORY);283		cmd.write_mem.addr = __cpu_to_le32(address);284		cmd.write_mem.len  = __cpu_to_le32(txlen);285 286		ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,287						  NULL, NULL);288		if (ret) {289			ath10k_warn(ar, "unable to write to the device (%d)\n",290				    ret);291			return ret;292		}293 294		/* fixup roundup() so `length` zeroes out for last chunk */295		txlen = min(txlen, length);296 297		address += txlen;298		buffer  += txlen;299		length  -= txlen;300	}301 302	return 0;303}304 305int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 param, u32 *result)306{307	struct bmi_cmd cmd;308	union bmi_resp resp;309	u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.execute);310	u32 resplen = sizeof(resp.execute);311	int ret;312 313	ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi execute address 0x%x param 0x%x\n",314		   address, param);315 316	if (ar->bmi.done_sent) {317		ath10k_warn(ar, "command disallowed\n");318		return -EBUSY;319	}320 321	cmd.id            = __cpu_to_le32(BMI_EXECUTE);322	cmd.execute.addr  = __cpu_to_le32(address);323	cmd.execute.param = __cpu_to_le32(param);324 325	ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen);326	if (ret) {327		ath10k_warn(ar, "unable to read from the device\n");328		return ret;329	}330 331	if (resplen < sizeof(resp.execute)) {332		ath10k_warn(ar, "invalid execute response length (%d)\n",333			    resplen);334		return -EIO;335	}336 337	*result = __le32_to_cpu(resp.execute.result);338 339	ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi execute result 0x%x\n", *result);340 341	return 0;342}343 344static int ath10k_bmi_lz_data_large(struct ath10k *ar, const void *buffer, u32 length)345{346	struct bmi_cmd *cmd;347	u32 hdrlen = sizeof(cmd->id) + sizeof(cmd->lz_data);348	u32 txlen;349	int ret;350	size_t buf_len;351 352	ath10k_dbg(ar, ATH10K_DBG_BMI, "large bmi lz data buffer 0x%pK length %d\n",353		   buffer, length);354 355	if (ar->bmi.done_sent) {356		ath10k_warn(ar, "command disallowed\n");357		return -EBUSY;358	}359 360	buf_len = sizeof(*cmd) + BMI_MAX_LARGE_DATA_SIZE - BMI_MAX_DATA_SIZE;361	cmd = kzalloc(buf_len, GFP_KERNEL);362	if (!cmd)363		return -ENOMEM;364 365	while (length) {366		txlen = min(length, BMI_MAX_LARGE_DATA_SIZE - hdrlen);367 368		WARN_ON_ONCE(txlen & 3);369 370		cmd->id          = __cpu_to_le32(BMI_LZ_DATA);371		cmd->lz_data.len = __cpu_to_le32(txlen);372		memcpy(cmd->lz_data.payload, buffer, txlen);373 374		ret = ath10k_hif_exchange_bmi_msg(ar, cmd, hdrlen + txlen,375						  NULL, NULL);376		if (ret) {377			ath10k_warn(ar, "unable to write to the device\n");378			kfree(cmd);379			return ret;380		}381 382		buffer += txlen;383		length -= txlen;384	}385 386	kfree(cmd);387 388	return 0;389}390 391int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length)392{393	struct bmi_cmd cmd;394	u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.lz_data);395	u32 txlen;396	int ret;397 398	ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi lz data buffer 0x%pK length %d\n",399		   buffer, length);400 401	if (ar->bmi.done_sent) {402		ath10k_warn(ar, "command disallowed\n");403		return -EBUSY;404	}405 406	while (length) {407		txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen);408 409		WARN_ON_ONCE(txlen & 3);410 411		cmd.id          = __cpu_to_le32(BMI_LZ_DATA);412		cmd.lz_data.len = __cpu_to_le32(txlen);413		memcpy(cmd.lz_data.payload, buffer, txlen);414 415		ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen,416						  NULL, NULL);417		if (ret) {418			ath10k_warn(ar, "unable to write to the device\n");419			return ret;420		}421 422		buffer += txlen;423		length -= txlen;424	}425 426	return 0;427}428 429int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address)430{431	struct bmi_cmd cmd;432	u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.lz_start);433	int ret;434 435	ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi lz stream start address 0x%x\n",436		   address);437 438	if (ar->bmi.done_sent) {439		ath10k_warn(ar, "command disallowed\n");440		return -EBUSY;441	}442 443	cmd.id            = __cpu_to_le32(BMI_LZ_STREAM_START);444	cmd.lz_start.addr = __cpu_to_le32(address);445 446	ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);447	if (ret) {448		ath10k_warn(ar, "unable to Start LZ Stream to the device\n");449		return ret;450	}451 452	return 0;453}454 455int ath10k_bmi_fast_download(struct ath10k *ar,456			     u32 address, const void *buffer, u32 length)457{458	u8 trailer[4] = {};459	u32 head_len = rounddown(length, 4);460	u32 trailer_len = length - head_len;461	int ret;462 463	ath10k_dbg(ar, ATH10K_DBG_BMI,464		   "bmi fast download address 0x%x buffer 0x%pK length %d\n",465		   address, buffer, length);466 467	ret = ath10k_bmi_lz_stream_start(ar, address);468	if (ret)469		return ret;470 471	/* copy the last word into a zero padded buffer */472	if (trailer_len > 0)473		memcpy(trailer, buffer + head_len, trailer_len);474 475	if (ar->hw_params.bmi_large_size_download)476		ret = ath10k_bmi_lz_data_large(ar, buffer, head_len);477	else478		ret = ath10k_bmi_lz_data(ar, buffer, head_len);479 480	if (ret)481		return ret;482 483	if (trailer_len > 0)484		ret = ath10k_bmi_lz_data(ar, trailer, 4);485 486	if (ret != 0)487		return ret;488 489	/*490	 * Close compressed stream and open a new (fake) one.491	 * This serves mainly to flush Target caches.492	 */493	ret = ath10k_bmi_lz_stream_start(ar, 0x00);494 495	return ret;496}497 498int ath10k_bmi_set_start(struct ath10k *ar, u32 address)499{500	struct bmi_cmd cmd;501	u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.set_app_start);502	int ret;503 504	if (ar->bmi.done_sent) {505		ath10k_warn(ar, "bmi set start command disallowed\n");506		return -EBUSY;507	}508 509	cmd.id = __cpu_to_le32(BMI_SET_APP_START);510	cmd.set_app_start.addr = __cpu_to_le32(address);511 512	ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL);513	if (ret) {514		ath10k_warn(ar, "unable to set start to the device:%d\n", ret);515		return ret;516	}517 518	return 0;519}520