1 /* 2 * Copyright (c) 2005-2011 Atheros Communications Inc. 3 * Copyright (c) 2011-2013 Qualcomm Atheros, Inc. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include "bmi.h" 19 #include "hif.h" 20 #include "debug.h" 21 #include "htc.h" 22 23 void ath10k_bmi_start(struct ath10k *ar) 24 { 25 ath10k_dbg(ATH10K_DBG_BMI, "bmi start\n"); 26 27 ar->bmi.done_sent = false; 28 } 29 30 int ath10k_bmi_done(struct ath10k *ar) 31 { 32 struct bmi_cmd cmd; 33 u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.done); 34 int ret; 35 36 ath10k_dbg(ATH10K_DBG_BMI, "bmi done\n"); 37 38 if (ar->bmi.done_sent) { 39 ath10k_dbg(ATH10K_DBG_BMI, "bmi skipped\n"); 40 return 0; 41 } 42 43 ar->bmi.done_sent = true; 44 cmd.id = __cpu_to_le32(BMI_DONE); 45 46 ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL); 47 if (ret) { 48 ath10k_warn("unable to write to the device: %d\n", ret); 49 return ret; 50 } 51 52 return 0; 53 } 54 55 int ath10k_bmi_get_target_info(struct ath10k *ar, 56 struct bmi_target_info *target_info) 57 { 58 struct bmi_cmd cmd; 59 union bmi_resp resp; 60 u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.get_target_info); 61 u32 resplen = sizeof(resp.get_target_info); 62 int ret; 63 64 ath10k_dbg(ATH10K_DBG_BMI, "bmi get target info\n"); 65 66 if (ar->bmi.done_sent) { 67 ath10k_warn("BMI Get Target Info Command disallowed\n"); 68 return -EBUSY; 69 } 70 71 cmd.id = __cpu_to_le32(BMI_GET_TARGET_INFO); 72 73 ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen); 74 if (ret) { 75 ath10k_warn("unable to get target info from device\n"); 76 return ret; 77 } 78 79 if (resplen < sizeof(resp.get_target_info)) { 80 ath10k_warn("invalid get_target_info response length (%d)\n", 81 resplen); 82 return -EIO; 83 } 84 85 target_info->version = __le32_to_cpu(resp.get_target_info.version); 86 target_info->type = __le32_to_cpu(resp.get_target_info.type); 87 88 return 0; 89 } 90 91 int ath10k_bmi_read_memory(struct ath10k *ar, 92 u32 address, void *buffer, u32 length) 93 { 94 struct bmi_cmd cmd; 95 union bmi_resp resp; 96 u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.read_mem); 97 u32 rxlen; 98 int ret; 99 100 ath10k_dbg(ATH10K_DBG_BMI, "bmi read address 0x%x length %d\n", 101 address, length); 102 103 if (ar->bmi.done_sent) { 104 ath10k_warn("command disallowed\n"); 105 return -EBUSY; 106 } 107 108 while (length) { 109 rxlen = min_t(u32, length, BMI_MAX_DATA_SIZE); 110 111 cmd.id = __cpu_to_le32(BMI_READ_MEMORY); 112 cmd.read_mem.addr = __cpu_to_le32(address); 113 cmd.read_mem.len = __cpu_to_le32(rxlen); 114 115 ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, 116 &resp, &rxlen); 117 if (ret) { 118 ath10k_warn("unable to read from the device (%d)\n", 119 ret); 120 return ret; 121 } 122 123 memcpy(buffer, resp.read_mem.payload, rxlen); 124 address += rxlen; 125 buffer += rxlen; 126 length -= rxlen; 127 } 128 129 return 0; 130 } 131 132 int ath10k_bmi_write_memory(struct ath10k *ar, 133 u32 address, const void *buffer, u32 length) 134 { 135 struct bmi_cmd cmd; 136 u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.write_mem); 137 u32 txlen; 138 int ret; 139 140 ath10k_dbg(ATH10K_DBG_BMI, "bmi write address 0x%x length %d\n", 141 address, length); 142 143 if (ar->bmi.done_sent) { 144 ath10k_warn("command disallowed\n"); 145 return -EBUSY; 146 } 147 148 while (length) { 149 txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen); 150 151 /* copy before roundup to avoid reading beyond buffer*/ 152 memcpy(cmd.write_mem.payload, buffer, txlen); 153 txlen = roundup(txlen, 4); 154 155 cmd.id = __cpu_to_le32(BMI_WRITE_MEMORY); 156 cmd.write_mem.addr = __cpu_to_le32(address); 157 cmd.write_mem.len = __cpu_to_le32(txlen); 158 159 ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen, 160 NULL, NULL); 161 if (ret) { 162 ath10k_warn("unable to write to the device (%d)\n", 163 ret); 164 return ret; 165 } 166 167 /* fixup roundup() so `length` zeroes out for last chunk */ 168 txlen = min(txlen, length); 169 170 address += txlen; 171 buffer += txlen; 172 length -= txlen; 173 } 174 175 return 0; 176 } 177 178 int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param) 179 { 180 struct bmi_cmd cmd; 181 union bmi_resp resp; 182 u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.execute); 183 u32 resplen = sizeof(resp.execute); 184 int ret; 185 186 ath10k_dbg(ATH10K_DBG_BMI, "bmi execute address 0x%x param 0x%x\n", 187 address, *param); 188 189 if (ar->bmi.done_sent) { 190 ath10k_warn("command disallowed\n"); 191 return -EBUSY; 192 } 193 194 cmd.id = __cpu_to_le32(BMI_EXECUTE); 195 cmd.execute.addr = __cpu_to_le32(address); 196 cmd.execute.param = __cpu_to_le32(*param); 197 198 ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, &resp, &resplen); 199 if (ret) { 200 ath10k_warn("unable to read from the device\n"); 201 return ret; 202 } 203 204 if (resplen < sizeof(resp.execute)) { 205 ath10k_warn("invalid execute response length (%d)\n", 206 resplen); 207 return ret; 208 } 209 210 *param = __le32_to_cpu(resp.execute.result); 211 return 0; 212 } 213 214 int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length) 215 { 216 struct bmi_cmd cmd; 217 u32 hdrlen = sizeof(cmd.id) + sizeof(cmd.lz_data); 218 u32 txlen; 219 int ret; 220 221 ath10k_dbg(ATH10K_DBG_BMI, "bmi lz data buffer 0x%p length %d\n", 222 buffer, length); 223 224 if (ar->bmi.done_sent) { 225 ath10k_warn("command disallowed\n"); 226 return -EBUSY; 227 } 228 229 while (length) { 230 txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen); 231 232 WARN_ON_ONCE(txlen & 3); 233 234 cmd.id = __cpu_to_le32(BMI_LZ_DATA); 235 cmd.lz_data.len = __cpu_to_le32(txlen); 236 memcpy(cmd.lz_data.payload, buffer, txlen); 237 238 ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, hdrlen + txlen, 239 NULL, NULL); 240 if (ret) { 241 ath10k_warn("unable to write to the device\n"); 242 return ret; 243 } 244 245 buffer += txlen; 246 length -= txlen; 247 } 248 249 return 0; 250 } 251 252 int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address) 253 { 254 struct bmi_cmd cmd; 255 u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.lz_start); 256 int ret; 257 258 ath10k_dbg(ATH10K_DBG_BMI, "bmi lz stream start address 0x%x\n", 259 address); 260 261 if (ar->bmi.done_sent) { 262 ath10k_warn("command disallowed\n"); 263 return -EBUSY; 264 } 265 266 cmd.id = __cpu_to_le32(BMI_LZ_STREAM_START); 267 cmd.lz_start.addr = __cpu_to_le32(address); 268 269 ret = ath10k_hif_exchange_bmi_msg(ar, &cmd, cmdlen, NULL, NULL); 270 if (ret) { 271 ath10k_warn("unable to Start LZ Stream to the device\n"); 272 return ret; 273 } 274 275 return 0; 276 } 277 278 int ath10k_bmi_fast_download(struct ath10k *ar, 279 u32 address, const void *buffer, u32 length) 280 { 281 u8 trailer[4] = {}; 282 u32 head_len = rounddown(length, 4); 283 u32 trailer_len = length - head_len; 284 int ret; 285 286 ath10k_dbg(ATH10K_DBG_BMI, 287 "bmi fast download address 0x%x buffer 0x%p length %d\n", 288 address, buffer, length); 289 290 ret = ath10k_bmi_lz_stream_start(ar, address); 291 if (ret) 292 return ret; 293 294 /* copy the last word into a zero padded buffer */ 295 if (trailer_len > 0) 296 memcpy(trailer, buffer + head_len, trailer_len); 297 298 ret = ath10k_bmi_lz_data(ar, buffer, head_len); 299 if (ret) 300 return ret; 301 302 if (trailer_len > 0) 303 ret = ath10k_bmi_lz_data(ar, trailer, 4); 304 305 if (ret != 0) 306 return ret; 307 308 /* 309 * Close compressed stream and open a new (fake) one. 310 * This serves mainly to flush Target caches. 311 */ 312 ret = ath10k_bmi_lz_stream_start(ar, 0x00); 313 314 return ret; 315 } 316