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 return 0; 200bdcd8170SKalle Valo } 201bdcd8170SKalle Valo 202bdcd8170SKalle Valo int ath6kl_bmi_get_target_info(struct ath6kl *ar, 203bdcd8170SKalle Valo struct ath6kl_bmi_target_info *targ_info) 204bdcd8170SKalle Valo { 205bdcd8170SKalle Valo int ret; 206bdcd8170SKalle Valo u32 cid = BMI_GET_TARGET_INFO; 207bdcd8170SKalle Valo 208bdcd8170SKalle Valo if (ar->bmi.done_sent) { 209bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 210bdcd8170SKalle Valo return -EACCES; 211bdcd8170SKalle Valo } 212bdcd8170SKalle Valo 213bdcd8170SKalle Valo ret = ath6kl_bmi_send_buf(ar, (u8 *)&cid, sizeof(cid)); 214bdcd8170SKalle Valo if (ret) { 215bdcd8170SKalle Valo ath6kl_err("Unable to send get target info: %d\n", ret); 216bdcd8170SKalle Valo return ret; 217bdcd8170SKalle Valo } 218bdcd8170SKalle Valo 219bdcd8170SKalle Valo ret = ath6kl_bmi_recv_buf(ar, (u8 *)&targ_info->version, 220cfc301edSKalle Valo sizeof(targ_info->version)); 221bdcd8170SKalle Valo if (ret) { 222bdcd8170SKalle Valo ath6kl_err("Unable to recv target info: %d\n", ret); 223bdcd8170SKalle Valo return ret; 224bdcd8170SKalle Valo } 225bdcd8170SKalle Valo 226bdcd8170SKalle Valo if (le32_to_cpu(targ_info->version) == TARGET_VERSION_SENTINAL) { 227bdcd8170SKalle Valo /* Determine how many bytes are in the Target's targ_info */ 228bdcd8170SKalle Valo ret = ath6kl_bmi_recv_buf(ar, 229bdcd8170SKalle Valo (u8 *)&targ_info->byte_count, 230cfc301edSKalle Valo sizeof(targ_info->byte_count)); 231bdcd8170SKalle Valo if (ret) { 232bdcd8170SKalle Valo ath6kl_err("unable to read target info byte count: %d\n", 233bdcd8170SKalle Valo ret); 234bdcd8170SKalle Valo return ret; 235bdcd8170SKalle Valo } 236bdcd8170SKalle Valo 237bdcd8170SKalle Valo /* 238bdcd8170SKalle Valo * The target's targ_info doesn't match the host's targ_info. 239bdcd8170SKalle Valo * We need to do some backwards compatibility to make this work. 240bdcd8170SKalle Valo */ 241bdcd8170SKalle Valo if (le32_to_cpu(targ_info->byte_count) != sizeof(*targ_info)) { 242bdcd8170SKalle Valo WARN_ON(1); 243bdcd8170SKalle Valo return -EINVAL; 244bdcd8170SKalle Valo } 245bdcd8170SKalle Valo 246bdcd8170SKalle Valo /* Read the remainder of the targ_info */ 247bdcd8170SKalle Valo ret = ath6kl_bmi_recv_buf(ar, 248bdcd8170SKalle Valo ((u8 *)targ_info) + 249bdcd8170SKalle Valo sizeof(targ_info->byte_count), 250bdcd8170SKalle Valo sizeof(*targ_info) - 251cfc301edSKalle Valo sizeof(targ_info->byte_count)); 252bdcd8170SKalle Valo 253bdcd8170SKalle Valo if (ret) { 254bdcd8170SKalle Valo ath6kl_err("Unable to read target info (%d bytes): %d\n", 255bdcd8170SKalle Valo targ_info->byte_count, ret); 256bdcd8170SKalle Valo return ret; 257bdcd8170SKalle Valo } 258bdcd8170SKalle Valo } 259bdcd8170SKalle Valo 260bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, "target info (ver: 0x%x type: 0x%x)\n", 261bdcd8170SKalle Valo targ_info->version, targ_info->type); 262bdcd8170SKalle Valo 263bdcd8170SKalle Valo return 0; 264bdcd8170SKalle Valo } 265bdcd8170SKalle Valo 266bdcd8170SKalle Valo int ath6kl_bmi_read(struct ath6kl *ar, u32 addr, u8 *buf, u32 len) 267bdcd8170SKalle Valo { 268bdcd8170SKalle Valo u32 cid = BMI_READ_MEMORY; 269bdcd8170SKalle Valo int ret; 270bdcd8170SKalle Valo u32 offset; 271bdcd8170SKalle Valo u32 len_remain, rx_len; 272bdcd8170SKalle Valo u16 size; 273bdcd8170SKalle Valo 274bdcd8170SKalle Valo if (ar->bmi.done_sent) { 275bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 276bdcd8170SKalle Valo return -EACCES; 277bdcd8170SKalle Valo } 278bdcd8170SKalle Valo 279bdcd8170SKalle Valo size = BMI_DATASZ_MAX + sizeof(cid) + sizeof(addr) + sizeof(len); 280bdcd8170SKalle Valo if (size > MAX_BMI_CMDBUF_SZ) { 281bdcd8170SKalle Valo WARN_ON(1); 282bdcd8170SKalle Valo return -EINVAL; 283bdcd8170SKalle Valo } 284bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 285bdcd8170SKalle Valo 286bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, 287bdcd8170SKalle Valo "bmi read memory: device: addr: 0x%x, len: %d\n", 288bdcd8170SKalle Valo addr, len); 289bdcd8170SKalle Valo 290bdcd8170SKalle Valo len_remain = len; 291bdcd8170SKalle Valo 292bdcd8170SKalle Valo while (len_remain) { 293bdcd8170SKalle Valo rx_len = (len_remain < BMI_DATASZ_MAX) ? 294bdcd8170SKalle Valo len_remain : BMI_DATASZ_MAX; 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 memcpy(&(ar->bmi.cmd_buf[offset]), &rx_len, sizeof(rx_len)); 301bdcd8170SKalle Valo offset += sizeof(len); 302bdcd8170SKalle Valo 303bdcd8170SKalle Valo ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset); 304bdcd8170SKalle Valo if (ret) { 305bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", 306bdcd8170SKalle Valo ret); 307bdcd8170SKalle Valo return ret; 308bdcd8170SKalle Valo } 309cfc301edSKalle Valo ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, rx_len); 310bdcd8170SKalle Valo if (ret) { 311bdcd8170SKalle Valo ath6kl_err("Unable to read from the device: %d\n", 312bdcd8170SKalle Valo ret); 313bdcd8170SKalle Valo return ret; 314bdcd8170SKalle Valo } 315bdcd8170SKalle Valo memcpy(&buf[len - len_remain], ar->bmi.cmd_buf, rx_len); 316bdcd8170SKalle Valo len_remain -= rx_len; addr += rx_len; 317bdcd8170SKalle Valo } 318bdcd8170SKalle Valo 319bdcd8170SKalle Valo return 0; 320bdcd8170SKalle Valo } 321bdcd8170SKalle Valo 322bdcd8170SKalle Valo int ath6kl_bmi_write(struct ath6kl *ar, u32 addr, u8 *buf, u32 len) 323bdcd8170SKalle Valo { 324bdcd8170SKalle Valo u32 cid = BMI_WRITE_MEMORY; 325bdcd8170SKalle Valo int ret; 326bdcd8170SKalle Valo u32 offset; 327bdcd8170SKalle Valo u32 len_remain, tx_len; 328bdcd8170SKalle Valo const u32 header = sizeof(cid) + sizeof(addr) + sizeof(len); 329bdcd8170SKalle Valo u8 aligned_buf[BMI_DATASZ_MAX]; 330bdcd8170SKalle Valo u8 *src; 331bdcd8170SKalle Valo 332bdcd8170SKalle Valo if (ar->bmi.done_sent) { 333bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 334bdcd8170SKalle Valo return -EACCES; 335bdcd8170SKalle Valo } 336bdcd8170SKalle Valo 337bdcd8170SKalle Valo if ((BMI_DATASZ_MAX + header) > MAX_BMI_CMDBUF_SZ) { 338bdcd8170SKalle Valo WARN_ON(1); 339bdcd8170SKalle Valo return -EINVAL; 340bdcd8170SKalle Valo } 341bdcd8170SKalle Valo 342bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, BMI_DATASZ_MAX + header); 343bdcd8170SKalle Valo 344bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, 345bdcd8170SKalle Valo "bmi write memory: addr: 0x%x, len: %d\n", addr, len); 346bdcd8170SKalle Valo 347bdcd8170SKalle Valo len_remain = len; 348bdcd8170SKalle Valo while (len_remain) { 349bdcd8170SKalle Valo src = &buf[len - len_remain]; 350bdcd8170SKalle Valo 351bdcd8170SKalle Valo if (len_remain < (BMI_DATASZ_MAX - header)) { 352bdcd8170SKalle Valo if (len_remain & 3) { 353bdcd8170SKalle Valo /* align it with 4 bytes */ 354bdcd8170SKalle Valo len_remain = len_remain + 355bdcd8170SKalle Valo (4 - (len_remain & 3)); 356bdcd8170SKalle Valo memcpy(aligned_buf, src, len_remain); 357bdcd8170SKalle Valo src = aligned_buf; 358bdcd8170SKalle Valo } 359bdcd8170SKalle Valo tx_len = len_remain; 360bdcd8170SKalle Valo } else { 361bdcd8170SKalle Valo tx_len = (BMI_DATASZ_MAX - header); 362bdcd8170SKalle Valo } 363bdcd8170SKalle Valo 364bdcd8170SKalle Valo offset = 0; 365bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 366bdcd8170SKalle Valo offset += sizeof(cid); 367bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 368bdcd8170SKalle Valo offset += sizeof(addr); 369bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len)); 370bdcd8170SKalle Valo offset += sizeof(tx_len); 371bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), src, tx_len); 372bdcd8170SKalle Valo offset += tx_len; 373bdcd8170SKalle Valo 374bdcd8170SKalle Valo ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset); 375bdcd8170SKalle Valo if (ret) { 376bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", 377bdcd8170SKalle Valo ret); 378bdcd8170SKalle Valo return ret; 379bdcd8170SKalle Valo } 380bdcd8170SKalle Valo len_remain -= tx_len; addr += tx_len; 381bdcd8170SKalle Valo } 382bdcd8170SKalle Valo 383bdcd8170SKalle Valo return 0; 384bdcd8170SKalle Valo } 385bdcd8170SKalle Valo 386bdcd8170SKalle Valo int ath6kl_bmi_execute(struct ath6kl *ar, u32 addr, u32 *param) 387bdcd8170SKalle Valo { 388bdcd8170SKalle Valo u32 cid = BMI_EXECUTE; 389bdcd8170SKalle Valo int ret; 390bdcd8170SKalle Valo u32 offset; 391bdcd8170SKalle Valo u16 size; 392bdcd8170SKalle Valo 393bdcd8170SKalle Valo if (ar->bmi.done_sent) { 394bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 395bdcd8170SKalle Valo return -EACCES; 396bdcd8170SKalle Valo } 397bdcd8170SKalle Valo 398bdcd8170SKalle Valo size = sizeof(cid) + sizeof(addr) + sizeof(param); 399bdcd8170SKalle Valo if (size > MAX_BMI_CMDBUF_SZ) { 400bdcd8170SKalle Valo WARN_ON(1); 401bdcd8170SKalle Valo return -EINVAL; 402bdcd8170SKalle Valo } 403bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 404bdcd8170SKalle Valo 405bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, "bmi execute: addr: 0x%x, param: %d)\n", 406bdcd8170SKalle Valo addr, *param); 407bdcd8170SKalle Valo 408bdcd8170SKalle Valo offset = 0; 409bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 410bdcd8170SKalle Valo offset += sizeof(cid); 411bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 412bdcd8170SKalle Valo offset += sizeof(addr); 413bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), param, sizeof(*param)); 414bdcd8170SKalle Valo offset += sizeof(*param); 415bdcd8170SKalle Valo 416bdcd8170SKalle Valo ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset); 417bdcd8170SKalle Valo if (ret) { 418bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", ret); 419bdcd8170SKalle Valo return ret; 420bdcd8170SKalle Valo } 421bdcd8170SKalle Valo 422cfc301edSKalle Valo ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, sizeof(*param)); 423bdcd8170SKalle Valo if (ret) { 424bdcd8170SKalle Valo ath6kl_err("Unable to read from the device: %d\n", ret); 425bdcd8170SKalle Valo return ret; 426bdcd8170SKalle Valo } 427bdcd8170SKalle Valo 428bdcd8170SKalle Valo memcpy(param, ar->bmi.cmd_buf, sizeof(*param)); 429bdcd8170SKalle Valo 430bdcd8170SKalle Valo return 0; 431bdcd8170SKalle Valo } 432bdcd8170SKalle Valo 433bdcd8170SKalle Valo int ath6kl_bmi_set_app_start(struct ath6kl *ar, u32 addr) 434bdcd8170SKalle Valo { 435bdcd8170SKalle Valo u32 cid = BMI_SET_APP_START; 436bdcd8170SKalle Valo int ret; 437bdcd8170SKalle Valo u32 offset; 438bdcd8170SKalle Valo u16 size; 439bdcd8170SKalle Valo 440bdcd8170SKalle Valo if (ar->bmi.done_sent) { 441bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 442bdcd8170SKalle Valo return -EACCES; 443bdcd8170SKalle Valo } 444bdcd8170SKalle Valo 445bdcd8170SKalle Valo size = sizeof(cid) + sizeof(addr); 446bdcd8170SKalle Valo if (size > MAX_BMI_CMDBUF_SZ) { 447bdcd8170SKalle Valo WARN_ON(1); 448bdcd8170SKalle Valo return -EINVAL; 449bdcd8170SKalle Valo } 450bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 451bdcd8170SKalle Valo 452bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, "bmi set app start: addr: 0x%x\n", addr); 453bdcd8170SKalle Valo 454bdcd8170SKalle Valo offset = 0; 455bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 456bdcd8170SKalle Valo offset += sizeof(cid); 457bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 458bdcd8170SKalle Valo offset += sizeof(addr); 459bdcd8170SKalle Valo 460bdcd8170SKalle Valo ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset); 461bdcd8170SKalle Valo if (ret) { 462bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", ret); 463bdcd8170SKalle Valo return ret; 464bdcd8170SKalle Valo } 465bdcd8170SKalle Valo 466bdcd8170SKalle Valo return 0; 467bdcd8170SKalle Valo } 468bdcd8170SKalle Valo 469bdcd8170SKalle Valo int ath6kl_bmi_reg_read(struct ath6kl *ar, u32 addr, u32 *param) 470bdcd8170SKalle Valo { 471bdcd8170SKalle Valo u32 cid = BMI_READ_SOC_REGISTER; 472bdcd8170SKalle Valo int ret; 473bdcd8170SKalle Valo u32 offset; 474bdcd8170SKalle Valo u16 size; 475bdcd8170SKalle Valo 476bdcd8170SKalle Valo if (ar->bmi.done_sent) { 477bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 478bdcd8170SKalle Valo return -EACCES; 479bdcd8170SKalle Valo } 480bdcd8170SKalle Valo 481bdcd8170SKalle Valo size = sizeof(cid) + sizeof(addr); 482bdcd8170SKalle Valo if (size > MAX_BMI_CMDBUF_SZ) { 483bdcd8170SKalle Valo WARN_ON(1); 484bdcd8170SKalle Valo return -EINVAL; 485bdcd8170SKalle Valo } 486bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 487bdcd8170SKalle Valo 488bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, "bmi read SOC reg: addr: 0x%x\n", addr); 489bdcd8170SKalle Valo 490bdcd8170SKalle Valo offset = 0; 491bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 492bdcd8170SKalle Valo offset += sizeof(cid); 493bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 494bdcd8170SKalle Valo offset += sizeof(addr); 495bdcd8170SKalle Valo 496bdcd8170SKalle Valo ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset); 497bdcd8170SKalle Valo if (ret) { 498bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", ret); 499bdcd8170SKalle Valo return ret; 500bdcd8170SKalle Valo } 501bdcd8170SKalle Valo 502cfc301edSKalle Valo ret = ath6kl_bmi_recv_buf(ar, ar->bmi.cmd_buf, sizeof(*param)); 503bdcd8170SKalle Valo if (ret) { 504bdcd8170SKalle Valo ath6kl_err("Unable to read from the device: %d\n", ret); 505bdcd8170SKalle Valo return ret; 506bdcd8170SKalle Valo } 507bdcd8170SKalle Valo memcpy(param, ar->bmi.cmd_buf, sizeof(*param)); 508bdcd8170SKalle Valo 509bdcd8170SKalle Valo return 0; 510bdcd8170SKalle Valo } 511bdcd8170SKalle Valo 512bdcd8170SKalle Valo int ath6kl_bmi_reg_write(struct ath6kl *ar, u32 addr, u32 param) 513bdcd8170SKalle Valo { 514bdcd8170SKalle Valo u32 cid = BMI_WRITE_SOC_REGISTER; 515bdcd8170SKalle Valo int ret; 516bdcd8170SKalle Valo u32 offset; 517bdcd8170SKalle Valo u16 size; 518bdcd8170SKalle Valo 519bdcd8170SKalle Valo if (ar->bmi.done_sent) { 520bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 521bdcd8170SKalle Valo return -EACCES; 522bdcd8170SKalle Valo } 523bdcd8170SKalle Valo 524bdcd8170SKalle Valo size = sizeof(cid) + sizeof(addr) + sizeof(param); 525bdcd8170SKalle Valo if (size > MAX_BMI_CMDBUF_SZ) { 526bdcd8170SKalle Valo WARN_ON(1); 527bdcd8170SKalle Valo return -EINVAL; 528bdcd8170SKalle Valo } 529bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 530bdcd8170SKalle Valo 531bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, 532bdcd8170SKalle Valo "bmi write SOC reg: addr: 0x%x, param: %d\n", 533bdcd8170SKalle Valo addr, param); 534bdcd8170SKalle Valo 535bdcd8170SKalle Valo offset = 0; 536bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 537bdcd8170SKalle Valo offset += sizeof(cid); 538bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 539bdcd8170SKalle Valo offset += sizeof(addr); 540bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), ¶m, sizeof(param)); 541bdcd8170SKalle Valo offset += sizeof(param); 542bdcd8170SKalle Valo 543bdcd8170SKalle Valo ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset); 544bdcd8170SKalle Valo if (ret) { 545bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", ret); 546bdcd8170SKalle Valo return ret; 547bdcd8170SKalle Valo } 548bdcd8170SKalle Valo 549bdcd8170SKalle Valo return 0; 550bdcd8170SKalle Valo } 551bdcd8170SKalle Valo 552bdcd8170SKalle Valo int ath6kl_bmi_lz_data(struct ath6kl *ar, u8 *buf, u32 len) 553bdcd8170SKalle Valo { 554bdcd8170SKalle Valo u32 cid = BMI_LZ_DATA; 555bdcd8170SKalle Valo int ret; 556bdcd8170SKalle Valo u32 offset; 557bdcd8170SKalle Valo u32 len_remain, tx_len; 558bdcd8170SKalle Valo const u32 header = sizeof(cid) + sizeof(len); 559bdcd8170SKalle Valo u16 size; 560bdcd8170SKalle Valo 561bdcd8170SKalle Valo if (ar->bmi.done_sent) { 562bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 563bdcd8170SKalle Valo return -EACCES; 564bdcd8170SKalle Valo } 565bdcd8170SKalle Valo 566bdcd8170SKalle Valo size = BMI_DATASZ_MAX + header; 567bdcd8170SKalle Valo if (size > MAX_BMI_CMDBUF_SZ) { 568bdcd8170SKalle Valo WARN_ON(1); 569bdcd8170SKalle Valo return -EINVAL; 570bdcd8170SKalle Valo } 571bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 572bdcd8170SKalle Valo 573bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, "bmi send LZ data: len: %d)\n", 574bdcd8170SKalle Valo len); 575bdcd8170SKalle Valo 576bdcd8170SKalle Valo len_remain = len; 577bdcd8170SKalle Valo while (len_remain) { 578bdcd8170SKalle Valo tx_len = (len_remain < (BMI_DATASZ_MAX - header)) ? 579bdcd8170SKalle Valo len_remain : (BMI_DATASZ_MAX - header); 580bdcd8170SKalle Valo 581bdcd8170SKalle Valo offset = 0; 582bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 583bdcd8170SKalle Valo offset += sizeof(cid); 584bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &tx_len, sizeof(tx_len)); 585bdcd8170SKalle Valo offset += sizeof(tx_len); 586bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &buf[len - len_remain], 587bdcd8170SKalle Valo tx_len); 588bdcd8170SKalle Valo offset += tx_len; 589bdcd8170SKalle Valo 590bdcd8170SKalle Valo ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset); 591bdcd8170SKalle Valo if (ret) { 592bdcd8170SKalle Valo ath6kl_err("Unable to write to the device: %d\n", 593bdcd8170SKalle Valo ret); 594bdcd8170SKalle Valo return ret; 595bdcd8170SKalle Valo } 596bdcd8170SKalle Valo 597bdcd8170SKalle Valo len_remain -= tx_len; 598bdcd8170SKalle Valo } 599bdcd8170SKalle Valo 600bdcd8170SKalle Valo return 0; 601bdcd8170SKalle Valo } 602bdcd8170SKalle Valo 603bdcd8170SKalle Valo int ath6kl_bmi_lz_stream_start(struct ath6kl *ar, u32 addr) 604bdcd8170SKalle Valo { 605bdcd8170SKalle Valo u32 cid = BMI_LZ_STREAM_START; 606bdcd8170SKalle Valo int ret; 607bdcd8170SKalle Valo u32 offset; 608bdcd8170SKalle Valo u16 size; 609bdcd8170SKalle Valo 610bdcd8170SKalle Valo if (ar->bmi.done_sent) { 611bdcd8170SKalle Valo ath6kl_err("bmi done sent already, cmd %d disallowed\n", cid); 612bdcd8170SKalle Valo return -EACCES; 613bdcd8170SKalle Valo } 614bdcd8170SKalle Valo 615bdcd8170SKalle Valo size = sizeof(cid) + sizeof(addr); 616bdcd8170SKalle Valo if (size > MAX_BMI_CMDBUF_SZ) { 617bdcd8170SKalle Valo WARN_ON(1); 618bdcd8170SKalle Valo return -EINVAL; 619bdcd8170SKalle Valo } 620bdcd8170SKalle Valo memset(ar->bmi.cmd_buf, 0, size); 621bdcd8170SKalle Valo 622bdcd8170SKalle Valo ath6kl_dbg(ATH6KL_DBG_BMI, 623bdcd8170SKalle Valo "bmi LZ stream start: addr: 0x%x)\n", 624bdcd8170SKalle Valo addr); 625bdcd8170SKalle Valo 626bdcd8170SKalle Valo offset = 0; 627bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &cid, sizeof(cid)); 628bdcd8170SKalle Valo offset += sizeof(cid); 629bdcd8170SKalle Valo memcpy(&(ar->bmi.cmd_buf[offset]), &addr, sizeof(addr)); 630bdcd8170SKalle Valo offset += sizeof(addr); 631bdcd8170SKalle Valo 632bdcd8170SKalle Valo ret = ath6kl_bmi_send_buf(ar, ar->bmi.cmd_buf, offset); 633bdcd8170SKalle Valo if (ret) { 634bdcd8170SKalle Valo ath6kl_err("Unable to start LZ stream to the device: %d\n", 635bdcd8170SKalle Valo ret); 636bdcd8170SKalle Valo return ret; 637bdcd8170SKalle Valo } 638bdcd8170SKalle Valo 639bdcd8170SKalle Valo return 0; 640bdcd8170SKalle Valo } 641bdcd8170SKalle Valo 642bdcd8170SKalle Valo int ath6kl_bmi_fast_download(struct ath6kl *ar, u32 addr, u8 *buf, u32 len) 643bdcd8170SKalle Valo { 644bdcd8170SKalle Valo int ret; 645bdcd8170SKalle Valo u32 last_word = 0; 646bdcd8170SKalle Valo u32 last_word_offset = len & ~0x3; 647bdcd8170SKalle Valo u32 unaligned_bytes = len & 0x3; 648bdcd8170SKalle Valo 649bdcd8170SKalle Valo ret = ath6kl_bmi_lz_stream_start(ar, addr); 650bdcd8170SKalle Valo if (ret) 651bdcd8170SKalle Valo return ret; 652bdcd8170SKalle Valo 653bdcd8170SKalle Valo if (unaligned_bytes) { 654bdcd8170SKalle Valo /* copy the last word into a zero padded buffer */ 655bdcd8170SKalle Valo memcpy(&last_word, &buf[last_word_offset], unaligned_bytes); 656bdcd8170SKalle Valo } 657bdcd8170SKalle Valo 658bdcd8170SKalle Valo ret = ath6kl_bmi_lz_data(ar, buf, last_word_offset); 659bdcd8170SKalle Valo if (ret) 660bdcd8170SKalle Valo return ret; 661bdcd8170SKalle Valo 662bdcd8170SKalle Valo if (unaligned_bytes) 663bdcd8170SKalle Valo ret = ath6kl_bmi_lz_data(ar, (u8 *)&last_word, 4); 664bdcd8170SKalle Valo 665bdcd8170SKalle Valo if (!ret) { 666bdcd8170SKalle Valo /* Close compressed stream and open a new (fake) one. 667bdcd8170SKalle Valo * This serves mainly to flush Target caches. */ 668bdcd8170SKalle Valo ret = ath6kl_bmi_lz_stream_start(ar, 0x00); 669bdcd8170SKalle Valo } 670bdcd8170SKalle Valo return ret; 671bdcd8170SKalle Valo } 672bdcd8170SKalle Valo 6735fe4dffbSKalle Valo void ath6kl_bmi_reset(struct ath6kl *ar) 6745fe4dffbSKalle Valo { 6755fe4dffbSKalle Valo ar->bmi.done_sent = false; 6765fe4dffbSKalle Valo } 6775fe4dffbSKalle 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