Prakash Dhavali | 7090c5f | 2015-11-02 17:55:19 -0800 | [diff] [blame^] | 1 | /* |
| 2 | * copyright (c) 2014-2015 The Linux Foundation. All rights reserved. |
| 3 | * |
| 4 | * Previously licensed under the ISC license by Qualcomm Atheros, Inc. |
| 5 | * |
| 6 | * |
| 7 | * Permission to use, copy, modify, and/or distribute this software for |
| 8 | * any purpose with or without fee is hereby granted, provided that the |
| 9 | * above copyright notice and this permission notice appear in all |
| 10 | * copies. |
| 11 | * |
| 12 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL |
| 13 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
| 14 | * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE |
| 15 | * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
| 16 | * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
| 17 | * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| 18 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| 19 | * PERFORMANCE OF THIS SOFTWARE. |
| 20 | */ |
| 21 | |
| 22 | /* |
| 23 | * This file was originally distributed by Qualcomm Atheros, Inc. |
| 24 | * under proprietary terms before Copyright ownership was assigned |
| 25 | * to the Linux Foundation. |
| 26 | */ |
| 27 | |
| 28 | #include "i_bmi.h" |
| 29 | |
| 30 | /* APIs visible to the driver */ |
| 31 | |
| 32 | CDF_STATUS |
| 33 | bmi_read_memory(uint32_t address, |
| 34 | uint8_t *buffer, uint32_t length, struct ol_softc *scn) |
| 35 | { |
| 36 | uint32_t cid; |
| 37 | int status; |
| 38 | uint32_t offset; |
| 39 | uint32_t remaining, rxlen; |
| 40 | uint8_t *bmi_cmd_buff = scn->bmi_cmd_buff; |
| 41 | uint8_t *bmi_rsp_buff = scn->bmi_rsp_buff; |
| 42 | uint32_t align; |
| 43 | |
| 44 | if (scn->bmi_done) { |
| 45 | BMI_DBG("command disallowed"); |
| 46 | return CDF_STATUS_E_PERM; |
| 47 | } |
| 48 | |
| 49 | if (!scn->bmi_cmd_buff || !scn->bmi_rsp_buff) { |
| 50 | BMI_ERR("BMI Initialization hasn't done"); |
| 51 | return CDF_STATUS_NOT_INITIALIZED; |
| 52 | } |
| 53 | |
| 54 | bmi_assert(BMI_COMMAND_FITS(BMI_DATASZ_MAX + sizeof(cid) + |
| 55 | sizeof(address) + sizeof(length))); |
| 56 | cdf_mem_set(bmi_cmd_buff, 0, BMI_DATASZ_MAX + sizeof(cid) + |
| 57 | sizeof(address) + sizeof(length)); |
| 58 | cdf_mem_set(bmi_rsp_buff, 0, BMI_DATASZ_MAX + sizeof(cid) + |
| 59 | sizeof(address) + sizeof(length)); |
| 60 | |
| 61 | BMI_DBG("BMI Read: device: 0x%p, address: 0x%x, length: %d", |
| 62 | scn, address, length); |
| 63 | |
| 64 | cid = BMI_READ_MEMORY; |
| 65 | align = 0; |
| 66 | remaining = length; |
| 67 | |
| 68 | while (remaining) { |
| 69 | rxlen = (remaining < BMI_DATASZ_MAX) ? |
| 70 | remaining : BMI_DATASZ_MAX; |
| 71 | offset = 0; |
| 72 | cdf_mem_copy(&(bmi_cmd_buff[offset]), &cid, sizeof(cid)); |
| 73 | offset += sizeof(cid); |
| 74 | cdf_mem_copy(&(bmi_cmd_buff[offset]), &address, |
| 75 | sizeof(address)); |
| 76 | offset += sizeof(address); |
| 77 | cdf_mem_copy(&(bmi_cmd_buff[offset]), &rxlen, sizeof(rxlen)); |
| 78 | offset += sizeof(length); |
| 79 | |
| 80 | /* note we reuse the same buffer to receive on */ |
| 81 | status = hif_exchange_bmi_msg(scn, bmi_cmd_buff, offset, |
| 82 | bmi_rsp_buff, &rxlen, BMI_EXCHANGE_TIMEOUT_MS); |
| 83 | if (status) { |
| 84 | BMI_ERR("Unable to read from the device"); |
| 85 | return CDF_STATUS_E_FAILURE; |
| 86 | } |
| 87 | if (remaining == rxlen) { |
| 88 | cdf_mem_copy(&buffer[length - remaining + align], |
| 89 | bmi_rsp_buff, rxlen - align); |
| 90 | /* last align bytes are invalid */ |
| 91 | } else { |
| 92 | cdf_mem_copy(&buffer[length - remaining + align], |
| 93 | bmi_rsp_buff, rxlen); |
| 94 | } |
| 95 | remaining -= rxlen; |
| 96 | address += rxlen; |
| 97 | } |
| 98 | |
| 99 | BMI_DBG("BMI Read Memory: Exit"); |
| 100 | return CDF_STATUS_SUCCESS; |
| 101 | } |
| 102 | |
| 103 | CDF_STATUS |
| 104 | bmi_write_memory(uint32_t address, |
| 105 | uint8_t *buffer, uint32_t length, struct ol_softc *scn) |
| 106 | { |
| 107 | uint32_t cid; |
| 108 | int status; |
| 109 | uint32_t offset; |
| 110 | uint32_t remaining, txlen; |
| 111 | const uint32_t header = sizeof(cid) + sizeof(address) + sizeof(length); |
| 112 | uint8_t aligned_buffer[BMI_DATASZ_MAX]; |
| 113 | uint8_t *src; |
| 114 | uint8_t *bmi_cmd_buff = scn->bmi_cmd_buff; |
| 115 | |
| 116 | if (scn->bmi_done) { |
| 117 | BMI_ERR("Command disallowed"); |
| 118 | return CDF_STATUS_E_PERM; |
| 119 | } |
| 120 | |
| 121 | if (!bmi_cmd_buff) { |
| 122 | BMI_ERR("BMI initialization hasn't done"); |
| 123 | return CDF_STATUS_E_PERM; |
| 124 | } |
| 125 | |
| 126 | bmi_assert(BMI_COMMAND_FITS(BMI_DATASZ_MAX + header)); |
| 127 | cdf_mem_set(bmi_cmd_buff, 0, BMI_DATASZ_MAX + header); |
| 128 | |
| 129 | BMI_DBG("BMI Write Memory:device: 0x%p, address: 0x%x, length: %d", |
| 130 | scn, address, length); |
| 131 | |
| 132 | cid = BMI_WRITE_MEMORY; |
| 133 | |
| 134 | remaining = length; |
| 135 | while (remaining) { |
| 136 | src = &buffer[length - remaining]; |
| 137 | if (remaining < (BMI_DATASZ_MAX - header)) { |
| 138 | if (remaining & 3) { |
| 139 | /* align it with 4 bytes */ |
| 140 | remaining = remaining + (4 - (remaining & 3)); |
| 141 | memcpy(aligned_buffer, src, remaining); |
| 142 | src = aligned_buffer; |
| 143 | } |
| 144 | txlen = remaining; |
| 145 | } else { |
| 146 | txlen = (BMI_DATASZ_MAX - header); |
| 147 | } |
| 148 | offset = 0; |
| 149 | cdf_mem_copy(&(bmi_cmd_buff[offset]), &cid, sizeof(cid)); |
| 150 | offset += sizeof(cid); |
| 151 | cdf_mem_copy(&(bmi_cmd_buff[offset]), &address, |
| 152 | sizeof(address)); |
| 153 | offset += sizeof(address); |
| 154 | cdf_mem_copy(&(bmi_cmd_buff[offset]), &txlen, sizeof(txlen)); |
| 155 | offset += sizeof(txlen); |
| 156 | cdf_mem_copy(&(bmi_cmd_buff[offset]), src, txlen); |
| 157 | offset += txlen; |
| 158 | status = hif_exchange_bmi_msg(scn, bmi_cmd_buff, offset, |
| 159 | NULL, NULL, BMI_EXCHANGE_TIMEOUT_MS); |
| 160 | if (status) { |
| 161 | BMI_ERR("Unable to write to the device; status:%d", |
| 162 | status); |
| 163 | return CDF_STATUS_E_FAILURE; |
| 164 | } |
| 165 | remaining -= txlen; |
| 166 | address += txlen; |
| 167 | } |
| 168 | |
| 169 | BMI_DBG("BMI Write Memory: Exit"); |
| 170 | |
| 171 | return CDF_STATUS_SUCCESS; |
| 172 | } |
| 173 | |
| 174 | CDF_STATUS |
| 175 | bmi_execute(uint32_t address, A_UINT32 *param, struct ol_softc *scn) |
| 176 | { |
| 177 | uint32_t cid; |
| 178 | int status; |
| 179 | uint32_t offset; |
| 180 | uint32_t param_len; |
| 181 | uint8_t *bmi_cmd_buff = scn->bmi_cmd_buff; |
| 182 | uint8_t *bmi_rsp_buff = scn->bmi_rsp_buff; |
| 183 | uint32_t size = sizeof(cid) + sizeof(address) + sizeof(param); |
| 184 | |
| 185 | if (scn->bmi_done) { |
| 186 | BMI_ERR("Command disallowed"); |
| 187 | return CDF_STATUS_E_PERM; |
| 188 | } |
| 189 | |
| 190 | if (!bmi_cmd_buff || !bmi_rsp_buff) { |
| 191 | BMI_ERR("%s:BMI CMD/RSP Buffer is NULL", __func__); |
| 192 | return CDF_STATUS_NOT_INITIALIZED; |
| 193 | } |
| 194 | |
| 195 | bmi_assert(BMI_COMMAND_FITS(size)); |
| 196 | cdf_mem_set(bmi_cmd_buff, 0, size); |
| 197 | cdf_mem_set(bmi_rsp_buff, 0, size); |
| 198 | |
| 199 | |
| 200 | BMI_DBG("BMI Execute: device: 0x%p, address: 0x%x, param: %d", |
| 201 | scn, address, *param); |
| 202 | |
| 203 | cid = BMI_EXECUTE; |
| 204 | |
| 205 | offset = 0; |
| 206 | cdf_mem_copy(&(bmi_cmd_buff[offset]), &cid, sizeof(cid)); |
| 207 | offset += sizeof(cid); |
| 208 | cdf_mem_copy(&(bmi_cmd_buff[offset]), &address, sizeof(address)); |
| 209 | offset += sizeof(address); |
| 210 | cdf_mem_copy(&(bmi_cmd_buff[offset]), param, sizeof(*param)); |
| 211 | offset += sizeof(*param); |
| 212 | param_len = sizeof(*param); |
| 213 | status = hif_exchange_bmi_msg(scn, bmi_cmd_buff, offset, |
| 214 | bmi_rsp_buff, ¶m_len, 0); |
| 215 | if (status) { |
| 216 | BMI_ERR("Unable to read from the device status:%d", status); |
| 217 | return CDF_STATUS_E_FAILURE; |
| 218 | } |
| 219 | |
| 220 | cdf_mem_copy(param, bmi_rsp_buff, sizeof(*param)); |
| 221 | |
| 222 | BMI_DBG("BMI Execute: Exit (param: %d)", *param); |
| 223 | return CDF_STATUS_SUCCESS; |
| 224 | } |
| 225 | |
| 226 | inline CDF_STATUS |
| 227 | bmi_no_command(struct ol_softc *scn) |
| 228 | { |
| 229 | return CDF_STATUS_SUCCESS; |
| 230 | } |
| 231 | |
| 232 | CDF_STATUS |
| 233 | bmi_firmware_download(struct ol_softc *scn) |
| 234 | { |
| 235 | CDF_STATUS status; |
| 236 | struct bmi_target_info targ_info; |
| 237 | cdf_mem_zero(&targ_info, sizeof(targ_info)); |
| 238 | |
| 239 | /* Initialize BMI */ |
| 240 | status = bmi_init(scn); |
| 241 | if (status != CDF_STATUS_SUCCESS) { |
| 242 | BMI_ERR("BMI Initialization Failed err:%d", status); |
| 243 | return status; |
| 244 | } |
| 245 | |
| 246 | /* Get target information */ |
| 247 | status = bmi_get_target_info(&targ_info, scn); |
| 248 | if (status != CDF_STATUS_SUCCESS) { |
| 249 | BMI_ERR("BMI Target Info get failed: status:%d", status); |
| 250 | return status; |
| 251 | } |
| 252 | |
| 253 | scn->target_type = targ_info.target_type; |
| 254 | scn->target_version = targ_info.target_ver; |
| 255 | |
| 256 | /* Configure target */ |
| 257 | status = ol_configure_target(scn); |
| 258 | if (status != CDF_STATUS_SUCCESS) { |
| 259 | BMI_ERR("BMI Configure Target Failed status:%d", status); |
| 260 | return status; |
| 261 | } |
| 262 | |
| 263 | status = ol_download_firmware(scn); |
| 264 | if (status != CDF_STATUS_SUCCESS) |
| 265 | BMI_ERR("BMI Download Firmware Failed Status:%d", status); |
| 266 | |
| 267 | return status; |
| 268 | } |
| 269 | |
| 270 | CDF_STATUS bmi_done_local(struct ol_softc *scn) |
| 271 | { |
| 272 | int status; |
| 273 | uint32_t cid; |
| 274 | |
| 275 | if (!scn) { |
| 276 | BMI_ERR("Invalid scn context"); |
| 277 | bmi_assert(0); |
| 278 | return CDF_STATUS_NOT_INITIALIZED; |
| 279 | } |
| 280 | |
| 281 | if (scn->bmi_done) { |
| 282 | BMI_DBG("bmi_done_local skipped"); |
| 283 | return CDF_STATUS_E_PERM; |
| 284 | } |
| 285 | |
| 286 | BMI_DBG("BMI Done: Enter (device: 0x%p)", scn); |
| 287 | |
| 288 | scn->bmi_done = true; |
| 289 | cid = BMI_DONE; |
| 290 | |
| 291 | if (!scn->bmi_cmd_buff) { |
| 292 | BMI_ERR("Invalid scn BMICmdBuff"); |
| 293 | bmi_assert(0); |
| 294 | return CDF_STATUS_NOT_INITIALIZED; |
| 295 | } |
| 296 | |
| 297 | cdf_mem_copy(scn->bmi_cmd_buff, &cid, sizeof(cid)); |
| 298 | |
| 299 | status = hif_exchange_bmi_msg(scn, scn->bmi_cmd_buff, |
| 300 | sizeof(cid), NULL, NULL, 0); |
| 301 | if (status) { |
| 302 | BMI_ERR("Failed to write to the device; status:%d", status); |
| 303 | return CDF_STATUS_E_FAILURE; |
| 304 | } |
| 305 | |
| 306 | if (scn->bmi_cmd_buff) { |
| 307 | cdf_os_mem_free_consistent(scn->cdf_dev, MAX_BMI_CMDBUF_SZ, |
| 308 | scn->bmi_cmd_buff, scn->bmi_cmd_da, 0); |
| 309 | scn->bmi_cmd_buff = NULL; |
| 310 | scn->bmi_cmd_da = 0; |
| 311 | } |
| 312 | |
| 313 | if (scn->bmi_rsp_buff) { |
| 314 | cdf_os_mem_free_consistent(scn->cdf_dev, MAX_BMI_CMDBUF_SZ, |
| 315 | scn->bmi_rsp_buff, scn->bmi_rsp_da, 0); |
| 316 | scn->bmi_rsp_buff = NULL; |
| 317 | scn->bmi_rsp_da = 0; |
| 318 | } |
| 319 | |
| 320 | return CDF_STATUS_SUCCESS; |
| 321 | } |