blob: b2d541d2d2f4eaf8c50f54e6eaa85e0904188c26 [file] [log] [blame]
/*
* Copyright (c) 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 <osdep.h>
#include "a_types.h"
#include "athdefs.h"
#include "osapi_linux.h"
#include "targcfg.h"
#include "cdf_lock.h"
#include "cdf_status.h"
#include <cdf_atomic.h> /* cdf_atomic_read */
#include <targaddrs.h>
#include <bmi_msg.h>
#include "hif_io32.h"
#include <hif.h>
#include "regtable.h"
#define ATH_MODULE_NAME hif
#include <a_debug.h>
#include "hif_main.h"
#include "ce_api.h"
#include "cdf_trace.h"
#include "cds_api.h"
#ifdef CONFIG_CNSS
#include <net/cnss.h>
#else
#include "cnss_stub.h"
#endif
#include <cds_get_bin.h>
#include "epping_main.h"
#include "hif_debug.h"
/* Track a BMI transaction that is in progress */
#ifndef BIT
#define BIT(n) (1 << (n))
#endif
enum {
BMI_REQ_SEND_DONE = BIT(0), /* the bmi tx completion */
BMI_RESP_RECV_DONE = BIT(1), /* the bmi respond is received */
};
struct BMI_transaction {
struct HIF_CE_state *hif_state;
cdf_semaphore_t bmi_transaction_sem;
uint8_t *bmi_request_host; /* Req BMI msg in Host addr space */
cdf_dma_addr_t bmi_request_CE; /* Req BMI msg in CE addr space */
uint32_t bmi_request_length; /* Length of BMI request */
uint8_t *bmi_response_host; /* Rsp BMI msg in Host addr space */
cdf_dma_addr_t bmi_response_CE; /* Rsp BMI msg in CE addr space */
unsigned int bmi_response_length; /* Length of received response */
unsigned int bmi_timeout_ms;
uint32_t bmi_transaction_flags; /* flags for the transcation */
};
/*
* send/recv completion functions for BMI.
* NB: The "net_buf" parameter is actually just a
* straight buffer, not an sk_buff.
*/
void hif_bmi_send_done(struct CE_handle *copyeng, void *ce_context,
void *transfer_context, cdf_dma_addr_t data,
unsigned int nbytes,
unsigned int transfer_id, unsigned int sw_index,
unsigned int hw_index, uint32_t toeplitz_hash_result)
{
struct BMI_transaction *transaction =
(struct BMI_transaction *)transfer_context;
struct ol_softc *scn = transaction->hif_state->scn;
#ifdef BMI_RSP_POLLING
/*
* Fix EV118783, Release a semaphore after sending
* no matter whether a response is been expecting now.
*/
cdf_semaphore_release(scn->cdf_dev,
&transaction->bmi_transaction_sem);
#else
/*
* If a response is anticipated, we'll complete the
* transaction if the response has been received.
* If no response is anticipated, complete the
* transaction now.
*/
transaction->bmi_transaction_flags |= BMI_REQ_SEND_DONE;
/* resp is't needed or has already been received,
* never assume resp comes later then this */
if (!transaction->bmi_response_CE ||
(transaction->bmi_transaction_flags & BMI_RESP_RECV_DONE)) {
cdf_semaphore_release(scn->cdf_dev,
&transaction->bmi_transaction_sem);
}
#endif
}
#ifndef BMI_RSP_POLLING
void hif_bmi_recv_data(struct CE_handle *copyeng, void *ce_context,
void *transfer_context, cdf_dma_addr_t data,
unsigned int nbytes,
unsigned int transfer_id, unsigned int flags)
{
struct BMI_transaction *transaction =
(struct BMI_transaction *)transfer_context;
struct ol_softc *scn = transaction->hif_state->scn;
transaction->bmi_response_length = nbytes;
transaction->bmi_transaction_flags |= BMI_RESP_RECV_DONE;
/* when both send/recv are done, the sem can be released */
if (transaction->bmi_transaction_flags & BMI_REQ_SEND_DONE) {
cdf_semaphore_release(scn->cdf_dev,
&transaction->bmi_transaction_sem);
}
}
#endif
CDF_STATUS hif_exchange_bmi_msg(struct ol_softc *scn,
uint8_t *bmi_request,
uint32_t request_length,
uint8_t *bmi_response,
uint32_t *bmi_response_lengthp, uint32_t TimeoutMS)
{
struct HIF_CE_state *hif_state = (struct HIF_CE_state *)scn->hif_hdl;
struct HIF_CE_pipe_info *send_pipe_info =
&(hif_state->pipe_info[BMI_CE_NUM_TO_TARG]);
struct CE_handle *ce_send_hdl = send_pipe_info->ce_hdl;
cdf_dma_addr_t CE_request, CE_response = 0;
struct BMI_transaction *transaction = NULL;
int status = CDF_STATUS_SUCCESS;
struct HIF_CE_pipe_info *recv_pipe_info =
&(hif_state->pipe_info[BMI_CE_NUM_TO_HOST]);
struct CE_handle *ce_recv = recv_pipe_info->ce_hdl;
unsigned int mux_id = 0;
unsigned int transaction_id = 0xffff;
unsigned int user_flags = 0;
#ifdef BMI_RSP_POLLING
cdf_dma_addr_t buf;
unsigned int completed_nbytes, id, flags;
int i;
#endif
transaction =
(struct BMI_transaction *)cdf_mem_malloc(sizeof(*transaction));
if (unlikely(!transaction)) {
HIF_ERROR("%s: no memory", __func__);
return CDF_STATUS_E_NOMEM;
}
transaction_id = (mux_id & MUX_ID_MASK) |
(transaction_id & TRANSACTION_ID_MASK);
#ifdef QCA_WIFI_3_0
user_flags &= DESC_DATA_FLAG_MASK;
#endif
A_TARGET_ACCESS_LIKELY(scn);
/* Initialize bmi_transaction_sem to block */
cdf_semaphore_init(&transaction->bmi_transaction_sem);
cdf_semaphore_acquire(scn->cdf_dev, &transaction->bmi_transaction_sem);
transaction->hif_state = hif_state;
transaction->bmi_request_host = bmi_request;
transaction->bmi_request_length = request_length;
transaction->bmi_response_length = 0;
transaction->bmi_timeout_ms = TimeoutMS;
transaction->bmi_transaction_flags = 0;
/*
* CE_request = dma_map_single(dev,
* (void *)bmi_request, request_length, DMA_TO_DEVICE);
*/
CE_request = scn->bmi_cmd_da;
transaction->bmi_request_CE = CE_request;
if (bmi_response) {
/*
* CE_response = dma_map_single(dev, bmi_response,
* BMI_DATASZ_MAX, DMA_FROM_DEVICE);
*/
CE_response = scn->bmi_rsp_da;
transaction->bmi_response_host = bmi_response;
transaction->bmi_response_CE = CE_response;
/* dma_cache_sync(dev, bmi_response,
BMI_DATASZ_MAX, DMA_FROM_DEVICE); */
cdf_os_mem_dma_sync_single_for_device(scn->cdf_dev,
CE_response,
BMI_DATASZ_MAX,
DMA_FROM_DEVICE);
ce_recv_buf_enqueue(ce_recv, transaction,
transaction->bmi_response_CE);
/* NB: see HIF_BMI_recv_done */
} else {
transaction->bmi_response_host = NULL;
transaction->bmi_response_CE = 0;
}
/* dma_cache_sync(dev, bmi_request, request_length, DMA_TO_DEVICE); */
cdf_os_mem_dma_sync_single_for_device(scn->cdf_dev, CE_request,
request_length, DMA_TO_DEVICE);
status =
ce_send(ce_send_hdl, transaction,
CE_request, request_length,
transaction_id, 0, user_flags);
ASSERT(status == CDF_STATUS_SUCCESS);
/* NB: see hif_bmi_send_done */
/* TBDXXX: handle timeout */
/* Wait for BMI request/response transaction to complete */
/* Always just wait for BMI request here if
* BMI_RSP_POLLING is defined */
while (cdf_semaphore_acquire
(scn->cdf_dev, &transaction->bmi_transaction_sem)) {
/*need some break out condition(time out?) */
}
if (bmi_response) {
#ifdef BMI_RSP_POLLING
/* Fix EV118783, do not wait a semaphore for the BMI response
* since the relative interruption may be lost.
* poll the BMI response instead.
*/
i = 0;
while (ce_completed_recv_next(
ce_recv, NULL, NULL, &buf,
&completed_nbytes, &id,
&flags) != CDF_STATUS_SUCCESS) {
if (i++ > BMI_RSP_TO_MILLISEC) {
HIF_ERROR("%s:error, can't get bmi response\n",
__func__);
status = CDF_STATUS_E_BUSY;
break;
}
OS_DELAY(1000);
}
if ((status == CDF_STATUS_SUCCESS) && bmi_response_lengthp)
*bmi_response_lengthp = completed_nbytes;
#else
if ((status == CDF_STATUS_SUCCESS) && bmi_response_lengthp) {
*bmi_response_lengthp =
transaction->bmi_response_length;
}
#endif
}
/* dma_unmap_single(dev, transaction->bmi_request_CE,
request_length, DMA_TO_DEVICE); */
/* bus_unmap_single(scn->sc_osdev,
transaction->bmi_request_CE,
request_length, BUS_DMA_TODEVICE); */
if (status != CDF_STATUS_SUCCESS) {
cdf_dma_addr_t unused_buffer;
unsigned int unused_nbytes;
unsigned int unused_id;
unsigned int toeplitz_hash_result;
ce_cancel_send_next(ce_send_hdl,
NULL, NULL, &unused_buffer,
&unused_nbytes, &unused_id,
&toeplitz_hash_result);
}
A_TARGET_ACCESS_UNLIKELY(scn);
cdf_mem_free(transaction);
return status;
}