1bdcd8170SKalle Valo /* 2bdcd8170SKalle Valo * Copyright (c) 2004-2011 Atheros Communications Inc. 3bdcd8170SKalle Valo * 4bdcd8170SKalle Valo * Permission to use, copy, modify, and/or distribute this software for any 5bdcd8170SKalle Valo * purpose with or without fee is hereby granted, provided that the above 6bdcd8170SKalle Valo * copyright notice and this permission notice appear in all copies. 7bdcd8170SKalle Valo * 8bdcd8170SKalle Valo * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9bdcd8170SKalle Valo * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10bdcd8170SKalle Valo * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11bdcd8170SKalle Valo * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12bdcd8170SKalle Valo * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13bdcd8170SKalle Valo * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14bdcd8170SKalle Valo * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15bdcd8170SKalle Valo */ 16bdcd8170SKalle Valo 17bdcd8170SKalle Valo #include "core.h" 18bdcd8170SKalle Valo #include "hif-ops.h" 19bdcd8170SKalle Valo #include "target.h" 20bdcd8170SKalle Valo #include "debug.h" 21bdcd8170SKalle Valo 22bdcd8170SKalle Valo int ath6kl_bmi_done(struct ath6kl *ar) 23bdcd8170SKalle Valo { 24bdcd8170SKalle Valo int ret; 25bdcd8170SKalle Valo u32 cid = BMI_DONE; 26bdcd8170SKalle Valo 27bdcd8170SKalle Valo if (ar->bmi.done_sent) { 28bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, "bmi done skipped\n"); 29bdcd8170SKalle Valo return 0; 30bdcd8170SKalle Valo } 31bdcd8170SKalle Valo 32bdcd8170SKalle Valo ar->bmi.done_sent = true; 33bdcd8170SKalle Valo 3466b693c3SKalle Valo ret = ath6kl_hif_bmi_write(ar, (u8 *)&cid, sizeof(cid)); 35bdcd8170SKalle Valo if (ret) { 36bdcd8170SKalle Valo ath6kl_err("Unable to send bmi done: %d\n", ret); 37bdcd8170SKalle Valo return ret; 38bdcd8170SKalle Valo } 39bdcd8170SKalle Valo 40bdcd8170SKalle Valo return 0; 41bdcd8170SKalle Valo } 42bdcd8170SKalle Valo 43bdcd8170SKalle Valo int ath6kl_bmi_get_target_info(struct ath6kl *ar, 44bdcd8170SKalle Valo struct ath6kl_bmi_target_info *targ_info) 45bdcd8170SKalle Valo { 46bdcd8170SKalle Valo int ret; 47bdcd8170SKalle Valo u32 cid = BMI_GET_TARGET_INFO; 48bdcd8170SKalle Valo 49bdcd8170SKalle Valo if (ar->bmi.done_sent) { 50bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 51bdcd8170SKalle Valo return -EACCES; 52bdcd8170SKalle Valo } 53bdcd8170SKalle Valo 5466b693c3SKalle Valo ret = ath6kl_hif_bmi_write(ar, (u8 *)&cid, sizeof(cid)); 55bdcd8170SKalle Valo if (ret) { 56bdcd8170SKalle Valo ath6kl_err("Unable to send get target info: %d\n", ret); 57bdcd8170SKalle Valo return ret; 58bdcd8170SKalle Valo } 59bdcd8170SKalle Valo 60241b128bSKalle Valo if (ar->hif_type == ATH6KL_HIF_TYPE_USB) { 61241b128bSKalle Valo ret = ath6kl_hif_bmi_read(ar, (u8 *)targ_info, 62241b128bSKalle Valo sizeof(*targ_info)); 63241b128bSKalle Valo } else { 6466b693c3SKalle Valo ret = ath6kl_hif_bmi_read(ar, (u8 *)&targ_info->version, 65cfc301edSKalle Valo sizeof(targ_info->version)); 66241b128bSKalle Valo } 67241b128bSKalle Valo 68bdcd8170SKalle Valo if (ret) { 69bdcd8170SKalle Valo ath6kl_err("Unable to recv target info: %d\n", ret); 70bdcd8170SKalle Valo return ret; 71bdcd8170SKalle Valo } 72bdcd8170SKalle Valo 73bdcd8170SKalle Valo if (le32_to_cpu(targ_info->version) == TARGET_VERSION_SENTINAL) { 74bdcd8170SKalle Valo /* Determine how many bytes are in the Target's targ_info */ 7566b693c3SKalle Valo ret = ath6kl_hif_bmi_read(ar, 76bdcd8170SKalle Valo (u8 *)&targ_info->byte_count, 77cfc301edSKalle Valo sizeof(targ_info->byte_count)); 78bdcd8170SKalle Valo if (ret) { 79bdcd8170SKalle Valo ath6kl_err("unable to read target info byte count: %d\n", 80bdcd8170SKalle Valo ret); 81bdcd8170SKalle Valo return ret; 82bdcd8170SKalle Valo } 83bdcd8170SKalle Valo 84bdcd8170SKalle Valo /* 85bdcd8170SKalle Valo * The target's targ_info doesn't match the host's targ_info. 86bdcd8170SKalle Valo * We need to do some backwards compatibility to make this work. 87bdcd8170SKalle Valo */ 88bdcd8170SKalle Valo if (le32_to_cpu(targ_info->byte_count) != sizeof(*targ_info)) { 89bdcd8170SKalle Valo WARN_ON(1); 90bdcd8170SKalle Valo return -EINVAL; 91bdcd8170SKalle Valo } 92bdcd8170SKalle Valo 93bdcd8170SKalle Valo /* Read the remainder of the targ_info */ 9466b693c3SKalle Valo ret = ath6kl_hif_bmi_read(ar, 95bdcd8170SKalle Valo ((u8 *)targ_info) + 96bdcd8170SKalle Valo sizeof(targ_info->byte_count), 97bdcd8170SKalle Valo sizeof(*targ_info) - 98cfc301edSKalle Valo sizeof(targ_info->byte_count)); 99bdcd8170SKalle Valo 100bdcd8170SKalle Valo if (ret) { 101bdcd8170SKalle Valo ath6kl_err("Unable to read target info (%d bytes): %d\n", 102bdcd8170SKalle Valo targ_info->byte_count, ret); 103bdcd8170SKalle Valo return ret; 104bdcd8170SKalle Valo } 105bdcd8170SKalle Valo } 106bdcd8170SKalle Valo 107bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, "target info (ver: 0x%x type: 0x%x)\n", 108bdcd8170SKalle Valo targ_info->version, targ_info->type); 109bdcd8170SKalle Valo 110bdcd8170SKalle Valo return 0; 111bdcd8170SKalle Valo } 112bdcd8170SKalle Valo 113bdcd8170SKalle Valo int ath6kl_bmi_read(struct ath6kl *ar, u32 addr, u8 *buf, u32 len) 114bdcd8170SKalle Valo { 115bdcd8170SKalle Valo u32 cid = BMI_READ_MEMORY; 116bdcd8170SKalle Valo int ret; 117bdcd8170SKalle Valo u32 offset; 118bdcd8170SKalle Valo u32 len_remain, rx_len; 119bdcd8170SKalle Valo u16 size; 120bdcd8170SKalle Valo 121bdcd8170SKalle Valo if (ar->bmi.done_sent) { 122bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 123bdcd8170SKalle Valo return -EACCES; 124bdcd8170SKalle Valo } 125bdcd8170SKalle Valo 1261f4c894dSKalle Valo size = ar->bmi.max_data_size + sizeof(cid) + sizeof(addr) + sizeof(len); 1271f4c894dSKalle Valo if (size > ar->bmi.max_cmd_size) { 128bdcd8170SKalle Valo WARN_ON(1); 129bdcd8170SKalle Valo return -EINVAL; 130bdcd8170SKalle Valo } 131bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 132bdcd8170SKalle Valo 133bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, 134bdcd8170SKalle Valo "bmi read memory: device: addr: 0x%x, len: %d\n", 135bdcd8170SKalle Valo addr, len); 136bdcd8170SKalle Valo 137bdcd8170SKalle Valo len_remain = len; 138bdcd8170SKalle Valo 139bdcd8170SKalle Valo while (len_remain) { 1401f4c894dSKalle Valo rx_len = (len_remain < ar->bmi.max_data_size) ? 1411f4c894dSKalle Valo len_remain : ar->bmi.max_data_size; 142bdcd8170SKalle Valo offset = 0; 143bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 144bdcd8170SKalle Valo offset += sizeof(cid); 145bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 146bdcd8170SKalle Valo offset += sizeof(addr); 147bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &rx_len, sizeof(rx_len)); 148bdcd8170SKalle Valo offset += sizeof(len); 149bdcd8170SKalle Valo 15066b693c3SKalle Valo ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 151bdcd8170SKalle Valo if (ret) { 152bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", 153bdcd8170SKalle Valo ret); 154bdcd8170SKalle Valo return ret; 155bdcd8170SKalle Valo } 15666b693c3SKalle Valo ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, rx_len); 157bdcd8170SKalle Valo if (ret) { 158bdcd8170SKalle Valo ath6kl_err("Unable to read from the device: %d\n", 159bdcd8170SKalle Valo ret); 160bdcd8170SKalle Valo return ret; 161bdcd8170SKalle Valo } 162bdcd8170SKalle Valo memcpy(&buf[len - len_remain], ar->bmi.cmd_buf, rx_len); 163bdcd8170SKalle Valo len_remain -= rx_len; addr += rx_len; 164bdcd8170SKalle Valo } 165bdcd8170SKalle Valo 166bdcd8170SKalle Valo return 0; 167bdcd8170SKalle Valo } 168bdcd8170SKalle Valo 169bdcd8170SKalle Valo int ath6kl_bmi_write(struct ath6kl *ar, u32 addr, u8 *buf, u32 len) 170bdcd8170SKalle Valo { 171bdcd8170SKalle Valo u32 cid = BMI_WRITE_MEMORY; 172bdcd8170SKalle Valo int ret; 173bdcd8170SKalle Valo u32 offset; 174bdcd8170SKalle Valo u32 len_remain, tx_len; 175bdcd8170SKalle Valo const u32 header = sizeof(cid) + sizeof(addr) + sizeof(len); 1761f4c894dSKalle Valo u8 aligned_buf[400]; 177bdcd8170SKalle Valo u8 *src; 178bdcd8170SKalle Valo 179bdcd8170SKalle Valo if (ar->bmi.done_sent) { 180bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 181bdcd8170SKalle Valo return -EACCES; 182bdcd8170SKalle Valo } 183bdcd8170SKalle Valo 1841f4c894dSKalle Valo if ((ar->bmi.max_data_size + header) > ar->bmi.max_cmd_size) { 185bdcd8170SKalle Valo WARN_ON(1); 186bdcd8170SKalle Valo return -EINVAL; 187bdcd8170SKalle Valo } 188bdcd8170SKalle Valo 1891f4c894dSKalle Valo if (WARN_ON(ar->bmi.max_data_size > sizeof(aligned_buf))) 1901f4c894dSKalle Valo return -E2BIG; 1911f4c894dSKalle Valo 1921f4c894dSKalle Valo memset(ar->bmi.cmd_buf, 0, ar->bmi.max_data_size + header); 193bdcd8170SKalle Valo 194bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, 195bdcd8170SKalle Valo "bmi write memory: addr: 0x%x, len: %d\n", addr, len); 196bdcd8170SKalle Valo 197bdcd8170SKalle Valo len_remain = len; 198bdcd8170SKalle Valo while (len_remain) { 199bdcd8170SKalle Valo src = &buf[len - len_remain]; 200bdcd8170SKalle Valo 2011f4c894dSKalle Valo if (len_remain < (ar->bmi.max_data_size - header)) { 202bdcd8170SKalle Valo if (len_remain & 3) { 203bdcd8170SKalle Valo /* align it with 4 bytes */ 204bdcd8170SKalle Valo len_remain = len_remain + 205bdcd8170SKalle Valo (4 - (len_remain & 3)); 206bdcd8170SKalle Valo memcpy(aligned_buf, src, len_remain); 207bdcd8170SKalle Valo src = aligned_buf; 208bdcd8170SKalle Valo } 209bdcd8170SKalle Valo tx_len = len_remain; 210bdcd8170SKalle Valo } else { 2111f4c894dSKalle Valo tx_len = (ar->bmi.max_data_size - header); 212bdcd8170SKalle Valo } 213bdcd8170SKalle Valo 214bdcd8170SKalle Valo offset = 0; 215bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 216bdcd8170SKalle Valo offset += sizeof(cid); 217bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 218bdcd8170SKalle Valo offset += sizeof(addr); 219bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len)); 220bdcd8170SKalle Valo offset += sizeof(tx_len); 221bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), src, tx_len); 222bdcd8170SKalle Valo offset += tx_len; 223bdcd8170SKalle Valo 22466b693c3SKalle Valo ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 225bdcd8170SKalle Valo if (ret) { 226bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", 227bdcd8170SKalle Valo ret); 228bdcd8170SKalle Valo return ret; 229bdcd8170SKalle Valo } 230bdcd8170SKalle Valo len_remain -= tx_len; addr += tx_len; 231bdcd8170SKalle Valo } 232bdcd8170SKalle Valo 233bdcd8170SKalle Valo return 0; 234bdcd8170SKalle Valo } 235bdcd8170SKalle Valo 236bdcd8170SKalle Valo int ath6kl_bmi_execute(struct ath6kl *ar, u32 addr, u32 *param) 237bdcd8170SKalle Valo { 238bdcd8170SKalle Valo u32 cid = BMI_EXECUTE; 239bdcd8170SKalle Valo int ret; 240bdcd8170SKalle Valo u32 offset; 241bdcd8170SKalle Valo u16 size; 242bdcd8170SKalle Valo 243bdcd8170SKalle Valo if (ar->bmi.done_sent) { 244bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 245bdcd8170SKalle Valo return -EACCES; 246bdcd8170SKalle Valo } 247bdcd8170SKalle Valo 248bdcd8170SKalle Valo size = sizeof(cid) + sizeof(addr) + sizeof(param); 2491f4c894dSKalle Valo if (size > ar->bmi.max_cmd_size) { 250bdcd8170SKalle Valo WARN_ON(1); 251bdcd8170SKalle Valo return -EINVAL; 252bdcd8170SKalle Valo } 253bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 254bdcd8170SKalle Valo 255bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, "bmi execute: addr: 0x%x, param: %d)\n", 256bdcd8170SKalle Valo addr, *param); 257bdcd8170SKalle Valo 258bdcd8170SKalle Valo offset = 0; 259bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 260bdcd8170SKalle Valo offset += sizeof(cid); 261bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 262bdcd8170SKalle Valo offset += sizeof(addr); 263bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), param, sizeof(*param)); 264bdcd8170SKalle Valo offset += sizeof(*param); 265bdcd8170SKalle Valo 26666b693c3SKalle Valo ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 267bdcd8170SKalle Valo if (ret) { 268bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", ret); 269bdcd8170SKalle Valo return ret; 270bdcd8170SKalle Valo } 271bdcd8170SKalle Valo 27266b693c3SKalle Valo ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, sizeof(*param)); 273bdcd8170SKalle Valo if (ret) { 274bdcd8170SKalle Valo ath6kl_err("Unable to read from the device: %d\n", ret); 275bdcd8170SKalle Valo return ret; 276bdcd8170SKalle Valo } 277bdcd8170SKalle Valo 278bdcd8170SKalle Valo memcpy(param, ar->bmi.cmd_buf, sizeof(*param)); 279bdcd8170SKalle Valo 280bdcd8170SKalle Valo return 0; 281bdcd8170SKalle Valo } 282bdcd8170SKalle Valo 283bdcd8170SKalle Valo int ath6kl_bmi_set_app_start(struct ath6kl *ar, u32 addr) 284bdcd8170SKalle Valo { 285bdcd8170SKalle Valo u32 cid = BMI_SET_APP_START; 286bdcd8170SKalle Valo int ret; 287bdcd8170SKalle Valo u32 offset; 288bdcd8170SKalle Valo u16 size; 289bdcd8170SKalle Valo 290bdcd8170SKalle Valo if (ar->bmi.done_sent) { 291bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 292bdcd8170SKalle Valo return -EACCES; 293bdcd8170SKalle Valo } 294bdcd8170SKalle Valo 295bdcd8170SKalle Valo size = sizeof(cid) + sizeof(addr); 2961f4c894dSKalle Valo if (size > ar->bmi.max_cmd_size) { 297bdcd8170SKalle Valo WARN_ON(1); 298bdcd8170SKalle Valo return -EINVAL; 299bdcd8170SKalle Valo } 300bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 301bdcd8170SKalle Valo 302bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, "bmi set app start: addr: 0x%x\n", addr); 303bdcd8170SKalle Valo 304bdcd8170SKalle Valo offset = 0; 305bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 306bdcd8170SKalle Valo offset += sizeof(cid); 307bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 308bdcd8170SKalle Valo offset += sizeof(addr); 309bdcd8170SKalle Valo 31066b693c3SKalle Valo ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 311bdcd8170SKalle Valo if (ret) { 312bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", ret); 313bdcd8170SKalle Valo return ret; 314bdcd8170SKalle Valo } 315bdcd8170SKalle Valo 316bdcd8170SKalle Valo return 0; 317bdcd8170SKalle Valo } 318bdcd8170SKalle Valo 319bdcd8170SKalle Valo int ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param) 320bdcd8170SKalle Valo { 321bdcd8170SKalle Valo u32 cid = BMI_READ_SOC_REGISTER; 322bdcd8170SKalle Valo int ret; 323bdcd8170SKalle Valo u32 offset; 324bdcd8170SKalle Valo u16 size; 325bdcd8170SKalle Valo 326bdcd8170SKalle Valo if (ar->bmi.done_sent) { 327bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 328bdcd8170SKalle Valo return -EACCES; 329bdcd8170SKalle Valo } 330bdcd8170SKalle Valo 331bdcd8170SKalle Valo size = sizeof(cid) + sizeof(addr); 3321f4c894dSKalle Valo if (size > ar->bmi.max_cmd_size) { 333bdcd8170SKalle Valo WARN_ON(1); 334bdcd8170SKalle Valo return -EINVAL; 335bdcd8170SKalle Valo } 336bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 337bdcd8170SKalle Valo 338bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, "bmi read SOC reg: addr: 0x%x\n", addr); 339bdcd8170SKalle Valo 340bdcd8170SKalle Valo offset = 0; 341bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 342bdcd8170SKalle Valo offset += sizeof(cid); 343bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 344bdcd8170SKalle Valo offset += sizeof(addr); 345bdcd8170SKalle Valo 34666b693c3SKalle Valo ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 347bdcd8170SKalle Valo if (ret) { 348bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", ret); 349bdcd8170SKalle Valo return ret; 350bdcd8170SKalle Valo } 351bdcd8170SKalle Valo 35266b693c3SKalle Valo ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, sizeof(*param)); 353bdcd8170SKalle Valo if (ret) { 354bdcd8170SKalle Valo ath6kl_err("Unable to read from the device: %d\n", ret); 355bdcd8170SKalle Valo return ret; 356bdcd8170SKalle Valo } 357bdcd8170SKalle Valo memcpy(param, ar->bmi.cmd_buf, sizeof(*param)); 358bdcd8170SKalle Valo 359bdcd8170SKalle Valo return 0; 360bdcd8170SKalle Valo } 361bdcd8170SKalle Valo 362bdcd8170SKalle Valo int ath6kl_bmi_reg_write(struct ath6kl *ar, u32 addr, u32 param) 363bdcd8170SKalle Valo { 364bdcd8170SKalle Valo u32 cid = BMI_WRITE_SOC_REGISTER; 365bdcd8170SKalle Valo int ret; 366bdcd8170SKalle Valo u32 offset; 367bdcd8170SKalle Valo u16 size; 368bdcd8170SKalle Valo 369bdcd8170SKalle Valo if (ar->bmi.done_sent) { 370bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 371bdcd8170SKalle Valo return -EACCES; 372bdcd8170SKalle Valo } 373bdcd8170SKalle Valo 374bdcd8170SKalle Valo size = sizeof(cid) + sizeof(addr) + sizeof(param); 3751f4c894dSKalle Valo if (size > ar->bmi.max_cmd_size) { 376bdcd8170SKalle Valo WARN_ON(1); 377bdcd8170SKalle Valo return -EINVAL; 378bdcd8170SKalle Valo } 379bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 380bdcd8170SKalle Valo 381bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, 382bdcd8170SKalle Valo "bmi write SOC reg: addr: 0x%x, param: %d\n", 383bdcd8170SKalle Valo addr, param); 384bdcd8170SKalle Valo 385bdcd8170SKalle Valo offset = 0; 386bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 387bdcd8170SKalle Valo offset += sizeof(cid); 388bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 389bdcd8170SKalle Valo offset += sizeof(addr); 390bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), ¶m, sizeof(param)); 391bdcd8170SKalle Valo offset += sizeof(param); 392bdcd8170SKalle Valo 39366b693c3SKalle Valo ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 394bdcd8170SKalle Valo if (ret) { 395bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", ret); 396bdcd8170SKalle Valo return ret; 397bdcd8170SKalle Valo } 398bdcd8170SKalle Valo 399bdcd8170SKalle Valo return 0; 400bdcd8170SKalle Valo } 401bdcd8170SKalle Valo 402bdcd8170SKalle Valo int ath6kl_bmi_lz_data(struct ath6kl *ar, u8 *buf, u32 len) 403bdcd8170SKalle Valo { 404bdcd8170SKalle Valo u32 cid = BMI_LZ_DATA; 405bdcd8170SKalle Valo int ret; 406bdcd8170SKalle Valo u32 offset; 407bdcd8170SKalle Valo u32 len_remain, tx_len; 408bdcd8170SKalle Valo const u32 header = sizeof(cid) + sizeof(len); 409bdcd8170SKalle Valo u16 size; 410bdcd8170SKalle Valo 411bdcd8170SKalle Valo if (ar->bmi.done_sent) { 412bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 413bdcd8170SKalle Valo return -EACCES; 414bdcd8170SKalle Valo } 415bdcd8170SKalle Valo 4161f4c894dSKalle Valo size = ar->bmi.max_data_size + header; 4171f4c894dSKalle Valo if (size > ar->bmi.max_cmd_size) { 418bdcd8170SKalle Valo WARN_ON(1); 419bdcd8170SKalle Valo return -EINVAL; 420bdcd8170SKalle Valo } 421bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 422bdcd8170SKalle Valo 423bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, "bmi send LZ data: len: %d)\n", 424bdcd8170SKalle Valo len); 425bdcd8170SKalle Valo 426bdcd8170SKalle Valo len_remain = len; 427bdcd8170SKalle Valo while (len_remain) { 4281f4c894dSKalle Valo tx_len = (len_remain < (ar->bmi.max_data_size - header)) ? 4291f4c894dSKalle Valo len_remain : (ar->bmi.max_data_size - header); 430bdcd8170SKalle Valo 431bdcd8170SKalle Valo offset = 0; 432bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 433bdcd8170SKalle Valo offset += sizeof(cid); 434bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len)); 435bdcd8170SKalle Valo offset += sizeof(tx_len); 436bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &buf[len - len_remain], 437bdcd8170SKalle Valo tx_len); 438bdcd8170SKalle Valo offset += tx_len; 439bdcd8170SKalle Valo 44066b693c3SKalle Valo ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 441bdcd8170SKalle Valo if (ret) { 442bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", 443bdcd8170SKalle Valo ret); 444bdcd8170SKalle Valo return ret; 445bdcd8170SKalle Valo } 446bdcd8170SKalle Valo 447bdcd8170SKalle Valo len_remain -= tx_len; 448bdcd8170SKalle Valo } 449bdcd8170SKalle Valo 450bdcd8170SKalle Valo return 0; 451bdcd8170SKalle Valo } 452bdcd8170SKalle Valo 453bdcd8170SKalle Valo int ath6kl_bmi_lz_stream_start(struct ath6kl *ar, u32 addr) 454bdcd8170SKalle Valo { 455bdcd8170SKalle Valo u32 cid = BMI_LZ_STREAM_START; 456bdcd8170SKalle Valo int ret; 457bdcd8170SKalle Valo u32 offset; 458bdcd8170SKalle Valo u16 size; 459bdcd8170SKalle Valo 460bdcd8170SKalle Valo if (ar->bmi.done_sent) { 461bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 462bdcd8170SKalle Valo return -EACCES; 463bdcd8170SKalle Valo } 464bdcd8170SKalle Valo 465bdcd8170SKalle Valo size = sizeof(cid) + sizeof(addr); 4661f4c894dSKalle Valo if (size > ar->bmi.max_cmd_size) { 467bdcd8170SKalle Valo WARN_ON(1); 468bdcd8170SKalle Valo return -EINVAL; 469bdcd8170SKalle Valo } 470bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 471bdcd8170SKalle Valo 472bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, 473bdcd8170SKalle Valo "bmi LZ stream start: addr: 0x%x)\n", 474bdcd8170SKalle Valo addr); 475bdcd8170SKalle Valo 476bdcd8170SKalle Valo offset = 0; 477bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 478bdcd8170SKalle Valo offset += sizeof(cid); 479bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 480bdcd8170SKalle Valo offset += sizeof(addr); 481bdcd8170SKalle Valo 48266b693c3SKalle Valo ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 483bdcd8170SKalle Valo if (ret) { 484bdcd8170SKalle Valo ath6kl_err("Unable to start LZ stream to the device: %d\n", 485bdcd8170SKalle Valo ret); 486bdcd8170SKalle Valo return ret; 487bdcd8170SKalle Valo } 488bdcd8170SKalle Valo 489bdcd8170SKalle Valo return 0; 490bdcd8170SKalle Valo } 491bdcd8170SKalle Valo 492bdcd8170SKalle Valo int ath6kl_bmi_fast_download(struct ath6kl *ar, u32 addr, u8 *buf, u32 len) 493bdcd8170SKalle Valo { 494bdcd8170SKalle Valo int ret; 495bdcd8170SKalle Valo u32 last_word = 0; 496bdcd8170SKalle Valo u32 last_word_offset = len & ~0x3; 497bdcd8170SKalle Valo u32 unaligned_bytes = len & 0x3; 498bdcd8170SKalle Valo 499bdcd8170SKalle Valo ret = ath6kl_bmi_lz_stream_start(ar, addr); 500bdcd8170SKalle Valo if (ret) 501bdcd8170SKalle Valo return ret; 502bdcd8170SKalle Valo 503bdcd8170SKalle Valo if (unaligned_bytes) { 504bdcd8170SKalle Valo /* copy the last word into a zero padded buffer */ 505bdcd8170SKalle Valo memcpy(&last_word, &buf[last_word_offset], unaligned_bytes); 506bdcd8170SKalle Valo } 507bdcd8170SKalle Valo 508bdcd8170SKalle Valo ret = ath6kl_bmi_lz_data(ar, buf, last_word_offset); 509bdcd8170SKalle Valo if (ret) 510bdcd8170SKalle Valo return ret; 511bdcd8170SKalle Valo 512bdcd8170SKalle Valo if (unaligned_bytes) 513bdcd8170SKalle Valo ret = ath6kl_bmi_lz_data(ar, (u8 *)&last_word, 4); 514bdcd8170SKalle Valo 515bdcd8170SKalle Valo if (!ret) { 516bdcd8170SKalle Valo /* Close compressed stream and open a new (fake) one. 517bdcd8170SKalle Valo * This serves mainly to flush Target caches. */ 518bdcd8170SKalle Valo ret = ath6kl_bmi_lz_stream_start(ar, 0x00); 519bdcd8170SKalle Valo } 520bdcd8170SKalle Valo return ret; 521bdcd8170SKalle Valo } 522bdcd8170SKalle Valo 5235fe4dffbSKalle Valo void ath6kl_bmi_reset(struct ath6kl *ar) 5245fe4dffbSKalle Valo { 5255fe4dffbSKalle Valo ar->bmi.done_sent = false; 5265fe4dffbSKalle Valo } 5275fe4dffbSKalle Valo 528bdcd8170SKalle Valo int ath6kl_bmi_init(struct ath6kl *ar) 529bdcd8170SKalle Valo { 5301f4c894dSKalle Valo if (WARN_ON(ar->bmi.max_data_size == 0)) 5311f4c894dSKalle Valo return -EINVAL; 532bdcd8170SKalle Valo 5331f4c894dSKalle Valo /* cmd + addr + len + data_size */ 5341f4c894dSKalle Valo ar->bmi.max_cmd_size = ar->bmi.max_data_size + (sizeof(u32) * 3); 5351f4c894dSKalle Valo 5361f4c894dSKalle Valo ar->bmi.cmd_buf = kzalloc(ar->bmi.max_cmd_size, GFP_ATOMIC); 537bdcd8170SKalle Valo if (!ar->bmi.cmd_buf) 538bdcd8170SKalle Valo return -ENOMEM; 539bdcd8170SKalle Valo 540bdcd8170SKalle Valo return 0; 541bdcd8170SKalle Valo } 542bdcd8170SKalle Valo 543bdcd8170SKalle Valo void ath6kl_bmi_cleanup(struct ath6kl *ar) 544bdcd8170SKalle Valo { 545bdcd8170SKalle Valo kfree(ar->bmi.cmd_buf); 546bdcd8170SKalle Valo ar->bmi.cmd_buf = NULL; 547bdcd8170SKalle Valo } 548