blob: cd8ce45bcf2691376a9e9d53c4e52bc4e0786d87 [file] [log] [blame]
/*
* copyright (c) 2014-2015 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"
/* This need to defined in firmware interface files.
* Defining here to address compilation issues.
* Will be deleted once firmware interface files for
* target are merged
*/
#define BMI_LOAD_IMAGE 18
CDF_STATUS
bmi_no_command(struct ol_softc *scn)
{
uint32_t cid;
int status;
uint32_t length;
uint8_t ret = 0;
uint8_t *bmi_cmd_buff = scn->bmi_cmd_buff;
uint8_t *bmi_rsp_buff = scn->bmi_rsp_buff;
if (scn->bmi_done) {
BMI_ERR("Command disallowed: BMI DONE ALREADY");
return CDF_STATUS_E_PERM;
}
if (!bmi_cmd_buff || !bmi_rsp_buff) {
BMI_ERR("No Memory Allocated for BMI CMD/RSP Buffer");
return CDF_STATUS_NOT_INITIALIZED;
}
cid = BMI_NO_COMMAND;
cdf_mem_copy(bmi_cmd_buff, &cid, sizeof(cid));
length = sizeof(ret);
status = hif_exchange_bmi_msg(scn, bmi_cmd_buff, sizeof(cid),
bmi_rsp_buff, &length, BMI_EXCHANGE_TIMEOUT_MS);
if (status) {
BMI_ERR("Failed to write bmi no command status:%d", status);
return CDF_STATUS_E_FAILURE;
}
cdf_mem_copy(&ret, bmi_rsp_buff, length);
if (ret != 0) {
BMI_ERR("bmi no command response error ret 0x%x", ret);
return CDF_STATUS_E_FAILURE;
}
return CDF_STATUS_SUCCESS;
}
CDF_STATUS
bmi_done_local(struct ol_softc *scn)
{
uint32_t cid;
int status;
uint32_t length;
uint8_t ret = 0;
uint8_t *bmi_cmd_buff = scn->bmi_cmd_buff;
uint8_t *bmi_rsp_buff = scn->bmi_rsp_buff;
if (scn->bmi_done) {
BMI_ERR("Command disallowed");
return CDF_STATUS_E_PERM;
}
if (!bmi_cmd_buff || !bmi_rsp_buff) {
BMI_ERR("No Memory Allocated for BMI CMD/RSP Buffer");
return CDF_STATUS_NOT_INITIALIZED;
}
cid = BMI_DONE;
cdf_mem_copy(bmi_cmd_buff, &cid, sizeof(cid));
length = sizeof(ret);
status = hif_exchange_bmi_msg(scn, bmi_cmd_buff, sizeof(cid),
bmi_rsp_buff, &length, BMI_EXCHANGE_TIMEOUT_MS);
if (status) {
BMI_ERR("Failed to close BMI on target status:%d", status);
return CDF_STATUS_E_FAILURE;
}
cdf_mem_copy(&ret, bmi_rsp_buff, length);
if (ret != 0) {
BMI_ERR("BMI DONE response failed:%d", ret);
return CDF_STATUS_E_FAILURE;
}
if (scn->bmi_cmd_buff) {
cdf_os_mem_free_consistent(scn->cdf_dev, MAX_BMI_CMDBUF_SZ,
scn->bmi_cmd_buff, scn->bmi_cmd_da, 0);
scn->bmi_cmd_buff = NULL;
scn->bmi_cmd_da = 0;
}
if (scn->bmi_rsp_buff) {
cdf_os_mem_free_consistent(scn->cdf_dev, MAX_BMI_CMDBUF_SZ,
scn->bmi_rsp_buff, scn->bmi_rsp_da, 0);
scn->bmi_rsp_buff = NULL;
scn->bmi_rsp_da = 0;
}
return CDF_STATUS_SUCCESS;
}
CDF_STATUS
bmi_write_memory(uint32_t address,
uint8_t *buffer,
uint32_t length,
struct ol_softc *scn)
{
uint32_t cid;
int status;
uint32_t rsp_len;
uint8_t ret = 0;
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;
uint8_t *bmi_cmd_buff = scn->bmi_cmd_buff;
uint8_t *bmi_rsp_buff = scn->bmi_rsp_buff;
if (scn->bmi_done) {
BMI_ERR("Command disallowed");
return CDF_STATUS_E_PERM;
}
if (!bmi_cmd_buff || !bmi_rsp_buff) {
BMI_ERR("BMI Initialization is not happened");
return CDF_STATUS_NOT_INITIALIZED;
}
bmi_assert(BMI_COMMAND_FITS(BMI_DATASZ_MAX + header));
cdf_mem_set(bmi_cmd_buff, 0, BMI_DATASZ_MAX + header);
cid = BMI_WRITE_MEMORY;
rsp_len = sizeof(ret);
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;
cdf_mem_copy(&(bmi_cmd_buff[offset]), &cid, sizeof(cid));
offset += sizeof(cid);
cdf_mem_copy(&(bmi_cmd_buff[offset]), &address,
sizeof(address));
offset += sizeof(address);
cdf_mem_copy(&(bmi_cmd_buff[offset]), &txlen, sizeof(txlen));
offset += sizeof(txlen);
cdf_mem_copy(&(bmi_cmd_buff[offset]), src, txlen);
offset += txlen;
status = hif_exchange_bmi_msg(scn, bmi_cmd_buff, offset,
bmi_rsp_buff, &rsp_len, BMI_EXCHANGE_TIMEOUT_MS);
if (status) {
BMI_ERR("BMI Write Memory Failed status:%d", status);
return CDF_STATUS_E_FAILURE;
}
cdf_mem_copy(&ret, bmi_rsp_buff, rsp_len);
if (ret != 0) {
BMI_ERR("BMI Write memory response fail: %x", ret);
return CDF_STATUS_E_FAILURE;
}
remaining -= txlen; address += txlen;
}
return CDF_STATUS_SUCCESS;
}
CDF_STATUS
bmi_read_memory(uint32_t address, uint8_t *buffer,
uint32_t length, struct ol_softc *scn)
{
uint32_t cid;
int status;
uint8_t ret = 0;
uint32_t offset;
uint32_t remaining, rxlen, rsp_len, total_len;
uint8_t *bmi_cmd_buff = scn->bmi_cmd_buff;
/* note we reuse the same buffer to receive on */
uint8_t *bmi_rsp_buff = scn->bmi_rsp_buff;
uint32_t size = sizeof(cid) + sizeof(address) + sizeof(length);
if (scn->bmi_done) {
BMI_ERR("Command disallowed");
return CDF_STATUS_E_PERM;
}
if (!bmi_cmd_buff || !bmi_rsp_buff) {
BMI_ERR("BMI Initialization is not done");
return CDF_STATUS_NOT_INITIALIZED;
}
bmi_assert(BMI_COMMAND_FITS(BMI_DATASZ_MAX + size));
cdf_mem_set(bmi_cmd_buff, 0, BMI_DATASZ_MAX + size);
cdf_mem_set(bmi_rsp_buff, 0, BMI_DATASZ_MAX + size);
cid = BMI_READ_MEMORY;
rsp_len = sizeof(ret);
remaining = length;
while (remaining) {
rxlen = (remaining < BMI_DATASZ_MAX - rsp_len) ? remaining :
(BMI_DATASZ_MAX - rsp_len);
offset = 0;
cdf_mem_copy(&(bmi_cmd_buff[offset]), &cid, sizeof(cid));
offset += sizeof(cid);
cdf_mem_copy(&(bmi_cmd_buff[offset]), &address,
sizeof(address));
offset += sizeof(address);
cdf_mem_copy(&(bmi_cmd_buff[offset]), &rxlen, sizeof(rxlen));
offset += sizeof(length);
total_len = rxlen + rsp_len;
status = hif_exchange_bmi_msg(scn,
bmi_cmd_buff,
offset,
bmi_rsp_buff,
&total_len,
BMI_EXCHANGE_TIMEOUT_MS);
if (status) {
BMI_ERR("BMI Read memory failed status:%d", status);
return CDF_STATUS_E_FAILURE;
}
cdf_mem_copy(&ret, bmi_rsp_buff, rsp_len);
if (ret != 0) {
BMI_ERR("bmi read memory response fail %x", ret);
return CDF_STATUS_E_FAILURE;
}
cdf_mem_copy(&buffer[length - remaining],
(uint8_t *)bmi_rsp_buff + rsp_len, rxlen);
remaining -= rxlen; address += rxlen;
}
return CDF_STATUS_SUCCESS;
}
CDF_STATUS
bmi_execute(uint32_t address, uint32_t *param,
struct ol_softc *scn)
{
uint32_t cid;
int status;
uint32_t length;
uint8_t ret = 0;
uint8_t *bmi_cmd_buff = scn->bmi_cmd_buff;
uint8_t *bmi_rsp_buff = scn->bmi_rsp_buff;
if (scn->bmi_done) {
BMI_ERR("Command disallowed");
return CDF_STATUS_E_PERM;
}
if (!bmi_cmd_buff || !bmi_rsp_buff) {
BMI_ERR("No Memory Allocated for bmi buffers");
return CDF_STATUS_NOT_INITIALIZED;
}
cid = BMI_EXECUTE;
cdf_mem_copy(bmi_cmd_buff, &cid, sizeof(cid));
length = sizeof(ret);
status = hif_exchange_bmi_msg(scn, bmi_cmd_buff, sizeof(cid),
bmi_rsp_buff, &length, BMI_EXCHANGE_TIMEOUT_MS);
if (status) {
BMI_ERR("Failed to do BMI_EXECUTE status:%d", status);
return CDF_STATUS_E_FAILURE;
}
cdf_mem_copy(&ret, bmi_rsp_buff, length);
if (ret != 0) {
BMI_ERR("%s: ret 0x%x", __func__, ret);
return CDF_STATUS_E_FAILURE;
}
return CDF_STATUS_SUCCESS;
}
static CDF_STATUS
bmi_load_image(dma_addr_t address,
uint32_t size, struct ol_softc *scn)
{
uint32_t cid;
CDF_STATUS status;
uint32_t offset;
uint32_t length;
uint8_t ret = 0;
uint8_t *bmi_cmd_buff = scn->bmi_cmd_buff;
uint8_t *bmi_rsp_buff = scn->bmi_rsp_buff;
uint32_t addr_h, addr_l;
if (scn->bmi_done) {
BMI_ERR("Command disallowed");
return CDF_STATUS_E_PERM;
}
if (!bmi_cmd_buff || !bmi_rsp_buff) {
BMI_ERR("No Memory Allocated for BMI CMD/RSP Buffer");
return CDF_STATUS_NOT_INITIALIZED;
}
bmi_assert(BMI_COMMAND_FITS(sizeof(cid) + sizeof(address)));
cdf_mem_set(bmi_cmd_buff, 0, sizeof(cid) + sizeof(address));
BMI_DBG("%s: Enter device: 0x%p, size %d", __func__, scn, size);
cid = BMI_LOAD_IMAGE;
offset = 0;
cdf_mem_copy(&(bmi_cmd_buff[offset]), &cid, sizeof(cid));
offset += sizeof(cid);
addr_l = address & 0xffffffff;
addr_h = 0x00;
cdf_mem_copy(&(bmi_cmd_buff[offset]), &addr_l, sizeof(addr_l));
offset += sizeof(addr_l);
cdf_mem_copy(&(bmi_cmd_buff[offset]), &addr_h, sizeof(addr_h));
offset += sizeof(addr_h);
cdf_mem_copy(&(bmi_cmd_buff[offset]), &size, sizeof(size));
offset += sizeof(size);
length = sizeof(ret);
status = hif_exchange_bmi_msg(scn, bmi_cmd_buff, offset,
bmi_rsp_buff, &length, BMI_EXCHANGE_TIMEOUT_MS);
if (status) {
BMI_ERR("BMI Load Image Failed; status:%d", status);
return CDF_STATUS_E_FAILURE;
}
cdf_mem_copy(&ret, bmi_rsp_buff, length);
if (ret != 0) {
BMI_ERR("%s: ret 0x%x", __func__, ret);
return CDF_STATUS_E_FAILURE;
}
return CDF_STATUS_SUCCESS;
}
static CDF_STATUS bmi_enable(struct ol_softc *scn)
{
struct bmi_target_info targ_info;
struct image_desc_info image_desc_info;
CDF_STATUS status;
if (!scn) {
BMI_ERR("Invalid scn context");
bmi_assert(0);
return CDF_STATUS_NOT_INITIALIZED;
}
if (scn->bmi_cmd_buff == NULL || scn->bmi_rsp_buff == NULL) {
BMI_ERR("bmi_open failed!");
return CDF_STATUS_NOT_INITIALIZED;
}
status = bmi_get_target_info(&targ_info, scn);
if (status != CDF_STATUS_SUCCESS)
return status;
BMI_DBG("%s: target type 0x%x, target ver 0x%x", __func__,
targ_info.target_type, targ_info.target_ver);
scn->target_type = targ_info.target_type;
scn->target_version = targ_info.target_ver;
if (cnss_get_fw_image(&image_desc_info) != 0) {
BMI_ERR("Failed to get fw image");
return CDF_STATUS_E_FAILURE;
}
status = bmi_load_image(image_desc_info.bdata_addr,
image_desc_info.bdata_size,
scn);
if (status != CDF_STATUS_SUCCESS) {
BMI_ERR("Load board data failed! status:%d", status);
return status;
}
status = bmi_load_image(image_desc_info.fw_addr,
image_desc_info.fw_size,
scn);
if (status != CDF_STATUS_SUCCESS)
BMI_ERR("Load fw image failed! status:%d", status);
return status;
}
CDF_STATUS bmi_firmware_download(struct ol_softc *scn)
{
CDF_STATUS status;
if (IHELIUM_NO_BMI)
return CDF_STATUS_SUCCESS;
status = bmi_init(scn);
if (status != CDF_STATUS_SUCCESS) {
BMI_ERR("BMI_INIT Failed status:%d", status);
goto end;
}
status = bmi_enable(scn);
if (status != CDF_STATUS_SUCCESS) {
BMI_ERR("BMI_ENABLE failed status:%d\n", status);
goto err_bmi_enable;
}
return status;
err_bmi_enable:
bmi_cleanup(scn);
end:
return status;
}