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 1201f4c894dSKalle Valo size = ar->bmi.max_data_size + sizeof(cid) + sizeof(addr) + sizeof(len); 1211f4c894dSKalle Valo if (size > ar->bmi.max_cmd_size) { 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) { 1341f4c894dSKalle Valo rx_len = (len_remain < ar->bmi.max_data_size) ? 1351f4c894dSKalle Valo len_remain : ar->bmi.max_data_size; 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); 1701f4c894dSKalle Valo u8 aligned_buf[400]; 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 1781f4c894dSKalle Valo if ((ar->bmi.max_data_size + header) > ar->bmi.max_cmd_size) { 179bdcd8170SKalle Valo WARN_ON(1); 180bdcd8170SKalle Valo return -EINVAL; 181bdcd8170SKalle Valo } 182bdcd8170SKalle Valo 1831f4c894dSKalle Valo if (WARN_ON(ar->bmi.max_data_size > sizeof(aligned_buf))) 1841f4c894dSKalle Valo return -E2BIG; 1851f4c894dSKalle Valo 1861f4c894dSKalle Valo memset(ar->bmi.cmd_buf, 0, ar->bmi.max_data_size + header); 187bdcd8170SKalle Valo 188bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, 189bdcd8170SKalle Valo "bmi write memory: addr: 0x%x, len: %d\n", addr, len); 190bdcd8170SKalle Valo 191bdcd8170SKalle Valo len_remain = len; 192bdcd8170SKalle Valo while (len_remain) { 193bdcd8170SKalle Valo src = &buf[len - len_remain]; 194bdcd8170SKalle Valo 1951f4c894dSKalle Valo if (len_remain < (ar->bmi.max_data_size - header)) { 196bdcd8170SKalle Valo if (len_remain & 3) { 197bdcd8170SKalle Valo /* align it with 4 bytes */ 198bdcd8170SKalle Valo len_remain = len_remain + 199bdcd8170SKalle Valo (4 - (len_remain & 3)); 200bdcd8170SKalle Valo memcpy(aligned_buf, src, len_remain); 201bdcd8170SKalle Valo src = aligned_buf; 202bdcd8170SKalle Valo } 203bdcd8170SKalle Valo tx_len = len_remain; 204bdcd8170SKalle Valo } else { 2051f4c894dSKalle Valo tx_len = (ar->bmi.max_data_size - header); 206bdcd8170SKalle Valo } 207bdcd8170SKalle Valo 208bdcd8170SKalle Valo offset = 0; 209bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 210bdcd8170SKalle Valo offset += sizeof(cid); 211bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 212bdcd8170SKalle Valo offset += sizeof(addr); 213bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len)); 214bdcd8170SKalle Valo offset += sizeof(tx_len); 215bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), src, tx_len); 216bdcd8170SKalle Valo offset += tx_len; 217bdcd8170SKalle Valo 21866b693c3SKalle Valo ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 219bdcd8170SKalle Valo if (ret) { 220bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", 221bdcd8170SKalle Valo ret); 222bdcd8170SKalle Valo return ret; 223bdcd8170SKalle Valo } 224bdcd8170SKalle Valo len_remain -= tx_len; addr += tx_len; 225bdcd8170SKalle Valo } 226bdcd8170SKalle Valo 227bdcd8170SKalle Valo return 0; 228bdcd8170SKalle Valo } 229bdcd8170SKalle Valo 230bdcd8170SKalle Valo int ath6kl_bmi_execute(struct ath6kl *ar, u32 addr, u32 *param) 231bdcd8170SKalle Valo { 232bdcd8170SKalle Valo u32 cid = BMI_EXECUTE; 233bdcd8170SKalle Valo int ret; 234bdcd8170SKalle Valo u32 offset; 235bdcd8170SKalle Valo u16 size; 236bdcd8170SKalle Valo 237bdcd8170SKalle Valo if (ar->bmi.done_sent) { 238bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 239bdcd8170SKalle Valo return -EACCES; 240bdcd8170SKalle Valo } 241bdcd8170SKalle Valo 242bdcd8170SKalle Valo size = sizeof(cid) + sizeof(addr) + sizeof(param); 2431f4c894dSKalle Valo if (size > ar->bmi.max_cmd_size) { 244bdcd8170SKalle Valo WARN_ON(1); 245bdcd8170SKalle Valo return -EINVAL; 246bdcd8170SKalle Valo } 247bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 248bdcd8170SKalle Valo 249bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, "bmi execute: addr: 0x%x, param: %d)\n", 250bdcd8170SKalle Valo addr, *param); 251bdcd8170SKalle Valo 252bdcd8170SKalle Valo offset = 0; 253bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 254bdcd8170SKalle Valo offset += sizeof(cid); 255bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 256bdcd8170SKalle Valo offset += sizeof(addr); 257bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), param, sizeof(*param)); 258bdcd8170SKalle Valo offset += sizeof(*param); 259bdcd8170SKalle Valo 26066b693c3SKalle Valo ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 261bdcd8170SKalle Valo if (ret) { 262bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", ret); 263bdcd8170SKalle Valo return ret; 264bdcd8170SKalle Valo } 265bdcd8170SKalle Valo 26666b693c3SKalle Valo ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, sizeof(*param)); 267bdcd8170SKalle Valo if (ret) { 268bdcd8170SKalle Valo ath6kl_err("Unable to read from the device: %d\n", ret); 269bdcd8170SKalle Valo return ret; 270bdcd8170SKalle Valo } 271bdcd8170SKalle Valo 272bdcd8170SKalle Valo memcpy(param, ar->bmi.cmd_buf, sizeof(*param)); 273bdcd8170SKalle Valo 274bdcd8170SKalle Valo return 0; 275bdcd8170SKalle Valo } 276bdcd8170SKalle Valo 277bdcd8170SKalle Valo int ath6kl_bmi_set_app_start(struct ath6kl *ar, u32 addr) 278bdcd8170SKalle Valo { 279bdcd8170SKalle Valo u32 cid = BMI_SET_APP_START; 280bdcd8170SKalle Valo int ret; 281bdcd8170SKalle Valo u32 offset; 282bdcd8170SKalle Valo u16 size; 283bdcd8170SKalle Valo 284bdcd8170SKalle Valo if (ar->bmi.done_sent) { 285bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 286bdcd8170SKalle Valo return -EACCES; 287bdcd8170SKalle Valo } 288bdcd8170SKalle Valo 289bdcd8170SKalle Valo size = sizeof(cid) + sizeof(addr); 2901f4c894dSKalle Valo if (size > ar->bmi.max_cmd_size) { 291bdcd8170SKalle Valo WARN_ON(1); 292bdcd8170SKalle Valo return -EINVAL; 293bdcd8170SKalle Valo } 294bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 295bdcd8170SKalle Valo 296bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, "bmi set app start: addr: 0x%x\n", addr); 297bdcd8170SKalle Valo 298bdcd8170SKalle Valo offset = 0; 299bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 300bdcd8170SKalle Valo offset += sizeof(cid); 301bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 302bdcd8170SKalle Valo offset += sizeof(addr); 303bdcd8170SKalle Valo 30466b693c3SKalle Valo ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 305bdcd8170SKalle Valo if (ret) { 306bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", ret); 307bdcd8170SKalle Valo return ret; 308bdcd8170SKalle Valo } 309bdcd8170SKalle Valo 310bdcd8170SKalle Valo return 0; 311bdcd8170SKalle Valo } 312bdcd8170SKalle Valo 313bdcd8170SKalle Valo int ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param) 314bdcd8170SKalle Valo { 315bdcd8170SKalle Valo u32 cid = BMI_READ_SOC_REGISTER; 316bdcd8170SKalle Valo int ret; 317bdcd8170SKalle Valo u32 offset; 318bdcd8170SKalle Valo u16 size; 319bdcd8170SKalle Valo 320bdcd8170SKalle Valo if (ar->bmi.done_sent) { 321bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 322bdcd8170SKalle Valo return -EACCES; 323bdcd8170SKalle Valo } 324bdcd8170SKalle Valo 325bdcd8170SKalle Valo size = sizeof(cid) + sizeof(addr); 3261f4c894dSKalle Valo if (size > ar->bmi.max_cmd_size) { 327bdcd8170SKalle Valo WARN_ON(1); 328bdcd8170SKalle Valo return -EINVAL; 329bdcd8170SKalle Valo } 330bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 331bdcd8170SKalle Valo 332bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, "bmi read SOC reg: addr: 0x%x\n", addr); 333bdcd8170SKalle Valo 334bdcd8170SKalle Valo offset = 0; 335bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 336bdcd8170SKalle Valo offset += sizeof(cid); 337bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 338bdcd8170SKalle Valo offset += sizeof(addr); 339bdcd8170SKalle Valo 34066b693c3SKalle Valo ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 341bdcd8170SKalle Valo if (ret) { 342bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", ret); 343bdcd8170SKalle Valo return ret; 344bdcd8170SKalle Valo } 345bdcd8170SKalle Valo 34666b693c3SKalle Valo ret = ath6kl_hif_bmi_read(ar, ar->bmi.cmd_buf, sizeof(*param)); 347bdcd8170SKalle Valo if (ret) { 348bdcd8170SKalle Valo ath6kl_err("Unable to read from the device: %d\n", ret); 349bdcd8170SKalle Valo return ret; 350bdcd8170SKalle Valo } 351bdcd8170SKalle Valo memcpy(param, ar->bmi.cmd_buf, sizeof(*param)); 352bdcd8170SKalle Valo 353bdcd8170SKalle Valo return 0; 354bdcd8170SKalle Valo } 355bdcd8170SKalle Valo 356bdcd8170SKalle Valo int ath6kl_bmi_reg_write(struct ath6kl *ar, u32 addr, u32 param) 357bdcd8170SKalle Valo { 358bdcd8170SKalle Valo u32 cid = BMI_WRITE_SOC_REGISTER; 359bdcd8170SKalle Valo int ret; 360bdcd8170SKalle Valo u32 offset; 361bdcd8170SKalle Valo u16 size; 362bdcd8170SKalle Valo 363bdcd8170SKalle Valo if (ar->bmi.done_sent) { 364bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 365bdcd8170SKalle Valo return -EACCES; 366bdcd8170SKalle Valo } 367bdcd8170SKalle Valo 368bdcd8170SKalle Valo size = sizeof(cid) + sizeof(addr) + sizeof(param); 3691f4c894dSKalle Valo if (size > ar->bmi.max_cmd_size) { 370bdcd8170SKalle Valo WARN_ON(1); 371bdcd8170SKalle Valo return -EINVAL; 372bdcd8170SKalle Valo } 373bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 374bdcd8170SKalle Valo 375bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, 376bdcd8170SKalle Valo "bmi write SOC reg: addr: 0x%x, param: %d\n", 377bdcd8170SKalle Valo addr, param); 378bdcd8170SKalle Valo 379bdcd8170SKalle Valo offset = 0; 380bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 381bdcd8170SKalle Valo offset += sizeof(cid); 382bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 383bdcd8170SKalle Valo offset += sizeof(addr); 384bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), ¶m, sizeof(param)); 385bdcd8170SKalle Valo offset += sizeof(param); 386bdcd8170SKalle Valo 38766b693c3SKalle Valo ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 388bdcd8170SKalle Valo if (ret) { 389bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", ret); 390bdcd8170SKalle Valo return ret; 391bdcd8170SKalle Valo } 392bdcd8170SKalle Valo 393bdcd8170SKalle Valo return 0; 394bdcd8170SKalle Valo } 395bdcd8170SKalle Valo 396bdcd8170SKalle Valo int ath6kl_bmi_lz_data(struct ath6kl *ar, u8 *buf, u32 len) 397bdcd8170SKalle Valo { 398bdcd8170SKalle Valo u32 cid = BMI_LZ_DATA; 399bdcd8170SKalle Valo int ret; 400bdcd8170SKalle Valo u32 offset; 401bdcd8170SKalle Valo u32 len_remain, tx_len; 402bdcd8170SKalle Valo const u32 header = sizeof(cid) + sizeof(len); 403bdcd8170SKalle Valo u16 size; 404bdcd8170SKalle Valo 405bdcd8170SKalle Valo if (ar->bmi.done_sent) { 406bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 407bdcd8170SKalle Valo return -EACCES; 408bdcd8170SKalle Valo } 409bdcd8170SKalle Valo 4101f4c894dSKalle Valo size = ar->bmi.max_data_size + header; 4111f4c894dSKalle Valo if (size > ar->bmi.max_cmd_size) { 412bdcd8170SKalle Valo WARN_ON(1); 413bdcd8170SKalle Valo return -EINVAL; 414bdcd8170SKalle Valo } 415bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 416bdcd8170SKalle Valo 417bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, "bmi send LZ data: len: %d)\n", 418bdcd8170SKalle Valo len); 419bdcd8170SKalle Valo 420bdcd8170SKalle Valo len_remain = len; 421bdcd8170SKalle Valo while (len_remain) { 4221f4c894dSKalle Valo tx_len = (len_remain < (ar->bmi.max_data_size - header)) ? 4231f4c894dSKalle Valo len_remain : (ar->bmi.max_data_size - header); 424bdcd8170SKalle Valo 425bdcd8170SKalle Valo offset = 0; 426bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 427bdcd8170SKalle Valo offset += sizeof(cid); 428bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len)); 429bdcd8170SKalle Valo offset += sizeof(tx_len); 430bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &buf[len - len_remain], 431bdcd8170SKalle Valo tx_len); 432bdcd8170SKalle Valo offset += tx_len; 433bdcd8170SKalle Valo 43466b693c3SKalle Valo ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 435bdcd8170SKalle Valo if (ret) { 436bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", 437bdcd8170SKalle Valo ret); 438bdcd8170SKalle Valo return ret; 439bdcd8170SKalle Valo } 440bdcd8170SKalle Valo 441bdcd8170SKalle Valo len_remain -= tx_len; 442bdcd8170SKalle Valo } 443bdcd8170SKalle Valo 444bdcd8170SKalle Valo return 0; 445bdcd8170SKalle Valo } 446bdcd8170SKalle Valo 447bdcd8170SKalle Valo int ath6kl_bmi_lz_stream_start(struct ath6kl *ar, u32 addr) 448bdcd8170SKalle Valo { 449bdcd8170SKalle Valo u32 cid = BMI_LZ_STREAM_START; 450bdcd8170SKalle Valo int ret; 451bdcd8170SKalle Valo u32 offset; 452bdcd8170SKalle Valo u16 size; 453bdcd8170SKalle Valo 454bdcd8170SKalle Valo if (ar->bmi.done_sent) { 455bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 456bdcd8170SKalle Valo return -EACCES; 457bdcd8170SKalle Valo } 458bdcd8170SKalle Valo 459bdcd8170SKalle Valo size = sizeof(cid) + sizeof(addr); 4601f4c894dSKalle Valo if (size > ar->bmi.max_cmd_size) { 461bdcd8170SKalle Valo WARN_ON(1); 462bdcd8170SKalle Valo return -EINVAL; 463bdcd8170SKalle Valo } 464bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 465bdcd8170SKalle Valo 466bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, 467bdcd8170SKalle Valo "bmi LZ stream start: addr: 0x%x)\n", 468bdcd8170SKalle Valo addr); 469bdcd8170SKalle Valo 470bdcd8170SKalle Valo offset = 0; 471bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 472bdcd8170SKalle Valo offset += sizeof(cid); 473bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 474bdcd8170SKalle Valo offset += sizeof(addr); 475bdcd8170SKalle Valo 47666b693c3SKalle Valo ret = ath6kl_hif_bmi_write(ar, ar->bmi.cmd_buf, offset); 477bdcd8170SKalle Valo if (ret) { 478bdcd8170SKalle Valo ath6kl_err("Unable to start LZ stream to the device: %d\n", 479bdcd8170SKalle Valo ret); 480bdcd8170SKalle Valo return ret; 481bdcd8170SKalle Valo } 482bdcd8170SKalle Valo 483bdcd8170SKalle Valo return 0; 484bdcd8170SKalle Valo } 485bdcd8170SKalle Valo 486bdcd8170SKalle Valo int ath6kl_bmi_fast_download(struct ath6kl *ar, u32 addr, u8 *buf, u32 len) 487bdcd8170SKalle Valo { 488bdcd8170SKalle Valo int ret; 489bdcd8170SKalle Valo u32 last_word = 0; 490bdcd8170SKalle Valo u32 last_word_offset = len & ~0x3; 491bdcd8170SKalle Valo u32 unaligned_bytes = len & 0x3; 492bdcd8170SKalle Valo 493bdcd8170SKalle Valo ret = ath6kl_bmi_lz_stream_start(ar, addr); 494bdcd8170SKalle Valo if (ret) 495bdcd8170SKalle Valo return ret; 496bdcd8170SKalle Valo 497bdcd8170SKalle Valo if (unaligned_bytes) { 498bdcd8170SKalle Valo /* copy the last word into a zero padded buffer */ 499bdcd8170SKalle Valo memcpy(&last_word, &buf[last_word_offset], unaligned_bytes); 500bdcd8170SKalle Valo } 501bdcd8170SKalle Valo 502bdcd8170SKalle Valo ret = ath6kl_bmi_lz_data(ar, buf, last_word_offset); 503bdcd8170SKalle Valo if (ret) 504bdcd8170SKalle Valo return ret; 505bdcd8170SKalle Valo 506bdcd8170SKalle Valo if (unaligned_bytes) 507bdcd8170SKalle Valo ret = ath6kl_bmi_lz_data(ar, (u8 *)&last_word, 4); 508bdcd8170SKalle Valo 509bdcd8170SKalle Valo if (!ret) { 510bdcd8170SKalle Valo /* Close compressed stream and open a new (fake) one. 511bdcd8170SKalle Valo * This serves mainly to flush Target caches. */ 512bdcd8170SKalle Valo ret = ath6kl_bmi_lz_stream_start(ar, 0x00); 513bdcd8170SKalle Valo } 514bdcd8170SKalle Valo return ret; 515bdcd8170SKalle Valo } 516bdcd8170SKalle Valo 5175fe4dffbSKalle Valo void ath6kl_bmi_reset(struct ath6kl *ar) 5185fe4dffbSKalle Valo { 5195fe4dffbSKalle Valo ar->bmi.done_sent = false; 5205fe4dffbSKalle Valo } 5215fe4dffbSKalle Valo 522bdcd8170SKalle Valo int ath6kl_bmi_init(struct ath6kl *ar) 523bdcd8170SKalle Valo { 5241f4c894dSKalle Valo if (WARN_ON(ar->bmi.max_data_size == 0)) 5251f4c894dSKalle Valo return -EINVAL; 526bdcd8170SKalle Valo 5271f4c894dSKalle Valo /* cmd + addr + len + data_size */ 5281f4c894dSKalle Valo ar->bmi.max_cmd_size = ar->bmi.max_data_size + (sizeof(u32) * 3); 5291f4c894dSKalle Valo 5301f4c894dSKalle Valo ar->bmi.cmd_buf = kzalloc(ar->bmi.max_cmd_size, GFP_ATOMIC); 531bdcd8170SKalle Valo if (!ar->bmi.cmd_buf) 532bdcd8170SKalle Valo return -ENOMEM; 533bdcd8170SKalle Valo 534bdcd8170SKalle Valo return 0; 535bdcd8170SKalle Valo } 536bdcd8170SKalle Valo 537bdcd8170SKalle Valo void ath6kl_bmi_cleanup(struct ath6kl *ar) 538bdcd8170SKalle Valo { 539bdcd8170SKalle Valo kfree(ar->bmi.cmd_buf); 540bdcd8170SKalle Valo ar->bmi.cmd_buf = NULL; 541bdcd8170SKalle Valo } 542