blob: 9033d9dc1686025612d306aa1589f7a4764bb2fc [file] [log] [blame]
/*
* Copyright (c) 2014-2016 The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* This file was originally distributed by Qualcomm Atheros, Inc.
* under proprietary terms before Copyright ownership was assigned
* to the Linux Foundation.
*/
#include "i_bmi.h"
#include "cds_api.h"
/* APIs visible to the driver */
QDF_STATUS
bmi_read_memory(uint32_t address,
uint8_t *buffer, uint32_t length, struct ol_context *ol_ctx)
{
struct hif_opaque_softc *scn = ol_ctx->scn;
uint32_t cid;
int status;
uint32_t offset;
uint32_t remaining, rxlen;
struct bmi_info *info = GET_BMI_CONTEXT(ol_ctx);
uint8_t *bmi_cmd_buff = info->bmi_cmd_buff;
uint8_t *bmi_rsp_buff = info->bmi_rsp_buff;
uint32_t align;
qdf_dma_addr_t cmd = info->bmi_cmd_da;
qdf_dma_addr_t rsp = info->bmi_rsp_da;
if (info->bmi_done) {
BMI_DBG("command disallowed");
return QDF_STATUS_E_PERM;
}
if (!info->bmi_cmd_buff || !info->bmi_rsp_buff) {
BMI_ERR("BMI Initialization hasn't done");
return QDF_STATUS_NOT_INITIALIZED;
}
bmi_assert(BMI_COMMAND_FITS(BMI_DATASZ_MAX + sizeof(cid) +
sizeof(address) + sizeof(length)));
qdf_mem_set(bmi_cmd_buff, 0, BMI_DATASZ_MAX + sizeof(cid) +
sizeof(address) + sizeof(length));
qdf_mem_set(bmi_rsp_buff, 0, BMI_DATASZ_MAX + sizeof(cid) +
sizeof(address) + sizeof(length));
cid = BMI_READ_MEMORY;
align = 0;
remaining = length;
while (remaining) {
rxlen = (remaining < BMI_DATASZ_MAX) ?
remaining : BMI_DATASZ_MAX;
offset = 0;
qdf_mem_copy(&(bmi_cmd_buff[offset]), &cid, sizeof(cid));
offset += sizeof(cid);
qdf_mem_copy(&(bmi_cmd_buff[offset]), &address,
sizeof(address));
offset += sizeof(address);
qdf_mem_copy(&(bmi_cmd_buff[offset]), &rxlen, sizeof(rxlen));
offset += sizeof(length);
/* note we reuse the same buffer to receive on */
status = hif_exchange_bmi_msg(scn, cmd, rsp, bmi_cmd_buff,
offset, bmi_rsp_buff, &rxlen,
BMI_EXCHANGE_TIMEOUT_MS);
if (status) {
BMI_ERR("Unable to read from the device");
return QDF_STATUS_E_FAILURE;
}
if (remaining == rxlen) {
qdf_mem_copy(&buffer[length - remaining + align],
bmi_rsp_buff, rxlen - align);
/* last align bytes are invalid */
} else {
qdf_mem_copy(&buffer[length - remaining + align],
bmi_rsp_buff, rxlen);
}
remaining -= rxlen;
address += rxlen;
}
return QDF_STATUS_SUCCESS;
}
QDF_STATUS bmi_write_memory(uint32_t address, uint8_t *buffer, uint32_t length,
struct ol_context *ol_ctx)
{
struct hif_opaque_softc *scn = ol_ctx->scn;
uint32_t cid;
int status;
uint32_t offset;
uint32_t remaining, txlen;
const uint32_t header = sizeof(cid) + sizeof(address) + sizeof(length);
uint8_t aligned_buffer[BMI_DATASZ_MAX];
uint8_t *src;
struct bmi_info *info = GET_BMI_CONTEXT(ol_ctx);
uint8_t *bmi_cmd_buff = info->bmi_cmd_buff;
qdf_dma_addr_t cmd = info->bmi_cmd_da;
qdf_dma_addr_t rsp = info->bmi_rsp_da;
if (info->bmi_done) {
BMI_ERR("Command disallowed");
return QDF_STATUS_E_PERM;
}
if (!bmi_cmd_buff) {
BMI_ERR("BMI initialization hasn't done");
return QDF_STATUS_E_PERM;
}
bmi_assert(BMI_COMMAND_FITS(BMI_DATASZ_MAX + header));
qdf_mem_set(bmi_cmd_buff, 0, BMI_DATASZ_MAX + header);
cid = BMI_WRITE_MEMORY;
remaining = length;
while (remaining) {
src = &buffer[length - remaining];
if (remaining < (BMI_DATASZ_MAX - header)) {
if (remaining & 3) {
/* align it with 4 bytes */
remaining = remaining + (4 - (remaining & 3));
memcpy(aligned_buffer, src, remaining);
src = aligned_buffer;
}
txlen = remaining;
} else {
txlen = (BMI_DATASZ_MAX - header);
}
offset = 0;
qdf_mem_copy(&(bmi_cmd_buff[offset]), &cid, sizeof(cid));
offset += sizeof(cid);
qdf_mem_copy(&(bmi_cmd_buff[offset]), &address,
sizeof(address));
offset += sizeof(address);
qdf_mem_copy(&(bmi_cmd_buff[offset]), &txlen, sizeof(txlen));
offset += sizeof(txlen);
qdf_mem_copy(&(bmi_cmd_buff[offset]), src, txlen);
offset += txlen;
status = hif_exchange_bmi_msg(scn, cmd, rsp, bmi_cmd_buff,
offset, NULL, NULL,
BMI_EXCHANGE_TIMEOUT_MS);
if (status) {
BMI_ERR("Unable to write to the device; status:%d",
status);
return QDF_STATUS_E_FAILURE;
}
remaining -= txlen;
address += txlen;
}
return QDF_STATUS_SUCCESS;
}
QDF_STATUS
bmi_execute(uint32_t address, A_UINT32 *param, struct ol_context *ol_ctx)
{
struct hif_opaque_softc *scn = ol_ctx->scn;
uint32_t cid;
int status;
uint32_t offset;
uint32_t param_len;
struct bmi_info *info = GET_BMI_CONTEXT(ol_ctx);
uint8_t *bmi_cmd_buff = info->bmi_cmd_buff;
uint8_t *bmi_rsp_buff = info->bmi_rsp_buff;
uint32_t size = sizeof(cid) + sizeof(address) + sizeof(param);
qdf_dma_addr_t cmd = info->bmi_cmd_da;
qdf_dma_addr_t rsp = info->bmi_rsp_da;
if (info->bmi_done) {
BMI_ERR("Command disallowed");
return QDF_STATUS_E_PERM;
}
if (!bmi_cmd_buff || !bmi_rsp_buff) {
BMI_ERR("%s:BMI CMD/RSP Buffer is NULL", __func__);
return QDF_STATUS_NOT_INITIALIZED;
}
bmi_assert(BMI_COMMAND_FITS(size));
qdf_mem_set(bmi_cmd_buff, 0, size);
qdf_mem_set(bmi_rsp_buff, 0, size);
BMI_DBG("BMI Execute: device: 0x%p, address: 0x%x, param: %d",
scn, address, *param);
cid = BMI_EXECUTE;
offset = 0;
qdf_mem_copy(&(bmi_cmd_buff[offset]), &cid, sizeof(cid));
offset += sizeof(cid);
qdf_mem_copy(&(bmi_cmd_buff[offset]), &address, sizeof(address));
offset += sizeof(address);
qdf_mem_copy(&(bmi_cmd_buff[offset]), param, sizeof(*param));
offset += sizeof(*param);
param_len = sizeof(*param);
status = hif_exchange_bmi_msg(scn, cmd, rsp, bmi_cmd_buff, offset,
bmi_rsp_buff, &param_len, 0);
if (status) {
BMI_ERR("Unable to read from the device status:%d", status);
return QDF_STATUS_E_FAILURE;
}
qdf_mem_copy(param, bmi_rsp_buff, sizeof(*param));
BMI_DBG("BMI Execute: Exit (param: %d)", *param);
return QDF_STATUS_SUCCESS;
}
inline QDF_STATUS
bmi_no_command(struct ol_context *ol_ctx)
{
return QDF_STATUS_SUCCESS;
}
QDF_STATUS
bmi_firmware_download(struct ol_context *ol_ctx)
{
struct hif_opaque_softc *scn = ol_ctx->scn;
QDF_STATUS status;
struct bmi_target_info targ_info;
struct hif_target_info *tgt_info = hif_get_target_info_handle(scn);
qdf_mem_zero(&targ_info, sizeof(targ_info));
/* Initialize BMI */
status = bmi_init(ol_ctx);
if (status != QDF_STATUS_SUCCESS) {
BMI_ERR("BMI Initialization Failed err:%d", status);
return status;
}
/* Get target information */
status = bmi_get_target_info(&targ_info, ol_ctx);
if (status != QDF_STATUS_SUCCESS) {
BMI_ERR("BMI Target Info get failed: status:%d", status);
return status;
}
tgt_info->target_type = targ_info.target_type;
tgt_info->target_version = targ_info.target_ver;
/* Configure target */
status = ol_configure_target(ol_ctx);
if (status != QDF_STATUS_SUCCESS) {
BMI_ERR("BMI Configure Target Failed status:%d", status);
return status;
}
status = ol_download_firmware(ol_ctx);
if (status != QDF_STATUS_SUCCESS)
BMI_ERR("BMI Download Firmware Failed Status:%d", status);
return status;
}
QDF_STATUS bmi_done_local(struct ol_context *ol_ctx)
{
struct hif_opaque_softc *scn = ol_ctx->scn;
int status;
uint32_t cid;
struct bmi_info *info;
qdf_device_t qdf_dev = ol_ctx->qdf_dev;
qdf_dma_addr_t cmd, rsp;
if (!scn) {
BMI_ERR("Invalid scn context");
bmi_assert(0);
return QDF_STATUS_NOT_INITIALIZED;
}
if (!qdf_dev->dev) {
BMI_ERR("%s: Invalid device pointer", __func__);
return QDF_STATUS_NOT_INITIALIZED;
}
info = GET_BMI_CONTEXT(ol_ctx);
if (info->bmi_done) {
BMI_DBG("bmi_done_local skipped");
return QDF_STATUS_E_PERM;
}
cmd = info->bmi_cmd_da;
rsp = info->bmi_rsp_da;
BMI_DBG("BMI Done: Enter (device: 0x%p)", scn);
info->bmi_done = true;
cid = BMI_DONE;
if (!info->bmi_cmd_buff) {
BMI_ERR("Invalid scn BMICmdBuff");
bmi_assert(0);
return QDF_STATUS_NOT_INITIALIZED;
}
qdf_mem_copy(info->bmi_cmd_buff, &cid, sizeof(cid));
status = hif_exchange_bmi_msg(scn, cmd, rsp, info->bmi_cmd_buff,
sizeof(cid), NULL, NULL, 0);
if (status) {
BMI_ERR("Failed to write to the device; status:%d", status);
return QDF_STATUS_E_FAILURE;
}
if (info->bmi_cmd_buff) {
qdf_mem_free_consistent(qdf_dev, qdf_dev->dev,
MAX_BMI_CMDBUF_SZ,
info->bmi_cmd_buff, info->bmi_cmd_da, 0);
info->bmi_cmd_buff = NULL;
info->bmi_cmd_da = 0;
}
if (info->bmi_rsp_buff) {
qdf_mem_free_consistent(qdf_dev, qdf_dev->dev,
MAX_BMI_CMDBUF_SZ,
info->bmi_rsp_buff, info->bmi_rsp_da, 0);
info->bmi_rsp_buff = NULL;
info->bmi_rsp_da = 0;
}
return QDF_STATUS_SUCCESS;
}