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 6066b693c3SKalle Valo ret = ath6kl_hif_bmi_read(ar, (u8 *)&targ_info->version, 61cfc301edSKalle Valo sizeof(targ_info->version)); 62bdcd8170SKalle Valo if (ret) { 63bdcd8170SKalle Valo ath6kl_err("Unable to recv target info: %d\n", ret); 64bdcd8170SKalle Valo return ret; 65bdcd8170SKalle Valo } 66bdcd8170SKalle Valo 67bdcd8170SKalle Valo if (le32_to_cpu(targ_info->version) == TARGET_VERSION_SENTINAL) { 68bdcd8170SKalle Valo /* Determine how many bytes are in the Target's targ_info */ 6966b693c3SKalle Valo ret = ath6kl_hif_bmi_read(ar, 70bdcd8170SKalle Valo (u8 *)&targ_info->byte_count, 71cfc301edSKalle Valo sizeof(targ_info->byte_count)); 72bdcd8170SKalle Valo if (ret) { 73bdcd8170SKalle Valo ath6kl_err("unable to read target info byte count: %d\n", 74bdcd8170SKalle Valo ret); 75bdcd8170SKalle Valo return ret; 76bdcd8170SKalle Valo } 77bdcd8170SKalle Valo 78bdcd8170SKalle Valo /* 79bdcd8170SKalle Valo * The target's targ_info doesn't match the host's targ_info. 80bdcd8170SKalle Valo * We need to do some backwards compatibility to make this work. 81bdcd8170SKalle Valo */ 82bdcd8170SKalle Valo if (le32_to_cpu(targ_info->byte_count) != sizeof(*targ_info)) { 83bdcd8170SKalle Valo WARN_ON(1); 84bdcd8170SKalle Valo return -EINVAL; 85bdcd8170SKalle Valo } 86bdcd8170SKalle Valo 87bdcd8170SKalle Valo /* Read the remainder of the targ_info */ 8866b693c3SKalle Valo ret = ath6kl_hif_bmi_read(ar, 89bdcd8170SKalle Valo ((u8 *)targ_info) + 90bdcd8170SKalle Valo sizeof(targ_info->byte_count), 91bdcd8170SKalle Valo sizeof(*targ_info) - 92cfc301edSKalle Valo sizeof(targ_info->byte_count)); 93bdcd8170SKalle Valo 94bdcd8170SKalle Valo if (ret) { 95bdcd8170SKalle Valo ath6kl_err("Unable to read target info (%d bytes): %d\n", 96bdcd8170SKalle Valo targ_info->byte_count, ret); 97bdcd8170SKalle Valo return ret; 98bdcd8170SKalle Valo } 99bdcd8170SKalle Valo } 100bdcd8170SKalle Valo 101bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, "target info (ver: 0x%x type: 0x%x)\n", 102bdcd8170SKalle Valo targ_info->version, targ_info->type); 103bdcd8170SKalle Valo 104bdcd8170SKalle Valo return 0; 105bdcd8170SKalle Valo } 106bdcd8170SKalle Valo 107bdcd8170SKalle Valo int ath6kl_bmi_read(struct ath6kl *ar, u32 addr, u8 *buf, u32 len) 108bdcd8170SKalle Valo { 109bdcd8170SKalle Valo u32 cid = BMI_READ_MEMORY; 110bdcd8170SKalle Valo int ret; 111bdcd8170SKalle Valo u32 offset; 112bdcd8170SKalle Valo u32 len_remain, rx_len; 113bdcd8170SKalle Valo u16 size; 114bdcd8170SKalle Valo 115bdcd8170SKalle Valo if (ar->bmi.done_sent) { 116bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 117bdcd8170SKalle Valo return -EACCES; 118bdcd8170SKalle Valo } 119bdcd8170SKalle Valo 120bdcd8170SKalle Valo size = BMI_DATASZ_MAX + sizeof(cid) + sizeof(addr) + sizeof(len); 121bdcd8170SKalle Valo if (size > MAX_BMI_CMDBUF_SZ) { 122bdcd8170SKalle Valo WARN_ON(1); 123bdcd8170SKalle Valo return -EINVAL; 124bdcd8170SKalle Valo } 125bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 126bdcd8170SKalle Valo 127bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, 128bdcd8170SKalle Valo "bmi read memory: device: addr: 0x%x, len: %d\n", 129bdcd8170SKalle Valo addr, len); 130bdcd8170SKalle Valo 131bdcd8170SKalle Valo len_remain = len; 132bdcd8170SKalle Valo 133bdcd8170SKalle Valo while (len_remain) { 134bdcd8170SKalle Valo rx_len = (len_remain < BMI_DATASZ_MAX) ? 135bdcd8170SKalle Valo len_remain : BMI_DATASZ_MAX; 136bdcd8170SKalle Valo offset = 0; 137bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 138bdcd8170SKalle Valo offset += sizeof(cid); 139bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 140bdcd8170SKalle Valo offset += sizeof(addr); 141bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &rx_len, sizeof(rx_len)); 142bdcd8170SKalle Valo offset += sizeof(len); 143bdcd8170SKalle Valo 14466b693c3SKalle Valo ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 145bdcd8170SKalle Valo if (ret) { 146bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", 147bdcd8170SKalle Valo ret); 148bdcd8170SKalle Valo return ret; 149bdcd8170SKalle Valo } 15066b693c3SKalle Valo ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, rx_len); 151bdcd8170SKalle Valo if (ret) { 152bdcd8170SKalle Valo ath6kl_err("Unable to read from the device: %d\n", 153bdcd8170SKalle Valo ret); 154bdcd8170SKalle Valo return ret; 155bdcd8170SKalle Valo } 156bdcd8170SKalle Valo memcpy(&buf[len - len_remain], ar->bmi.cmd_buf, rx_len); 157bdcd8170SKalle Valo len_remain -= rx_len; addr += rx_len; 158bdcd8170SKalle Valo } 159bdcd8170SKalle Valo 160bdcd8170SKalle Valo return 0; 161bdcd8170SKalle Valo } 162bdcd8170SKalle Valo 163bdcd8170SKalle Valo int ath6kl_bmi_write(struct ath6kl *ar, u32 addr, u8 *buf, u32 len) 164bdcd8170SKalle Valo { 165bdcd8170SKalle Valo u32 cid = BMI_WRITE_MEMORY; 166bdcd8170SKalle Valo int ret; 167bdcd8170SKalle Valo u32 offset; 168bdcd8170SKalle Valo u32 len_remain, tx_len; 169bdcd8170SKalle Valo const u32 header = sizeof(cid) + sizeof(addr) + sizeof(len); 170bdcd8170SKalle Valo u8 aligned_buf[BMI_DATASZ_MAX]; 171bdcd8170SKalle Valo u8 *src; 172bdcd8170SKalle Valo 173bdcd8170SKalle Valo if (ar->bmi.done_sent) { 174bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 175bdcd8170SKalle Valo return -EACCES; 176bdcd8170SKalle Valo } 177bdcd8170SKalle Valo 178bdcd8170SKalle Valo if ((BMI_DATASZ_MAX + header) > MAX_BMI_CMDBUF_SZ) { 179bdcd8170SKalle Valo WARN_ON(1); 180bdcd8170SKalle Valo return -EINVAL; 181bdcd8170SKalle Valo } 182bdcd8170SKalle Valo 183bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, BMI_DATASZ_MAX + header); 184bdcd8170SKalle Valo 185bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, 186bdcd8170SKalle Valo "bmi write memory: addr: 0x%x, len: %d\n", addr, len); 187bdcd8170SKalle Valo 188bdcd8170SKalle Valo len_remain = len; 189bdcd8170SKalle Valo while (len_remain) { 190bdcd8170SKalle Valo src = &buf[len - len_remain]; 191bdcd8170SKalle Valo 192bdcd8170SKalle Valo if (len_remain < (BMI_DATASZ_MAX - header)) { 193bdcd8170SKalle Valo if (len_remain & 3) { 194bdcd8170SKalle Valo /* align it with 4 bytes */ 195bdcd8170SKalle Valo len_remain = len_remain + 196bdcd8170SKalle Valo (4 - (len_remain & 3)); 197bdcd8170SKalle Valo memcpy(aligned_buf, src, len_remain); 198bdcd8170SKalle Valo src = aligned_buf; 199bdcd8170SKalle Valo } 200bdcd8170SKalle Valo tx_len = len_remain; 201bdcd8170SKalle Valo } else { 202bdcd8170SKalle Valo tx_len = (BMI_DATASZ_MAX - header); 203bdcd8170SKalle Valo } 204bdcd8170SKalle Valo 205bdcd8170SKalle Valo offset = 0; 206bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 207bdcd8170SKalle Valo offset += sizeof(cid); 208bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 209bdcd8170SKalle Valo offset += sizeof(addr); 210bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len)); 211bdcd8170SKalle Valo offset += sizeof(tx_len); 212bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), src, tx_len); 213bdcd8170SKalle Valo offset += tx_len; 214bdcd8170SKalle Valo 21566b693c3SKalle Valo ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 216bdcd8170SKalle Valo if (ret) { 217bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", 218bdcd8170SKalle Valo ret); 219bdcd8170SKalle Valo return ret; 220bdcd8170SKalle Valo } 221bdcd8170SKalle Valo len_remain -= tx_len; addr += tx_len; 222bdcd8170SKalle Valo } 223bdcd8170SKalle Valo 224bdcd8170SKalle Valo return 0; 225bdcd8170SKalle Valo } 226bdcd8170SKalle Valo 227bdcd8170SKalle Valo int ath6kl_bmi_execute(struct ath6kl *ar, u32 addr, u32 *param) 228bdcd8170SKalle Valo { 229bdcd8170SKalle Valo u32 cid = BMI_EXECUTE; 230bdcd8170SKalle Valo int ret; 231bdcd8170SKalle Valo u32 offset; 232bdcd8170SKalle Valo u16 size; 233bdcd8170SKalle Valo 234bdcd8170SKalle Valo if (ar->bmi.done_sent) { 235bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 236bdcd8170SKalle Valo return -EACCES; 237bdcd8170SKalle Valo } 238bdcd8170SKalle Valo 239bdcd8170SKalle Valo size = sizeof(cid) + sizeof(addr) + sizeof(param); 240bdcd8170SKalle Valo if (size > MAX_BMI_CMDBUF_SZ) { 241bdcd8170SKalle Valo WARN_ON(1); 242bdcd8170SKalle Valo return -EINVAL; 243bdcd8170SKalle Valo } 244bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 245bdcd8170SKalle Valo 246bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, "bmi execute: addr: 0x%x, param: %d)\n", 247bdcd8170SKalle Valo addr, *param); 248bdcd8170SKalle Valo 249bdcd8170SKalle Valo offset = 0; 250bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 251bdcd8170SKalle Valo offset += sizeof(cid); 252bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 253bdcd8170SKalle Valo offset += sizeof(addr); 254bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), param, sizeof(*param)); 255bdcd8170SKalle Valo offset += sizeof(*param); 256bdcd8170SKalle Valo 25766b693c3SKalle Valo ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 258bdcd8170SKalle Valo if (ret) { 259bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", ret); 260bdcd8170SKalle Valo return ret; 261bdcd8170SKalle Valo } 262bdcd8170SKalle Valo 26366b693c3SKalle Valo ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, sizeof(*param)); 264bdcd8170SKalle Valo if (ret) { 265bdcd8170SKalle Valo ath6kl_err("Unable to read from the device: %d\n", ret); 266bdcd8170SKalle Valo return ret; 267bdcd8170SKalle Valo } 268bdcd8170SKalle Valo 269bdcd8170SKalle Valo memcpy(param, ar->bmi.cmd_buf, sizeof(*param)); 270bdcd8170SKalle Valo 271bdcd8170SKalle Valo return 0; 272bdcd8170SKalle Valo } 273bdcd8170SKalle Valo 274bdcd8170SKalle Valo int ath6kl_bmi_set_app_start(struct ath6kl *ar, u32 addr) 275bdcd8170SKalle Valo { 276bdcd8170SKalle Valo u32 cid = BMI_SET_APP_START; 277bdcd8170SKalle Valo int ret; 278bdcd8170SKalle Valo u32 offset; 279bdcd8170SKalle Valo u16 size; 280bdcd8170SKalle Valo 281bdcd8170SKalle Valo if (ar->bmi.done_sent) { 282bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 283bdcd8170SKalle Valo return -EACCES; 284bdcd8170SKalle Valo } 285bdcd8170SKalle Valo 286bdcd8170SKalle Valo size = sizeof(cid) + sizeof(addr); 287bdcd8170SKalle Valo if (size > MAX_BMI_CMDBUF_SZ) { 288bdcd8170SKalle Valo WARN_ON(1); 289bdcd8170SKalle Valo return -EINVAL; 290bdcd8170SKalle Valo } 291bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 292bdcd8170SKalle Valo 293bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, "bmi set app start: addr: 0x%x\n", addr); 294bdcd8170SKalle Valo 295bdcd8170SKalle Valo offset = 0; 296bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 297bdcd8170SKalle Valo offset += sizeof(cid); 298bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 299bdcd8170SKalle Valo offset += sizeof(addr); 300bdcd8170SKalle Valo 30166b693c3SKalle Valo ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 302bdcd8170SKalle Valo if (ret) { 303bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", ret); 304bdcd8170SKalle Valo return ret; 305bdcd8170SKalle Valo } 306bdcd8170SKalle Valo 307bdcd8170SKalle Valo return 0; 308bdcd8170SKalle Valo } 309bdcd8170SKalle Valo 310bdcd8170SKalle Valo int ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param) 311bdcd8170SKalle Valo { 312bdcd8170SKalle Valo u32 cid = BMI_READ_SOC_REGISTER; 313bdcd8170SKalle Valo int ret; 314bdcd8170SKalle Valo u32 offset; 315bdcd8170SKalle Valo u16 size; 316bdcd8170SKalle Valo 317bdcd8170SKalle Valo if (ar->bmi.done_sent) { 318bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 319bdcd8170SKalle Valo return -EACCES; 320bdcd8170SKalle Valo } 321bdcd8170SKalle Valo 322bdcd8170SKalle Valo size = sizeof(cid) + sizeof(addr); 323bdcd8170SKalle Valo if (size > MAX_BMI_CMDBUF_SZ) { 324bdcd8170SKalle Valo WARN_ON(1); 325bdcd8170SKalle Valo return -EINVAL; 326bdcd8170SKalle Valo } 327bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 328bdcd8170SKalle Valo 329bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, "bmi read SOC reg: addr: 0x%x\n", addr); 330bdcd8170SKalle Valo 331bdcd8170SKalle Valo offset = 0; 332bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 333bdcd8170SKalle Valo offset += sizeof(cid); 334bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 335bdcd8170SKalle Valo offset += sizeof(addr); 336bdcd8170SKalle Valo 33766b693c3SKalle Valo ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 338bdcd8170SKalle Valo if (ret) { 339bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", ret); 340bdcd8170SKalle Valo return ret; 341bdcd8170SKalle Valo } 342bdcd8170SKalle Valo 34366b693c3SKalle Valo ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, sizeof(*param)); 344bdcd8170SKalle Valo if (ret) { 345bdcd8170SKalle Valo ath6kl_err("Unable to read from the device: %d\n", ret); 346bdcd8170SKalle Valo return ret; 347bdcd8170SKalle Valo } 348bdcd8170SKalle Valo memcpy(param, ar->bmi.cmd_buf, sizeof(*param)); 349bdcd8170SKalle Valo 350bdcd8170SKalle Valo return 0; 351bdcd8170SKalle Valo } 352bdcd8170SKalle Valo 353bdcd8170SKalle Valo int ath6kl_bmi_reg_write(struct ath6kl *ar, u32 addr, u32 param) 354bdcd8170SKalle Valo { 355bdcd8170SKalle Valo u32 cid = BMI_WRITE_SOC_REGISTER; 356bdcd8170SKalle Valo int ret; 357bdcd8170SKalle Valo u32 offset; 358bdcd8170SKalle Valo u16 size; 359bdcd8170SKalle Valo 360bdcd8170SKalle Valo if (ar->bmi.done_sent) { 361bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 362bdcd8170SKalle Valo return -EACCES; 363bdcd8170SKalle Valo } 364bdcd8170SKalle Valo 365bdcd8170SKalle Valo size = sizeof(cid) + sizeof(addr) + sizeof(param); 366bdcd8170SKalle Valo if (size > MAX_BMI_CMDBUF_SZ) { 367bdcd8170SKalle Valo WARN_ON(1); 368bdcd8170SKalle Valo return -EINVAL; 369bdcd8170SKalle Valo } 370bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 371bdcd8170SKalle Valo 372bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, 373bdcd8170SKalle Valo "bmi write SOC reg: addr: 0x%x, param: %d\n", 374bdcd8170SKalle Valo addr, param); 375bdcd8170SKalle Valo 376bdcd8170SKalle Valo offset = 0; 377bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 378bdcd8170SKalle Valo offset += sizeof(cid); 379bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 380bdcd8170SKalle Valo offset += sizeof(addr); 381bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), ¶m, sizeof(param)); 382bdcd8170SKalle Valo offset += sizeof(param); 383bdcd8170SKalle Valo 38466b693c3SKalle Valo ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 385bdcd8170SKalle Valo if (ret) { 386bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", ret); 387bdcd8170SKalle Valo return ret; 388bdcd8170SKalle Valo } 389bdcd8170SKalle Valo 390bdcd8170SKalle Valo return 0; 391bdcd8170SKalle Valo } 392bdcd8170SKalle Valo 393bdcd8170SKalle Valo int ath6kl_bmi_lz_data(struct ath6kl *ar, u8 *buf, u32 len) 394bdcd8170SKalle Valo { 395bdcd8170SKalle Valo u32 cid = BMI_LZ_DATA; 396bdcd8170SKalle Valo int ret; 397bdcd8170SKalle Valo u32 offset; 398bdcd8170SKalle Valo u32 len_remain, tx_len; 399bdcd8170SKalle Valo const u32 header = sizeof(cid) + sizeof(len); 400bdcd8170SKalle Valo u16 size; 401bdcd8170SKalle Valo 402bdcd8170SKalle Valo if (ar->bmi.done_sent) { 403bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 404bdcd8170SKalle Valo return -EACCES; 405bdcd8170SKalle Valo } 406bdcd8170SKalle Valo 407bdcd8170SKalle Valo size = BMI_DATASZ_MAX + header; 408bdcd8170SKalle Valo if (size > MAX_BMI_CMDBUF_SZ) { 409bdcd8170SKalle Valo WARN_ON(1); 410bdcd8170SKalle Valo return -EINVAL; 411bdcd8170SKalle Valo } 412bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 413bdcd8170SKalle Valo 414bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, "bmi send LZ data: len: %d)\n", 415bdcd8170SKalle Valo len); 416bdcd8170SKalle Valo 417bdcd8170SKalle Valo len_remain = len; 418bdcd8170SKalle Valo while (len_remain) { 419bdcd8170SKalle Valo tx_len = (len_remain < (BMI_DATASZ_MAX - header)) ? 420bdcd8170SKalle Valo len_remain : (BMI_DATASZ_MAX - header); 421bdcd8170SKalle Valo 422bdcd8170SKalle Valo offset = 0; 423bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 424bdcd8170SKalle Valo offset += sizeof(cid); 425bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len)); 426bdcd8170SKalle Valo offset += sizeof(tx_len); 427bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &buf[len - len_remain], 428bdcd8170SKalle Valo tx_len); 429bdcd8170SKalle Valo offset += tx_len; 430bdcd8170SKalle Valo 43166b693c3SKalle Valo ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 432bdcd8170SKalle Valo if (ret) { 433bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", 434bdcd8170SKalle Valo ret); 435bdcd8170SKalle Valo return ret; 436bdcd8170SKalle Valo } 437bdcd8170SKalle Valo 438bdcd8170SKalle Valo len_remain -= tx_len; 439bdcd8170SKalle Valo } 440bdcd8170SKalle Valo 441bdcd8170SKalle Valo return 0; 442bdcd8170SKalle Valo } 443bdcd8170SKalle Valo 444bdcd8170SKalle Valo int ath6kl_bmi_lz_stream_start(struct ath6kl *ar, u32 addr) 445bdcd8170SKalle Valo { 446bdcd8170SKalle Valo u32 cid = BMI_LZ_STREAM_START; 447bdcd8170SKalle Valo int ret; 448bdcd8170SKalle Valo u32 offset; 449bdcd8170SKalle Valo u16 size; 450bdcd8170SKalle Valo 451bdcd8170SKalle Valo if (ar->bmi.done_sent) { 452bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 453bdcd8170SKalle Valo return -EACCES; 454bdcd8170SKalle Valo } 455bdcd8170SKalle Valo 456bdcd8170SKalle Valo size = sizeof(cid) + sizeof(addr); 457bdcd8170SKalle Valo if (size > MAX_BMI_CMDBUF_SZ) { 458bdcd8170SKalle Valo WARN_ON(1); 459bdcd8170SKalle Valo return -EINVAL; 460bdcd8170SKalle Valo } 461bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 462bdcd8170SKalle Valo 463bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, 464bdcd8170SKalle Valo "bmi LZ stream start: addr: 0x%x)\n", 465bdcd8170SKalle Valo addr); 466bdcd8170SKalle Valo 467bdcd8170SKalle Valo offset = 0; 468bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 469bdcd8170SKalle Valo offset += sizeof(cid); 470bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 471bdcd8170SKalle Valo offset += sizeof(addr); 472bdcd8170SKalle Valo 47366b693c3SKalle Valo ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 474bdcd8170SKalle Valo if (ret) { 475bdcd8170SKalle Valo ath6kl_err("Unable to start LZ stream to the device: %d\n", 476bdcd8170SKalle Valo ret); 477bdcd8170SKalle Valo return ret; 478bdcd8170SKalle Valo } 479bdcd8170SKalle Valo 480bdcd8170SKalle Valo return 0; 481bdcd8170SKalle Valo } 482bdcd8170SKalle Valo 483bdcd8170SKalle Valo int ath6kl_bmi_fast_download(struct ath6kl *ar, u32 addr, u8 *buf, u32 len) 484bdcd8170SKalle Valo { 485bdcd8170SKalle Valo int ret; 486bdcd8170SKalle Valo u32 last_word = 0; 487bdcd8170SKalle Valo u32 last_word_offset = len & ~0x3; 488bdcd8170SKalle Valo u32 unaligned_bytes = len & 0x3; 489bdcd8170SKalle Valo 490bdcd8170SKalle Valo ret = ath6kl_bmi_lz_stream_start(ar, addr); 491bdcd8170SKalle Valo if (ret) 492bdcd8170SKalle Valo return ret; 493bdcd8170SKalle Valo 494bdcd8170SKalle Valo if (unaligned_bytes) { 495bdcd8170SKalle Valo /* copy the last word into a zero padded buffer */ 496bdcd8170SKalle Valo memcpy(&last_word, &buf[last_word_offset], unaligned_bytes); 497bdcd8170SKalle Valo } 498bdcd8170SKalle Valo 499bdcd8170SKalle Valo ret = ath6kl_bmi_lz_data(ar, buf, last_word_offset); 500bdcd8170SKalle Valo if (ret) 501bdcd8170SKalle Valo return ret; 502bdcd8170SKalle Valo 503bdcd8170SKalle Valo if (unaligned_bytes) 504bdcd8170SKalle Valo ret = ath6kl_bmi_lz_data(ar, (u8 *)&last_word, 4); 505bdcd8170SKalle Valo 506bdcd8170SKalle Valo if (!ret) { 507bdcd8170SKalle Valo /* Close compressed stream and open a new (fake) one. 508bdcd8170SKalle Valo * This serves mainly to flush Target caches. */ 509bdcd8170SKalle Valo ret = ath6kl_bmi_lz_stream_start(ar, 0x00); 510bdcd8170SKalle Valo } 511bdcd8170SKalle Valo return ret; 512bdcd8170SKalle Valo } 513bdcd8170SKalle Valo 5145fe4dffbSKalle Valo void ath6kl_bmi_reset(struct ath6kl *ar) 5155fe4dffbSKalle Valo { 5165fe4dffbSKalle Valo ar->bmi.done_sent = false; 5175fe4dffbSKalle Valo } 5185fe4dffbSKalle Valo 519bdcd8170SKalle Valo int ath6kl_bmi_init(struct ath6kl *ar) 520bdcd8170SKalle Valo { 521bdcd8170SKalle Valo ar->bmi.cmd_buf = kzalloc(MAX_BMI_CMDBUF_SZ, GFP_ATOMIC); 522bdcd8170SKalle Valo 523bdcd8170SKalle Valo if (!ar->bmi.cmd_buf) 524bdcd8170SKalle Valo return -ENOMEM; 525bdcd8170SKalle Valo 526bdcd8170SKalle Valo return 0; 527bdcd8170SKalle Valo } 528bdcd8170SKalle Valo 529bdcd8170SKalle Valo void ath6kl_bmi_cleanup(struct ath6kl *ar) 530bdcd8170SKalle Valo { 531bdcd8170SKalle Valo kfree(ar->bmi.cmd_buf); 532bdcd8170SKalle Valo ar->bmi.cmd_buf = NULL; 533bdcd8170SKalle Valo } 534