18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (c) 2004-2011 Atheros Communications Inc.
38c2ecf20Sopenharmony_ci * Copyright (c) 2011-2012 Qualcomm Atheros, Inc.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any
68c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above
78c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
108c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
118c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
128c2ecf20Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
138c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
148c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
158c2ecf20Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
168c2ecf20Sopenharmony_ci */
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "core.h"
198c2ecf20Sopenharmony_ci#include "hif-ops.h"
208c2ecf20Sopenharmony_ci#include "target.h"
218c2ecf20Sopenharmony_ci#include "debug.h"
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ciint ath6kl_bmi_done(struct ath6kl *ar)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	int ret;
268c2ecf20Sopenharmony_ci	u32 cid = BMI_DONE;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	if (ar->bmi.done_sent) {
298c2ecf20Sopenharmony_ci		ath6kl_dbg(ATH6KL_DBG_BMI, "bmi done skipped\n");
308c2ecf20Sopenharmony_ci		return 0;
318c2ecf20Sopenharmony_ci	}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	ar->bmi.done_sent = true;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	ret = ath6kl_hif_bmi_write(ar, (u8 *)&cid, sizeof(cid));
368c2ecf20Sopenharmony_ci	if (ret) {
378c2ecf20Sopenharmony_ci		ath6kl_err("Unable to send bmi done: %d\n", ret);
388c2ecf20Sopenharmony_ci		return ret;
398c2ecf20Sopenharmony_ci	}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	return 0;
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ciint ath6kl_bmi_get_target_info(struct ath6kl *ar,
458c2ecf20Sopenharmony_ci			       struct ath6kl_bmi_target_info *targ_info)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	int ret;
488c2ecf20Sopenharmony_ci	u32 cid = BMI_GET_TARGET_INFO;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	if (ar->bmi.done_sent) {
518c2ecf20Sopenharmony_ci		ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
528c2ecf20Sopenharmony_ci		return -EACCES;
538c2ecf20Sopenharmony_ci	}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	ret = ath6kl_hif_bmi_write(ar, (u8 *)&cid, sizeof(cid));
568c2ecf20Sopenharmony_ci	if (ret) {
578c2ecf20Sopenharmony_ci		ath6kl_err("Unable to send get target info: %d\n", ret);
588c2ecf20Sopenharmony_ci		return ret;
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	if (ar->hif_type == ATH6KL_HIF_TYPE_USB) {
628c2ecf20Sopenharmony_ci		ret = ath6kl_hif_bmi_read(ar, (u8 *)targ_info,
638c2ecf20Sopenharmony_ci					  sizeof(*targ_info));
648c2ecf20Sopenharmony_ci	} else {
658c2ecf20Sopenharmony_ci		ret = ath6kl_hif_bmi_read(ar, (u8 *)&targ_info->version,
668c2ecf20Sopenharmony_ci				sizeof(targ_info->version));
678c2ecf20Sopenharmony_ci	}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	if (ret) {
708c2ecf20Sopenharmony_ci		ath6kl_err("Unable to recv target info: %d\n", ret);
718c2ecf20Sopenharmony_ci		return ret;
728c2ecf20Sopenharmony_ci	}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	if (le32_to_cpu(targ_info->version) == TARGET_VERSION_SENTINAL) {
758c2ecf20Sopenharmony_ci		/* Determine how many bytes are in the Target's targ_info */
768c2ecf20Sopenharmony_ci		ret = ath6kl_hif_bmi_read(ar,
778c2ecf20Sopenharmony_ci				   (u8 *)&targ_info->byte_count,
788c2ecf20Sopenharmony_ci				   sizeof(targ_info->byte_count));
798c2ecf20Sopenharmony_ci		if (ret) {
808c2ecf20Sopenharmony_ci			ath6kl_err("unable to read target info byte count: %d\n",
818c2ecf20Sopenharmony_ci				   ret);
828c2ecf20Sopenharmony_ci			return ret;
838c2ecf20Sopenharmony_ci		}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci		/*
868c2ecf20Sopenharmony_ci		 * The target's targ_info doesn't match the host's targ_info.
878c2ecf20Sopenharmony_ci		 * We need to do some backwards compatibility to make this work.
888c2ecf20Sopenharmony_ci		 */
898c2ecf20Sopenharmony_ci		if (le32_to_cpu(targ_info->byte_count) != sizeof(*targ_info)) {
908c2ecf20Sopenharmony_ci			WARN_ON(1);
918c2ecf20Sopenharmony_ci			return -EINVAL;
928c2ecf20Sopenharmony_ci		}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci		/* Read the remainder of the targ_info */
958c2ecf20Sopenharmony_ci		ret = ath6kl_hif_bmi_read(ar,
968c2ecf20Sopenharmony_ci				   ((u8 *)targ_info) +
978c2ecf20Sopenharmony_ci				   sizeof(targ_info->byte_count),
988c2ecf20Sopenharmony_ci				   sizeof(*targ_info) -
998c2ecf20Sopenharmony_ci				   sizeof(targ_info->byte_count));
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci		if (ret) {
1028c2ecf20Sopenharmony_ci			ath6kl_err("Unable to read target info (%d bytes): %d\n",
1038c2ecf20Sopenharmony_ci				   targ_info->byte_count, ret);
1048c2ecf20Sopenharmony_ci			return ret;
1058c2ecf20Sopenharmony_ci		}
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	ath6kl_dbg(ATH6KL_DBG_BMI, "target info (ver: 0x%x type: 0x%x)\n",
1098c2ecf20Sopenharmony_ci		   targ_info->version, targ_info->type);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	return 0;
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ciint ath6kl_bmi_read(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	u32 cid = BMI_READ_MEMORY;
1178c2ecf20Sopenharmony_ci	int ret;
1188c2ecf20Sopenharmony_ci	u32 offset;
1198c2ecf20Sopenharmony_ci	u32 len_remain, rx_len;
1208c2ecf20Sopenharmony_ci	u16 size;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	if (ar->bmi.done_sent) {
1238c2ecf20Sopenharmony_ci		ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
1248c2ecf20Sopenharmony_ci		return -EACCES;
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	size = ar->bmi.max_data_size + sizeof(cid) + sizeof(addr) + sizeof(len);
1288c2ecf20Sopenharmony_ci	if (size > ar->bmi.max_cmd_size) {
1298c2ecf20Sopenharmony_ci		WARN_ON(1);
1308c2ecf20Sopenharmony_ci		return -EINVAL;
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci	memset(ar->bmi.cmd_buf, 0, size);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	ath6kl_dbg(ATH6KL_DBG_BMI,
1358c2ecf20Sopenharmony_ci		   "bmi read memory: device: addr: 0x%x, len: %d\n",
1368c2ecf20Sopenharmony_ci		   addr, len);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	len_remain = len;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	while (len_remain) {
1418c2ecf20Sopenharmony_ci		rx_len = (len_remain < ar->bmi.max_data_size) ?
1428c2ecf20Sopenharmony_ci					len_remain : ar->bmi.max_data_size;
1438c2ecf20Sopenharmony_ci		offset = 0;
1448c2ecf20Sopenharmony_ci		memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
1458c2ecf20Sopenharmony_ci		offset += sizeof(cid);
1468c2ecf20Sopenharmony_ci		memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
1478c2ecf20Sopenharmony_ci		offset += sizeof(addr);
1488c2ecf20Sopenharmony_ci		memcpy(&(ar->bmi.cmd_buf[offset]), &rx_len, sizeof(rx_len));
1498c2ecf20Sopenharmony_ci		offset += sizeof(len);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci		ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
1528c2ecf20Sopenharmony_ci		if (ret) {
1538c2ecf20Sopenharmony_ci			ath6kl_err("Unable to write to the device: %d\n",
1548c2ecf20Sopenharmony_ci				   ret);
1558c2ecf20Sopenharmony_ci			return ret;
1568c2ecf20Sopenharmony_ci		}
1578c2ecf20Sopenharmony_ci		ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, rx_len);
1588c2ecf20Sopenharmony_ci		if (ret) {
1598c2ecf20Sopenharmony_ci			ath6kl_err("Unable to read from the device: %d\n",
1608c2ecf20Sopenharmony_ci				   ret);
1618c2ecf20Sopenharmony_ci			return ret;
1628c2ecf20Sopenharmony_ci		}
1638c2ecf20Sopenharmony_ci		memcpy(&buf[len - len_remain], ar->bmi.cmd_buf, rx_len);
1648c2ecf20Sopenharmony_ci		len_remain -= rx_len; addr += rx_len;
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	return 0;
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ciint ath6kl_bmi_write(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	u32 cid = BMI_WRITE_MEMORY;
1738c2ecf20Sopenharmony_ci	int ret;
1748c2ecf20Sopenharmony_ci	u32 offset;
1758c2ecf20Sopenharmony_ci	u32 len_remain, tx_len;
1768c2ecf20Sopenharmony_ci	const u32 header = sizeof(cid) + sizeof(addr) + sizeof(len);
1778c2ecf20Sopenharmony_ci	u8 aligned_buf[400];
1788c2ecf20Sopenharmony_ci	u8 *src;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	if (ar->bmi.done_sent) {
1818c2ecf20Sopenharmony_ci		ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
1828c2ecf20Sopenharmony_ci		return -EACCES;
1838c2ecf20Sopenharmony_ci	}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	if ((ar->bmi.max_data_size + header) > ar->bmi.max_cmd_size) {
1868c2ecf20Sopenharmony_ci		WARN_ON(1);
1878c2ecf20Sopenharmony_ci		return -EINVAL;
1888c2ecf20Sopenharmony_ci	}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	if (WARN_ON(ar->bmi.max_data_size > sizeof(aligned_buf)))
1918c2ecf20Sopenharmony_ci		return -E2BIG;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	memset(ar->bmi.cmd_buf, 0, ar->bmi.max_data_size + header);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	ath6kl_dbg(ATH6KL_DBG_BMI,
1968c2ecf20Sopenharmony_ci		   "bmi write memory: addr: 0x%x, len: %d\n", addr, len);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	len_remain = len;
1998c2ecf20Sopenharmony_ci	while (len_remain) {
2008c2ecf20Sopenharmony_ci		src = &buf[len - len_remain];
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci		if (len_remain < (ar->bmi.max_data_size - header)) {
2038c2ecf20Sopenharmony_ci			if (len_remain & 3) {
2048c2ecf20Sopenharmony_ci				/* align it with 4 bytes */
2058c2ecf20Sopenharmony_ci				len_remain = len_remain +
2068c2ecf20Sopenharmony_ci					     (4 - (len_remain & 3));
2078c2ecf20Sopenharmony_ci				memcpy(aligned_buf, src, len_remain);
2088c2ecf20Sopenharmony_ci				src = aligned_buf;
2098c2ecf20Sopenharmony_ci			}
2108c2ecf20Sopenharmony_ci			tx_len = len_remain;
2118c2ecf20Sopenharmony_ci		} else {
2128c2ecf20Sopenharmony_ci			tx_len = (ar->bmi.max_data_size - header);
2138c2ecf20Sopenharmony_ci		}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci		offset = 0;
2168c2ecf20Sopenharmony_ci		memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
2178c2ecf20Sopenharmony_ci		offset += sizeof(cid);
2188c2ecf20Sopenharmony_ci		memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
2198c2ecf20Sopenharmony_ci		offset += sizeof(addr);
2208c2ecf20Sopenharmony_ci		memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len));
2218c2ecf20Sopenharmony_ci		offset += sizeof(tx_len);
2228c2ecf20Sopenharmony_ci		memcpy(&(ar->bmi.cmd_buf[offset]), src, tx_len);
2238c2ecf20Sopenharmony_ci		offset += tx_len;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci		ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
2268c2ecf20Sopenharmony_ci		if (ret) {
2278c2ecf20Sopenharmony_ci			ath6kl_err("Unable to write to the device: %d\n",
2288c2ecf20Sopenharmony_ci				   ret);
2298c2ecf20Sopenharmony_ci			return ret;
2308c2ecf20Sopenharmony_ci		}
2318c2ecf20Sopenharmony_ci		len_remain -= tx_len; addr += tx_len;
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	return 0;
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ciint ath6kl_bmi_execute(struct ath6kl *ar, u32 addr, u32 *param)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	u32 cid = BMI_EXECUTE;
2408c2ecf20Sopenharmony_ci	int ret;
2418c2ecf20Sopenharmony_ci	u32 offset;
2428c2ecf20Sopenharmony_ci	u16 size;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	if (ar->bmi.done_sent) {
2458c2ecf20Sopenharmony_ci		ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
2468c2ecf20Sopenharmony_ci		return -EACCES;
2478c2ecf20Sopenharmony_ci	}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	size = sizeof(cid) + sizeof(addr) + sizeof(*param);
2508c2ecf20Sopenharmony_ci	if (size > ar->bmi.max_cmd_size) {
2518c2ecf20Sopenharmony_ci		WARN_ON(1);
2528c2ecf20Sopenharmony_ci		return -EINVAL;
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci	memset(ar->bmi.cmd_buf, 0, size);
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	ath6kl_dbg(ATH6KL_DBG_BMI, "bmi execute: addr: 0x%x, param: %d)\n",
2578c2ecf20Sopenharmony_ci		   addr, *param);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	offset = 0;
2608c2ecf20Sopenharmony_ci	memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
2618c2ecf20Sopenharmony_ci	offset += sizeof(cid);
2628c2ecf20Sopenharmony_ci	memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
2638c2ecf20Sopenharmony_ci	offset += sizeof(addr);
2648c2ecf20Sopenharmony_ci	memcpy(&(ar->bmi.cmd_buf[offset]), param, sizeof(*param));
2658c2ecf20Sopenharmony_ci	offset += sizeof(*param);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
2688c2ecf20Sopenharmony_ci	if (ret) {
2698c2ecf20Sopenharmony_ci		ath6kl_err("Unable to write to the device: %d\n", ret);
2708c2ecf20Sopenharmony_ci		return ret;
2718c2ecf20Sopenharmony_ci	}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, sizeof(*param));
2748c2ecf20Sopenharmony_ci	if (ret) {
2758c2ecf20Sopenharmony_ci		ath6kl_err("Unable to read from the device: %d\n", ret);
2768c2ecf20Sopenharmony_ci		return ret;
2778c2ecf20Sopenharmony_ci	}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	memcpy(param, ar->bmi.cmd_buf, sizeof(*param));
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	return 0;
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ciint ath6kl_bmi_set_app_start(struct ath6kl *ar, u32 addr)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	u32 cid = BMI_SET_APP_START;
2878c2ecf20Sopenharmony_ci	int ret;
2888c2ecf20Sopenharmony_ci	u32 offset;
2898c2ecf20Sopenharmony_ci	u16 size;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	if (ar->bmi.done_sent) {
2928c2ecf20Sopenharmony_ci		ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
2938c2ecf20Sopenharmony_ci		return -EACCES;
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	size = sizeof(cid) + sizeof(addr);
2978c2ecf20Sopenharmony_ci	if (size > ar->bmi.max_cmd_size) {
2988c2ecf20Sopenharmony_ci		WARN_ON(1);
2998c2ecf20Sopenharmony_ci		return -EINVAL;
3008c2ecf20Sopenharmony_ci	}
3018c2ecf20Sopenharmony_ci	memset(ar->bmi.cmd_buf, 0, size);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	ath6kl_dbg(ATH6KL_DBG_BMI, "bmi set app start: addr: 0x%x\n", addr);
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	offset = 0;
3068c2ecf20Sopenharmony_ci	memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
3078c2ecf20Sopenharmony_ci	offset += sizeof(cid);
3088c2ecf20Sopenharmony_ci	memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
3098c2ecf20Sopenharmony_ci	offset += sizeof(addr);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
3128c2ecf20Sopenharmony_ci	if (ret) {
3138c2ecf20Sopenharmony_ci		ath6kl_err("Unable to write to the device: %d\n", ret);
3148c2ecf20Sopenharmony_ci		return ret;
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	return 0;
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ciint ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param)
3218c2ecf20Sopenharmony_ci{
3228c2ecf20Sopenharmony_ci	u32 cid = BMI_READ_SOC_REGISTER;
3238c2ecf20Sopenharmony_ci	int ret;
3248c2ecf20Sopenharmony_ci	u32 offset;
3258c2ecf20Sopenharmony_ci	u16 size;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	if (ar->bmi.done_sent) {
3288c2ecf20Sopenharmony_ci		ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
3298c2ecf20Sopenharmony_ci		return -EACCES;
3308c2ecf20Sopenharmony_ci	}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	size = sizeof(cid) + sizeof(addr);
3338c2ecf20Sopenharmony_ci	if (size > ar->bmi.max_cmd_size) {
3348c2ecf20Sopenharmony_ci		WARN_ON(1);
3358c2ecf20Sopenharmony_ci		return -EINVAL;
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci	memset(ar->bmi.cmd_buf, 0, size);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	ath6kl_dbg(ATH6KL_DBG_BMI, "bmi read SOC reg: addr: 0x%x\n", addr);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	offset = 0;
3428c2ecf20Sopenharmony_ci	memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
3438c2ecf20Sopenharmony_ci	offset += sizeof(cid);
3448c2ecf20Sopenharmony_ci	memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
3458c2ecf20Sopenharmony_ci	offset += sizeof(addr);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
3488c2ecf20Sopenharmony_ci	if (ret) {
3498c2ecf20Sopenharmony_ci		ath6kl_err("Unable to write to the device: %d\n", ret);
3508c2ecf20Sopenharmony_ci		return ret;
3518c2ecf20Sopenharmony_ci	}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, sizeof(*param));
3548c2ecf20Sopenharmony_ci	if (ret) {
3558c2ecf20Sopenharmony_ci		ath6kl_err("Unable to read from the device: %d\n", ret);
3568c2ecf20Sopenharmony_ci		return ret;
3578c2ecf20Sopenharmony_ci	}
3588c2ecf20Sopenharmony_ci	memcpy(param, ar->bmi.cmd_buf, sizeof(*param));
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	return 0;
3618c2ecf20Sopenharmony_ci}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ciint ath6kl_bmi_reg_write(struct ath6kl *ar, u32 addr, u32 param)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	u32 cid = BMI_WRITE_SOC_REGISTER;
3668c2ecf20Sopenharmony_ci	int ret;
3678c2ecf20Sopenharmony_ci	u32 offset;
3688c2ecf20Sopenharmony_ci	u16 size;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	if (ar->bmi.done_sent) {
3718c2ecf20Sopenharmony_ci		ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
3728c2ecf20Sopenharmony_ci		return -EACCES;
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	size = sizeof(cid) + sizeof(addr) + sizeof(param);
3768c2ecf20Sopenharmony_ci	if (size > ar->bmi.max_cmd_size) {
3778c2ecf20Sopenharmony_ci		WARN_ON(1);
3788c2ecf20Sopenharmony_ci		return -EINVAL;
3798c2ecf20Sopenharmony_ci	}
3808c2ecf20Sopenharmony_ci	memset(ar->bmi.cmd_buf, 0, size);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	ath6kl_dbg(ATH6KL_DBG_BMI,
3838c2ecf20Sopenharmony_ci		   "bmi write SOC reg: addr: 0x%x, param: %d\n",
3848c2ecf20Sopenharmony_ci		    addr, param);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	offset = 0;
3878c2ecf20Sopenharmony_ci	memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
3888c2ecf20Sopenharmony_ci	offset += sizeof(cid);
3898c2ecf20Sopenharmony_ci	memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
3908c2ecf20Sopenharmony_ci	offset += sizeof(addr);
3918c2ecf20Sopenharmony_ci	memcpy(&(ar->bmi.cmd_buf[offset]), &param, sizeof(param));
3928c2ecf20Sopenharmony_ci	offset += sizeof(param);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
3958c2ecf20Sopenharmony_ci	if (ret) {
3968c2ecf20Sopenharmony_ci		ath6kl_err("Unable to write to the device: %d\n", ret);
3978c2ecf20Sopenharmony_ci		return ret;
3988c2ecf20Sopenharmony_ci	}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	return 0;
4018c2ecf20Sopenharmony_ci}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ciint ath6kl_bmi_lz_data(struct ath6kl *ar, u8 *buf, u32 len)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	u32 cid = BMI_LZ_DATA;
4068c2ecf20Sopenharmony_ci	int ret;
4078c2ecf20Sopenharmony_ci	u32 offset;
4088c2ecf20Sopenharmony_ci	u32 len_remain, tx_len;
4098c2ecf20Sopenharmony_ci	const u32 header = sizeof(cid) + sizeof(len);
4108c2ecf20Sopenharmony_ci	u16 size;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	if (ar->bmi.done_sent) {
4138c2ecf20Sopenharmony_ci		ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
4148c2ecf20Sopenharmony_ci		return -EACCES;
4158c2ecf20Sopenharmony_ci	}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	size = ar->bmi.max_data_size + header;
4188c2ecf20Sopenharmony_ci	if (size > ar->bmi.max_cmd_size) {
4198c2ecf20Sopenharmony_ci		WARN_ON(1);
4208c2ecf20Sopenharmony_ci		return -EINVAL;
4218c2ecf20Sopenharmony_ci	}
4228c2ecf20Sopenharmony_ci	memset(ar->bmi.cmd_buf, 0, size);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	ath6kl_dbg(ATH6KL_DBG_BMI, "bmi send LZ data: len: %d)\n",
4258c2ecf20Sopenharmony_ci		   len);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	len_remain = len;
4288c2ecf20Sopenharmony_ci	while (len_remain) {
4298c2ecf20Sopenharmony_ci		tx_len = (len_remain < (ar->bmi.max_data_size - header)) ?
4308c2ecf20Sopenharmony_ci			  len_remain : (ar->bmi.max_data_size - header);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci		offset = 0;
4338c2ecf20Sopenharmony_ci		memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
4348c2ecf20Sopenharmony_ci		offset += sizeof(cid);
4358c2ecf20Sopenharmony_ci		memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len));
4368c2ecf20Sopenharmony_ci		offset += sizeof(tx_len);
4378c2ecf20Sopenharmony_ci		memcpy(&(ar->bmi.cmd_buf[offset]), &buf[len - len_remain],
4388c2ecf20Sopenharmony_ci		       tx_len);
4398c2ecf20Sopenharmony_ci		offset += tx_len;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci		ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
4428c2ecf20Sopenharmony_ci		if (ret) {
4438c2ecf20Sopenharmony_ci			ath6kl_err("Unable to write to the device: %d\n",
4448c2ecf20Sopenharmony_ci				   ret);
4458c2ecf20Sopenharmony_ci			return ret;
4468c2ecf20Sopenharmony_ci		}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci		len_remain -= tx_len;
4498c2ecf20Sopenharmony_ci	}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	return 0;
4528c2ecf20Sopenharmony_ci}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ciint ath6kl_bmi_lz_stream_start(struct ath6kl *ar, u32 addr)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	u32 cid = BMI_LZ_STREAM_START;
4578c2ecf20Sopenharmony_ci	int ret;
4588c2ecf20Sopenharmony_ci	u32 offset;
4598c2ecf20Sopenharmony_ci	u16 size;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	if (ar->bmi.done_sent) {
4628c2ecf20Sopenharmony_ci		ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid);
4638c2ecf20Sopenharmony_ci		return -EACCES;
4648c2ecf20Sopenharmony_ci	}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	size = sizeof(cid) + sizeof(addr);
4678c2ecf20Sopenharmony_ci	if (size > ar->bmi.max_cmd_size) {
4688c2ecf20Sopenharmony_ci		WARN_ON(1);
4698c2ecf20Sopenharmony_ci		return -EINVAL;
4708c2ecf20Sopenharmony_ci	}
4718c2ecf20Sopenharmony_ci	memset(ar->bmi.cmd_buf, 0, size);
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	ath6kl_dbg(ATH6KL_DBG_BMI,
4748c2ecf20Sopenharmony_ci		   "bmi LZ stream start: addr: 0x%x)\n",
4758c2ecf20Sopenharmony_ci		    addr);
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	offset = 0;
4788c2ecf20Sopenharmony_ci	memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid));
4798c2ecf20Sopenharmony_ci	offset += sizeof(cid);
4808c2ecf20Sopenharmony_ci	memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr));
4818c2ecf20Sopenharmony_ci	offset += sizeof(addr);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset);
4848c2ecf20Sopenharmony_ci	if (ret) {
4858c2ecf20Sopenharmony_ci		ath6kl_err("Unable to start LZ stream to the device: %d\n",
4868c2ecf20Sopenharmony_ci			   ret);
4878c2ecf20Sopenharmony_ci		return ret;
4888c2ecf20Sopenharmony_ci	}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	return 0;
4918c2ecf20Sopenharmony_ci}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ciint ath6kl_bmi_fast_download(struct ath6kl *ar, u32 addr, u8 *buf, u32 len)
4948c2ecf20Sopenharmony_ci{
4958c2ecf20Sopenharmony_ci	int ret;
4968c2ecf20Sopenharmony_ci	u32 last_word = 0;
4978c2ecf20Sopenharmony_ci	u32 last_word_offset = len & ~0x3;
4988c2ecf20Sopenharmony_ci	u32 unaligned_bytes = len & 0x3;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	ret = ath6kl_bmi_lz_stream_start(ar, addr);
5018c2ecf20Sopenharmony_ci	if (ret)
5028c2ecf20Sopenharmony_ci		return ret;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	if (unaligned_bytes) {
5058c2ecf20Sopenharmony_ci		/* copy the last word into a zero padded buffer */
5068c2ecf20Sopenharmony_ci		memcpy(&last_word, &buf[last_word_offset], unaligned_bytes);
5078c2ecf20Sopenharmony_ci	}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	ret = ath6kl_bmi_lz_data(ar, buf, last_word_offset);
5108c2ecf20Sopenharmony_ci	if (ret)
5118c2ecf20Sopenharmony_ci		return ret;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	if (unaligned_bytes)
5148c2ecf20Sopenharmony_ci		ret = ath6kl_bmi_lz_data(ar, (u8 *)&last_word, 4);
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	if (!ret) {
5178c2ecf20Sopenharmony_ci		/* Close compressed stream and open a new (fake) one.
5188c2ecf20Sopenharmony_ci		 * This serves mainly to flush Target caches. */
5198c2ecf20Sopenharmony_ci		ret = ath6kl_bmi_lz_stream_start(ar, 0x00);
5208c2ecf20Sopenharmony_ci	}
5218c2ecf20Sopenharmony_ci	return ret;
5228c2ecf20Sopenharmony_ci}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_civoid ath6kl_bmi_reset(struct ath6kl *ar)
5258c2ecf20Sopenharmony_ci{
5268c2ecf20Sopenharmony_ci	ar->bmi.done_sent = false;
5278c2ecf20Sopenharmony_ci}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ciint ath6kl_bmi_init(struct ath6kl *ar)
5308c2ecf20Sopenharmony_ci{
5318c2ecf20Sopenharmony_ci	if (WARN_ON(ar->bmi.max_data_size == 0))
5328c2ecf20Sopenharmony_ci		return -EINVAL;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci	/* cmd + addr + len + data_size */
5358c2ecf20Sopenharmony_ci	ar->bmi.max_cmd_size = ar->bmi.max_data_size + (sizeof(u32) * 3);
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	ar->bmi.cmd_buf = kzalloc(ar->bmi.max_cmd_size, GFP_KERNEL);
5388c2ecf20Sopenharmony_ci	if (!ar->bmi.cmd_buf)
5398c2ecf20Sopenharmony_ci		return -ENOMEM;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	return 0;
5428c2ecf20Sopenharmony_ci}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_civoid ath6kl_bmi_cleanup(struct ath6kl *ar)
5458c2ecf20Sopenharmony_ci{
5468c2ecf20Sopenharmony_ci	kfree(ar->bmi.cmd_buf);
5478c2ecf20Sopenharmony_ci	ar->bmi.cmd_buf = NULL;
5488c2ecf20Sopenharmony_ci}
549