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