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 static int ath6kl_get_bmi_cmd_credits(struct ath6kl *ar) 23bdcd8170SKalle Valo { 24bdcd8170SKalle Valo u32 addr; 25bdcd8170SKalle Valo unsigned long timeout; 26bdcd8170SKalle Valo int ret; 27bdcd8170SKalle Valo 28bdcd8170SKalle Valo ar->bmi.cmd_credits = 0; 29bdcd8170SKalle Valo 30bdcd8170SKalle Valo /* Read the counter register to get the command credits */ 31bdcd8170SKalle Valo addr = COUNT_DEC_ADDRESS + (HTC_MAILBOX_NUM_MAX + ENDPOINT1) * 4; 32bdcd8170SKalle Valo 33bdcd8170SKalle Valo timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT); 34bdcd8170SKalle Valo while (time_before(jiffies, timeout) && !ar->bmi.cmd_credits) { 35bdcd8170SKalle Valo 36bdcd8170SKalle Valo /* 37bdcd8170SKalle Valo * Hit the credit counter with a 4-byte access, the first byte 38bdcd8170SKalle Valo * read will hit the counter and cause a decrement, while the 39bdcd8170SKalle Valo * remaining 3 bytes has no effect. The rationale behind this 40bdcd8170SKalle Valo * is to make all HIF accesses 4-byte aligned. 41bdcd8170SKalle Valo */ 42bdcd8170SKalle Valo ret = hif_read_write_sync(ar, addr, 43bdcd8170SKalle Valo (u8 *)&ar->bmi.cmd_credits, 4, 44bdcd8170SKalle Valo HIF_RD_SYNC_BYTE_INC); 45bdcd8170SKalle Valo if (ret) { 46bdcd8170SKalle Valo ath6kl_err("Unable to decrement the command credit count register: %d\n", 47bdcd8170SKalle Valo ret); 48bdcd8170SKalle Valo return ret; 49bdcd8170SKalle Valo } 50bdcd8170SKalle Valo 51bdcd8170SKalle Valo /* The counter is only 8 bits. 52bdcd8170SKalle Valo * Ignore anything in the upper 3 bytes 53bdcd8170SKalle Valo */ 54bdcd8170SKalle Valo ar->bmi.cmd_credits &= 0xFF; 55bdcd8170SKalle Valo } 56bdcd8170SKalle Valo 57bdcd8170SKalle Valo if (!ar->bmi.cmd_credits) { 58bdcd8170SKalle Valo ath6kl_err("bmi communication timeout\n"); 59bdcd8170SKalle Valo return -ETIMEDOUT; 60bdcd8170SKalle Valo } 61bdcd8170SKalle Valo 62bdcd8170SKalle Valo return 0; 63bdcd8170SKalle Valo } 64bdcd8170SKalle Valo 65bdcd8170SKalle Valo static int ath6kl_bmi_get_rx_lkahd(struct ath6kl *ar, bool need_timeout) 66bdcd8170SKalle Valo { 67bdcd8170SKalle Valo unsigned long timeout; 68bdcd8170SKalle Valo u32 rx_word = 0; 69bdcd8170SKalle Valo int ret = 0; 70bdcd8170SKalle Valo 71bdcd8170SKalle Valo timeout = jiffies + msecs_to_jiffies(BMI_COMMUNICATION_TIMEOUT); 72bdcd8170SKalle Valo while ((!need_timeout || time_before(jiffies, timeout)) && !rx_word) { 73bdcd8170SKalle Valo ret = hif_read_write_sync(ar, RX_LOOKAHEAD_VALID_ADDRESS, 74bdcd8170SKalle Valo (u8 *)&rx_word, sizeof(rx_word), 75bdcd8170SKalle Valo HIF_RD_SYNC_BYTE_INC); 76bdcd8170SKalle Valo if (ret) { 77bdcd8170SKalle Valo ath6kl_err("unable to read RX_LOOKAHEAD_VALID\n"); 78bdcd8170SKalle Valo return ret; 79bdcd8170SKalle Valo } 80bdcd8170SKalle Valo 81bdcd8170SKalle Valo /* all we really want is one bit */ 82bdcd8170SKalle Valo rx_word &= (1 << ENDPOINT1); 83bdcd8170SKalle Valo } 84bdcd8170SKalle Valo 85bdcd8170SKalle Valo if (!rx_word) { 86bdcd8170SKalle Valo ath6kl_err("bmi_recv_buf FIFO empty\n"); 87bdcd8170SKalle Valo return -EINVAL; 88bdcd8170SKalle Valo } 89bdcd8170SKalle Valo 90bdcd8170SKalle Valo return ret; 91bdcd8170SKalle Valo } 92bdcd8170SKalle Valo 93bdcd8170SKalle Valo static int ath6kl_bmi_send_buf(struct ath6kl *ar, u8 *buf, u32 len) 94bdcd8170SKalle Valo { 95bdcd8170SKalle Valo int ret; 96bdcd8170SKalle Valo u32 addr; 97bdcd8170SKalle Valo 98bdcd8170SKalle Valo ret = ath6kl_get_bmi_cmd_credits(ar); 99bdcd8170SKalle Valo if (ret) 100bdcd8170SKalle Valo return ret; 101bdcd8170SKalle Valo 102bdcd8170SKalle Valo addr = ar->mbox_info.htc_addr; 103bdcd8170SKalle Valo 104bdcd8170SKalle Valo ret = hif_read_write_sync(ar, addr, buf, len, 105bdcd8170SKalle Valo HIF_WR_SYNC_BYTE_INC); 106bdcd8170SKalle Valo if (ret) 107bdcd8170SKalle Valo ath6kl_err("unable to send the bmi data to the device\n"); 108bdcd8170SKalle Valo 109bdcd8170SKalle Valo return ret; 110bdcd8170SKalle Valo } 111bdcd8170SKalle Valo 112bdcd8170SKalle Valo static int ath6kl_bmi_recv_buf(struct ath6kl *ar, 113bdcd8170SKalle Valo u8 *buf, u32 len, bool want_timeout) 114bdcd8170SKalle Valo { 115bdcd8170SKalle Valo int ret; 116bdcd8170SKalle Valo u32 addr; 117bdcd8170SKalle Valo 118bdcd8170SKalle Valo /* 119bdcd8170SKalle Valo * During normal bootup, small reads may be required. 120bdcd8170SKalle Valo * Rather than issue an HIF Read and then wait as the Target 121bdcd8170SKalle Valo * adds successive bytes to the FIFO, we wait here until 122bdcd8170SKalle Valo * we know that response data is available. 123bdcd8170SKalle Valo * 124bdcd8170SKalle Valo * This allows us to cleanly timeout on an unexpected 125bdcd8170SKalle Valo * Target failure rather than risk problems at the HIF level. 126bdcd8170SKalle Valo * In particular, this avoids SDIO timeouts and possibly garbage 127bdcd8170SKalle Valo * data on some host controllers. And on an interconnect 128bdcd8170SKalle Valo * such as Compact Flash (as well as some SDIO masters) which 129bdcd8170SKalle Valo * does not provide any indication on data timeout, it avoids 130bdcd8170SKalle Valo * a potential hang or garbage response. 131bdcd8170SKalle Valo * 132bdcd8170SKalle Valo * Synchronization is more difficult for reads larger than the 133bdcd8170SKalle Valo * size of the MBOX FIFO (128B), because the Target is unable 134bdcd8170SKalle Valo * to push the 129th byte of data until AFTER the Host posts an 135bdcd8170SKalle Valo * HIF Read and removes some FIFO data. So for large reads the 136bdcd8170SKalle Valo * Host proceeds to post an HIF Read BEFORE all the data is 137bdcd8170SKalle Valo * actually available to read. Fortunately, large BMI reads do 138bdcd8170SKalle Valo * not occur in practice -- they're supported for debug/development. 139bdcd8170SKalle Valo * 140bdcd8170SKalle Valo * So Host/Target BMI synchronization is divided into these cases: 141bdcd8170SKalle Valo * CASE 1: length < 4 142bdcd8170SKalle Valo * Should not happen 143bdcd8170SKalle Valo * 144bdcd8170SKalle Valo * CASE 2: 4 <= length <= 128 145bdcd8170SKalle Valo * Wait for first 4 bytes to be in FIFO 146bdcd8170SKalle Valo * If CONSERVATIVE_BMI_READ is enabled, also wait for 147bdcd8170SKalle Valo * a BMI command credit, which indicates that the ENTIRE 148bdcd8170SKalle Valo * response is available in the the FIFO 149bdcd8170SKalle Valo * 150bdcd8170SKalle Valo * CASE 3: length > 128 151bdcd8170SKalle Valo * Wait for the first 4 bytes to be in FIFO 152bdcd8170SKalle Valo * 153bdcd8170SKalle Valo * For most uses, a small timeout should be sufficient and we will 154bdcd8170SKalle Valo * usually see a response quickly; but there may be some unusual 155bdcd8170SKalle Valo * (debug) cases of BMI_EXECUTE where we want an larger timeout. 156bdcd8170SKalle Valo * For now, we use an unbounded busy loop while waiting for 157bdcd8170SKalle Valo * BMI_EXECUTE. 158bdcd8170SKalle Valo * 159bdcd8170SKalle Valo * If BMI_EXECUTE ever needs to support longer-latency execution, 160bdcd8170SKalle Valo * especially in production, this code needs to be enhanced to sleep 161bdcd8170SKalle Valo * and yield. Also note that BMI_COMMUNICATION_TIMEOUT is currently 162bdcd8170SKalle Valo * a function of Host processor speed. 163bdcd8170SKalle Valo */ 164bdcd8170SKalle Valo if (len >= 4) { /* NB: Currently, always true */ 165bdcd8170SKalle Valo ret = ath6kl_bmi_get_rx_lkahd(ar, want_timeout); 166bdcd8170SKalle Valo if (ret) 167bdcd8170SKalle Valo return ret; 168bdcd8170SKalle Valo } 169bdcd8170SKalle Valo 170bdcd8170SKalle Valo addr = ar->mbox_info.htc_addr; 171bdcd8170SKalle Valo ret = hif_read_write_sync(ar, addr, buf, len, 172bdcd8170SKalle Valo HIF_RD_SYNC_BYTE_INC); 173bdcd8170SKalle Valo if (ret) { 174bdcd8170SKalle Valo ath6kl_err("Unable to read the bmi data from the device: %d\n", 175bdcd8170SKalle Valo ret); 176bdcd8170SKalle Valo return ret; 177bdcd8170SKalle Valo } 178bdcd8170SKalle Valo 179bdcd8170SKalle Valo return 0; 180bdcd8170SKalle Valo } 181bdcd8170SKalle Valo 182bdcd8170SKalle Valo int ath6kl_bmi_done(struct ath6kl *ar) 183bdcd8170SKalle Valo { 184bdcd8170SKalle Valo int ret; 185bdcd8170SKalle Valo u32 cid = BMI_DONE; 186bdcd8170SKalle Valo 187bdcd8170SKalle Valo if (ar->bmi.done_sent) { 188bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, "bmi done skipped\n"); 189bdcd8170SKalle Valo return 0; 190bdcd8170SKalle Valo } 191bdcd8170SKalle Valo 192bdcd8170SKalle Valo ar->bmi.done_sent = true; 193bdcd8170SKalle Valo 194bdcd8170SKalle Valo ret = ath6kl_bmi_send_buf(ar, (u8 *)&cid, sizeof(cid)); 195bdcd8170SKalle Valo if (ret) { 196bdcd8170SKalle Valo ath6kl_err("Unable to send bmi done: %d\n", ret); 197bdcd8170SKalle Valo return ret; 198bdcd8170SKalle Valo } 199bdcd8170SKalle Valo 200bdcd8170SKalle Valo ath6kl_bmi_cleanup(ar); 201bdcd8170SKalle Valo 202bdcd8170SKalle Valo return 0; 203bdcd8170SKalle Valo } 204bdcd8170SKalle Valo 205bdcd8170SKalle Valo int ath6kl_bmi_get_target_info(struct ath6kl *ar, 206bdcd8170SKalle Valo struct ath6kl_bmi_target_info *targ_info) 207bdcd8170SKalle Valo { 208bdcd8170SKalle Valo int ret; 209bdcd8170SKalle Valo u32 cid = BMI_GET_TARGET_INFO; 210bdcd8170SKalle Valo 211bdcd8170SKalle Valo if (ar->bmi.done_sent) { 212bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 213bdcd8170SKalle Valo return -EACCES; 214bdcd8170SKalle Valo } 215bdcd8170SKalle Valo 216bdcd8170SKalle Valo ret = ath6kl_bmi_send_buf(ar, (u8 *)&cid, sizeof(cid)); 217bdcd8170SKalle Valo if (ret) { 218bdcd8170SKalle Valo ath6kl_err("Unable to send get target info: %d\n", ret); 219bdcd8170SKalle Valo return ret; 220bdcd8170SKalle Valo } 221bdcd8170SKalle Valo 222bdcd8170SKalle Valo ret = ath6kl_bmi_recv_buf(ar, (u8 *)&targ_info->version, 223bdcd8170SKalle Valo sizeof(targ_info->version), true); 224bdcd8170SKalle Valo if (ret) { 225bdcd8170SKalle Valo ath6kl_err("Unable to recv target info: %d\n", ret); 226bdcd8170SKalle Valo return ret; 227bdcd8170SKalle Valo } 228bdcd8170SKalle Valo 229bdcd8170SKalle Valo if (le32_to_cpu(targ_info->version) == TARGET_VERSION_SENTINAL) { 230bdcd8170SKalle Valo /* Determine how many bytes are in the Target's targ_info */ 231bdcd8170SKalle Valo ret = ath6kl_bmi_recv_buf(ar, 232bdcd8170SKalle Valo (u8 *)&targ_info->byte_count, 233bdcd8170SKalle Valo sizeof(targ_info->byte_count), 234bdcd8170SKalle Valo true); 235bdcd8170SKalle Valo if (ret) { 236bdcd8170SKalle Valo ath6kl_err("unable to read target info byte count: %d\n", 237bdcd8170SKalle Valo ret); 238bdcd8170SKalle Valo return ret; 239bdcd8170SKalle Valo } 240bdcd8170SKalle Valo 241bdcd8170SKalle Valo /* 242bdcd8170SKalle Valo * The target's targ_info doesn't match the host's targ_info. 243bdcd8170SKalle Valo * We need to do some backwards compatibility to make this work. 244bdcd8170SKalle Valo */ 245bdcd8170SKalle Valo if (le32_to_cpu(targ_info->byte_count) != sizeof(*targ_info)) { 246bdcd8170SKalle Valo WARN_ON(1); 247bdcd8170SKalle Valo return -EINVAL; 248bdcd8170SKalle Valo } 249bdcd8170SKalle Valo 250bdcd8170SKalle Valo /* Read the remainder of the targ_info */ 251bdcd8170SKalle Valo ret = ath6kl_bmi_recv_buf(ar, 252bdcd8170SKalle Valo ((u8 *)targ_info) + 253bdcd8170SKalle Valo sizeof(targ_info->byte_count), 254bdcd8170SKalle Valo sizeof(*targ_info) - 255bdcd8170SKalle Valo sizeof(targ_info->byte_count), 256bdcd8170SKalle Valo true); 257bdcd8170SKalle Valo 258bdcd8170SKalle Valo if (ret) { 259bdcd8170SKalle Valo ath6kl_err("Unable to read target info (%d bytes): %d\n", 260bdcd8170SKalle Valo targ_info->byte_count, ret); 261bdcd8170SKalle Valo return ret; 262bdcd8170SKalle Valo } 263bdcd8170SKalle Valo } 264bdcd8170SKalle Valo 265bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, "target info (ver: 0x%x type: 0x%x)\n", 266bdcd8170SKalle Valo targ_info->version, targ_info->type); 267bdcd8170SKalle Valo 268bdcd8170SKalle Valo return 0; 269bdcd8170SKalle Valo } 270bdcd8170SKalle Valo 271bdcd8170SKalle Valo int ath6kl_bmi_read(struct ath6kl *ar, u32 addr, u8 *buf, u32 len) 272bdcd8170SKalle Valo { 273bdcd8170SKalle Valo u32 cid = BMI_READ_MEMORY; 274bdcd8170SKalle Valo int ret; 275bdcd8170SKalle Valo u32 offset; 276bdcd8170SKalle Valo u32 len_remain, rx_len; 277bdcd8170SKalle Valo u16 size; 278bdcd8170SKalle Valo 279bdcd8170SKalle Valo if (ar->bmi.done_sent) { 280bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 281bdcd8170SKalle Valo return -EACCES; 282bdcd8170SKalle Valo } 283bdcd8170SKalle Valo 284bdcd8170SKalle Valo size = BMI_DATASZ_MAX + sizeof(cid) + sizeof(addr) + sizeof(len); 285bdcd8170SKalle Valo if (size > MAX_BMI_CMDBUF_SZ) { 286bdcd8170SKalle Valo WARN_ON(1); 287bdcd8170SKalle Valo return -EINVAL; 288bdcd8170SKalle Valo } 289bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 290bdcd8170SKalle Valo 291bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, 292bdcd8170SKalle Valo "bmi read memory: device: addr: 0x%x, len: %d\n", 293bdcd8170SKalle Valo addr, len); 294bdcd8170SKalle Valo 295bdcd8170SKalle Valo len_remain = len; 296bdcd8170SKalle Valo 297bdcd8170SKalle Valo while (len_remain) { 298bdcd8170SKalle Valo rx_len = (len_remain < BMI_DATASZ_MAX) ? 299bdcd8170SKalle Valo len_remain : BMI_DATASZ_MAX; 300bdcd8170SKalle Valo offset = 0; 301bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 302bdcd8170SKalle Valo offset += sizeof(cid); 303bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 304bdcd8170SKalle Valo offset += sizeof(addr); 305bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &rx_len, sizeof(rx_len)); 306bdcd8170SKalle Valo offset += sizeof(len); 307bdcd8170SKalle Valo 308bdcd8170SKalle Valo ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset); 309bdcd8170SKalle Valo if (ret) { 310bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", 311bdcd8170SKalle Valo ret); 312bdcd8170SKalle Valo return ret; 313bdcd8170SKalle Valo } 314bdcd8170SKalle Valo ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, rx_len, true); 315bdcd8170SKalle Valo if (ret) { 316bdcd8170SKalle Valo ath6kl_err("Unable to read from the device: %d\n", 317bdcd8170SKalle Valo ret); 318bdcd8170SKalle Valo return ret; 319bdcd8170SKalle Valo } 320bdcd8170SKalle Valo memcpy(&buf[len - len_remain], ar->bmi.cmd_buf, rx_len); 321bdcd8170SKalle Valo len_remain -= rx_len; addr += rx_len; 322bdcd8170SKalle Valo } 323bdcd8170SKalle Valo 324bdcd8170SKalle Valo return 0; 325bdcd8170SKalle Valo } 326bdcd8170SKalle Valo 327bdcd8170SKalle Valo int ath6kl_bmi_write(struct ath6kl *ar, u32 addr, u8 *buf, u32 len) 328bdcd8170SKalle Valo { 329bdcd8170SKalle Valo u32 cid = BMI_WRITE_MEMORY; 330bdcd8170SKalle Valo int ret; 331bdcd8170SKalle Valo u32 offset; 332bdcd8170SKalle Valo u32 len_remain, tx_len; 333bdcd8170SKalle Valo const u32 header = sizeof(cid) + sizeof(addr) + sizeof(len); 334bdcd8170SKalle Valo u8 aligned_buf[BMI_DATASZ_MAX]; 335bdcd8170SKalle Valo u8 *src; 336bdcd8170SKalle Valo 337bdcd8170SKalle Valo if (ar->bmi.done_sent) { 338bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 339bdcd8170SKalle Valo return -EACCES; 340bdcd8170SKalle Valo } 341bdcd8170SKalle Valo 342bdcd8170SKalle Valo if ((BMI_DATASZ_MAX + header) > MAX_BMI_CMDBUF_SZ) { 343bdcd8170SKalle Valo WARN_ON(1); 344bdcd8170SKalle Valo return -EINVAL; 345bdcd8170SKalle Valo } 346bdcd8170SKalle Valo 347bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, BMI_DATASZ_MAX + header); 348bdcd8170SKalle Valo 349bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, 350bdcd8170SKalle Valo "bmi write memory: addr: 0x%x, len: %d\n", addr, len); 351bdcd8170SKalle Valo 352bdcd8170SKalle Valo len_remain = len; 353bdcd8170SKalle Valo while (len_remain) { 354bdcd8170SKalle Valo src = &buf[len - len_remain]; 355bdcd8170SKalle Valo 356bdcd8170SKalle Valo if (len_remain < (BMI_DATASZ_MAX - header)) { 357bdcd8170SKalle Valo if (len_remain & 3) { 358bdcd8170SKalle Valo /* align it with 4 bytes */ 359bdcd8170SKalle Valo len_remain = len_remain + 360bdcd8170SKalle Valo (4 - (len_remain & 3)); 361bdcd8170SKalle Valo memcpy(aligned_buf, src, len_remain); 362bdcd8170SKalle Valo src = aligned_buf; 363bdcd8170SKalle Valo } 364bdcd8170SKalle Valo tx_len = len_remain; 365bdcd8170SKalle Valo } else { 366bdcd8170SKalle Valo tx_len = (BMI_DATASZ_MAX - header); 367bdcd8170SKalle Valo } 368bdcd8170SKalle Valo 369bdcd8170SKalle Valo offset = 0; 370bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 371bdcd8170SKalle Valo offset += sizeof(cid); 372bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 373bdcd8170SKalle Valo offset += sizeof(addr); 374bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len)); 375bdcd8170SKalle Valo offset += sizeof(tx_len); 376bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), src, tx_len); 377bdcd8170SKalle Valo offset += tx_len; 378bdcd8170SKalle Valo 379bdcd8170SKalle Valo ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset); 380bdcd8170SKalle Valo if (ret) { 381bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", 382bdcd8170SKalle Valo ret); 383bdcd8170SKalle Valo return ret; 384bdcd8170SKalle Valo } 385bdcd8170SKalle Valo len_remain -= tx_len; addr += tx_len; 386bdcd8170SKalle Valo } 387bdcd8170SKalle Valo 388bdcd8170SKalle Valo return 0; 389bdcd8170SKalle Valo } 390bdcd8170SKalle Valo 391bdcd8170SKalle Valo int ath6kl_bmi_execute(struct ath6kl *ar, u32 addr, u32 *param) 392bdcd8170SKalle Valo { 393bdcd8170SKalle Valo u32 cid = BMI_EXECUTE; 394bdcd8170SKalle Valo int ret; 395bdcd8170SKalle Valo u32 offset; 396bdcd8170SKalle Valo u16 size; 397bdcd8170SKalle Valo 398bdcd8170SKalle Valo if (ar->bmi.done_sent) { 399bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 400bdcd8170SKalle Valo return -EACCES; 401bdcd8170SKalle Valo } 402bdcd8170SKalle Valo 403bdcd8170SKalle Valo size = sizeof(cid) + sizeof(addr) + sizeof(param); 404bdcd8170SKalle Valo if (size > MAX_BMI_CMDBUF_SZ) { 405bdcd8170SKalle Valo WARN_ON(1); 406bdcd8170SKalle Valo return -EINVAL; 407bdcd8170SKalle Valo } 408bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 409bdcd8170SKalle Valo 410bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, "bmi execute: addr: 0x%x, param: %d)\n", 411bdcd8170SKalle Valo addr, *param); 412bdcd8170SKalle Valo 413bdcd8170SKalle Valo offset = 0; 414bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 415bdcd8170SKalle Valo offset += sizeof(cid); 416bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 417bdcd8170SKalle Valo offset += sizeof(addr); 418bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), param, sizeof(*param)); 419bdcd8170SKalle Valo offset += sizeof(*param); 420bdcd8170SKalle Valo 421bdcd8170SKalle Valo ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset); 422bdcd8170SKalle Valo if (ret) { 423bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", ret); 424bdcd8170SKalle Valo return ret; 425bdcd8170SKalle Valo } 426bdcd8170SKalle Valo 427bdcd8170SKalle Valo ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, sizeof(*param), false); 428bdcd8170SKalle Valo if (ret) { 429bdcd8170SKalle Valo ath6kl_err("Unable to read from the device: %d\n", ret); 430bdcd8170SKalle Valo return ret; 431bdcd8170SKalle Valo } 432bdcd8170SKalle Valo 433bdcd8170SKalle Valo memcpy(param, ar->bmi.cmd_buf, sizeof(*param)); 434bdcd8170SKalle Valo 435bdcd8170SKalle Valo return 0; 436bdcd8170SKalle Valo } 437bdcd8170SKalle Valo 438bdcd8170SKalle Valo int ath6kl_bmi_set_app_start(struct ath6kl *ar, u32 addr) 439bdcd8170SKalle Valo { 440bdcd8170SKalle Valo u32 cid = BMI_SET_APP_START; 441bdcd8170SKalle Valo int ret; 442bdcd8170SKalle Valo u32 offset; 443bdcd8170SKalle Valo u16 size; 444bdcd8170SKalle Valo 445bdcd8170SKalle Valo if (ar->bmi.done_sent) { 446bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 447bdcd8170SKalle Valo return -EACCES; 448bdcd8170SKalle Valo } 449bdcd8170SKalle Valo 450bdcd8170SKalle Valo size = sizeof(cid) + sizeof(addr); 451bdcd8170SKalle Valo if (size > MAX_BMI_CMDBUF_SZ) { 452bdcd8170SKalle Valo WARN_ON(1); 453bdcd8170SKalle Valo return -EINVAL; 454bdcd8170SKalle Valo } 455bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 456bdcd8170SKalle Valo 457bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, "bmi set app start: addr: 0x%x\n", addr); 458bdcd8170SKalle Valo 459bdcd8170SKalle Valo offset = 0; 460bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 461bdcd8170SKalle Valo offset += sizeof(cid); 462bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 463bdcd8170SKalle Valo offset += sizeof(addr); 464bdcd8170SKalle Valo 465bdcd8170SKalle Valo ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset); 466bdcd8170SKalle Valo if (ret) { 467bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", ret); 468bdcd8170SKalle Valo return ret; 469bdcd8170SKalle Valo } 470bdcd8170SKalle Valo 471bdcd8170SKalle Valo return 0; 472bdcd8170SKalle Valo } 473bdcd8170SKalle Valo 474bdcd8170SKalle Valo int ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param) 475bdcd8170SKalle Valo { 476bdcd8170SKalle Valo u32 cid = BMI_READ_SOC_REGISTER; 477bdcd8170SKalle Valo int ret; 478bdcd8170SKalle Valo u32 offset; 479bdcd8170SKalle Valo u16 size; 480bdcd8170SKalle Valo 481bdcd8170SKalle Valo if (ar->bmi.done_sent) { 482bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 483bdcd8170SKalle Valo return -EACCES; 484bdcd8170SKalle Valo } 485bdcd8170SKalle Valo 486bdcd8170SKalle Valo size = sizeof(cid) + sizeof(addr); 487bdcd8170SKalle Valo if (size > MAX_BMI_CMDBUF_SZ) { 488bdcd8170SKalle Valo WARN_ON(1); 489bdcd8170SKalle Valo return -EINVAL; 490bdcd8170SKalle Valo } 491bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 492bdcd8170SKalle Valo 493bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, "bmi read SOC reg: addr: 0x%x\n", addr); 494bdcd8170SKalle Valo 495bdcd8170SKalle Valo offset = 0; 496bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 497bdcd8170SKalle Valo offset += sizeof(cid); 498bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 499bdcd8170SKalle Valo offset += sizeof(addr); 500bdcd8170SKalle Valo 501bdcd8170SKalle Valo ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset); 502bdcd8170SKalle Valo if (ret) { 503bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", ret); 504bdcd8170SKalle Valo return ret; 505bdcd8170SKalle Valo } 506bdcd8170SKalle Valo 507bdcd8170SKalle Valo ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, sizeof(*param), true); 508bdcd8170SKalle Valo if (ret) { 509bdcd8170SKalle Valo ath6kl_err("Unable to read from the device: %d\n", ret); 510bdcd8170SKalle Valo return ret; 511bdcd8170SKalle Valo } 512bdcd8170SKalle Valo memcpy(param, ar->bmi.cmd_buf, sizeof(*param)); 513bdcd8170SKalle Valo 514bdcd8170SKalle Valo return 0; 515bdcd8170SKalle Valo } 516bdcd8170SKalle Valo 517bdcd8170SKalle Valo int ath6kl_bmi_reg_write(struct ath6kl *ar, u32 addr, u32 param) 518bdcd8170SKalle Valo { 519bdcd8170SKalle Valo u32 cid = BMI_WRITE_SOC_REGISTER; 520bdcd8170SKalle Valo int ret; 521bdcd8170SKalle Valo u32 offset; 522bdcd8170SKalle Valo u16 size; 523bdcd8170SKalle Valo 524bdcd8170SKalle Valo if (ar->bmi.done_sent) { 525bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 526bdcd8170SKalle Valo return -EACCES; 527bdcd8170SKalle Valo } 528bdcd8170SKalle Valo 529bdcd8170SKalle Valo size = sizeof(cid) + sizeof(addr) + sizeof(param); 530bdcd8170SKalle Valo if (size > MAX_BMI_CMDBUF_SZ) { 531bdcd8170SKalle Valo WARN_ON(1); 532bdcd8170SKalle Valo return -EINVAL; 533bdcd8170SKalle Valo } 534bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 535bdcd8170SKalle Valo 536bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, 537bdcd8170SKalle Valo "bmi write SOC reg: addr: 0x%x, param: %d\n", 538bdcd8170SKalle Valo addr, param); 539bdcd8170SKalle Valo 540bdcd8170SKalle Valo offset = 0; 541bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 542bdcd8170SKalle Valo offset += sizeof(cid); 543bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 544bdcd8170SKalle Valo offset += sizeof(addr); 545bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), ¶m, sizeof(param)); 546bdcd8170SKalle Valo offset += sizeof(param); 547bdcd8170SKalle Valo 548bdcd8170SKalle Valo ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset); 549bdcd8170SKalle Valo if (ret) { 550bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", ret); 551bdcd8170SKalle Valo return ret; 552bdcd8170SKalle Valo } 553bdcd8170SKalle Valo 554bdcd8170SKalle Valo return 0; 555bdcd8170SKalle Valo } 556bdcd8170SKalle Valo 557bdcd8170SKalle Valo int ath6kl_bmi_lz_data(struct ath6kl *ar, u8 *buf, u32 len) 558bdcd8170SKalle Valo { 559bdcd8170SKalle Valo u32 cid = BMI_LZ_DATA; 560bdcd8170SKalle Valo int ret; 561bdcd8170SKalle Valo u32 offset; 562bdcd8170SKalle Valo u32 len_remain, tx_len; 563bdcd8170SKalle Valo const u32 header = sizeof(cid) + sizeof(len); 564bdcd8170SKalle Valo u16 size; 565bdcd8170SKalle Valo 566bdcd8170SKalle Valo if (ar->bmi.done_sent) { 567bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 568bdcd8170SKalle Valo return -EACCES; 569bdcd8170SKalle Valo } 570bdcd8170SKalle Valo 571bdcd8170SKalle Valo size = BMI_DATASZ_MAX + header; 572bdcd8170SKalle Valo if (size > MAX_BMI_CMDBUF_SZ) { 573bdcd8170SKalle Valo WARN_ON(1); 574bdcd8170SKalle Valo return -EINVAL; 575bdcd8170SKalle Valo } 576bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 577bdcd8170SKalle Valo 578bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, "bmi send LZ data: len: %d)\n", 579bdcd8170SKalle Valo len); 580bdcd8170SKalle Valo 581bdcd8170SKalle Valo len_remain = len; 582bdcd8170SKalle Valo while (len_remain) { 583bdcd8170SKalle Valo tx_len = (len_remain < (BMI_DATASZ_MAX - header)) ? 584bdcd8170SKalle Valo len_remain : (BMI_DATASZ_MAX - header); 585bdcd8170SKalle Valo 586bdcd8170SKalle Valo offset = 0; 587bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 588bdcd8170SKalle Valo offset += sizeof(cid); 589bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len)); 590bdcd8170SKalle Valo offset += sizeof(tx_len); 591bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &buf[len - len_remain], 592bdcd8170SKalle Valo tx_len); 593bdcd8170SKalle Valo offset += tx_len; 594bdcd8170SKalle Valo 595bdcd8170SKalle Valo ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset); 596bdcd8170SKalle Valo if (ret) { 597bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", 598bdcd8170SKalle Valo ret); 599bdcd8170SKalle Valo return ret; 600bdcd8170SKalle Valo } 601bdcd8170SKalle Valo 602bdcd8170SKalle Valo len_remain -= tx_len; 603bdcd8170SKalle Valo } 604bdcd8170SKalle Valo 605bdcd8170SKalle Valo return 0; 606bdcd8170SKalle Valo } 607bdcd8170SKalle Valo 608bdcd8170SKalle Valo int ath6kl_bmi_lz_stream_start(struct ath6kl *ar, u32 addr) 609bdcd8170SKalle Valo { 610bdcd8170SKalle Valo u32 cid = BMI_LZ_STREAM_START; 611bdcd8170SKalle Valo int ret; 612bdcd8170SKalle Valo u32 offset; 613bdcd8170SKalle Valo u16 size; 614bdcd8170SKalle Valo 615bdcd8170SKalle Valo if (ar->bmi.done_sent) { 616bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 617bdcd8170SKalle Valo return -EACCES; 618bdcd8170SKalle Valo } 619bdcd8170SKalle Valo 620bdcd8170SKalle Valo size = sizeof(cid) + sizeof(addr); 621bdcd8170SKalle Valo if (size > MAX_BMI_CMDBUF_SZ) { 622bdcd8170SKalle Valo WARN_ON(1); 623bdcd8170SKalle Valo return -EINVAL; 624bdcd8170SKalle Valo } 625bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 626bdcd8170SKalle Valo 627bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, 628bdcd8170SKalle Valo "bmi LZ stream start: addr: 0x%x)\n", 629bdcd8170SKalle Valo addr); 630bdcd8170SKalle Valo 631bdcd8170SKalle Valo offset = 0; 632bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 633bdcd8170SKalle Valo offset += sizeof(cid); 634bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 635bdcd8170SKalle Valo offset += sizeof(addr); 636bdcd8170SKalle Valo 637bdcd8170SKalle Valo ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset); 638bdcd8170SKalle Valo if (ret) { 639bdcd8170SKalle Valo ath6kl_err("Unable to start LZ stream to the device: %d\n", 640bdcd8170SKalle Valo ret); 641bdcd8170SKalle Valo return ret; 642bdcd8170SKalle Valo } 643bdcd8170SKalle Valo 644bdcd8170SKalle Valo return 0; 645bdcd8170SKalle Valo } 646bdcd8170SKalle Valo 647bdcd8170SKalle Valo int ath6kl_bmi_fast_download(struct ath6kl *ar, u32 addr, u8 *buf, u32 len) 648bdcd8170SKalle Valo { 649bdcd8170SKalle Valo int ret; 650bdcd8170SKalle Valo u32 last_word = 0; 651bdcd8170SKalle Valo u32 last_word_offset = len & ~0x3; 652bdcd8170SKalle Valo u32 unaligned_bytes = len & 0x3; 653bdcd8170SKalle Valo 654bdcd8170SKalle Valo ret = ath6kl_bmi_lz_stream_start(ar, addr); 655bdcd8170SKalle Valo if (ret) 656bdcd8170SKalle Valo return ret; 657bdcd8170SKalle Valo 658bdcd8170SKalle Valo if (unaligned_bytes) { 659bdcd8170SKalle Valo /* copy the last word into a zero padded buffer */ 660bdcd8170SKalle Valo memcpy(&last_word, &buf[last_word_offset], unaligned_bytes); 661bdcd8170SKalle Valo } 662bdcd8170SKalle Valo 663bdcd8170SKalle Valo ret = ath6kl_bmi_lz_data(ar, buf, last_word_offset); 664bdcd8170SKalle Valo if (ret) 665bdcd8170SKalle Valo return ret; 666bdcd8170SKalle Valo 667bdcd8170SKalle Valo if (unaligned_bytes) 668bdcd8170SKalle Valo ret = ath6kl_bmi_lz_data(ar, (u8 *)&last_word, 4); 669bdcd8170SKalle Valo 670bdcd8170SKalle Valo if (!ret) { 671bdcd8170SKalle Valo /* Close compressed stream and open a new (fake) one. 672bdcd8170SKalle Valo * This serves mainly to flush Target caches. */ 673bdcd8170SKalle Valo ret = ath6kl_bmi_lz_stream_start(ar, 0x00); 674bdcd8170SKalle Valo } 675bdcd8170SKalle Valo return ret; 676bdcd8170SKalle Valo } 677bdcd8170SKalle Valo 678bdcd8170SKalle Valo int ath6kl_bmi_init(struct ath6kl *ar) 679bdcd8170SKalle Valo { 680bdcd8170SKalle Valo ar->bmi.cmd_buf = kzalloc(MAX_BMI_CMDBUF_SZ, GFP_ATOMIC); 681bdcd8170SKalle Valo 682bdcd8170SKalle Valo if (!ar->bmi.cmd_buf) 683bdcd8170SKalle Valo return -ENOMEM; 684bdcd8170SKalle Valo 685bdcd8170SKalle Valo return 0; 686bdcd8170SKalle Valo } 687bdcd8170SKalle Valo 688bdcd8170SKalle Valo void ath6kl_bmi_cleanup(struct ath6kl *ar) 689bdcd8170SKalle Valo { 690bdcd8170SKalle Valo kfree(ar->bmi.cmd_buf); 691bdcd8170SKalle Valo ar->bmi.cmd_buf = NULL; 692bdcd8170SKalle Valo } 693