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]), ¶m, 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