4164 lines
127 KiB
C
4164 lines
127 KiB
C
/*****************************************************************************
|
|
|
|
(c) Cambridge Silicon Radio Limited 2012
|
|
All rights reserved and confidential information of CSR
|
|
|
|
Refer to LICENSE.txt included with this source for details
|
|
on the license terms.
|
|
|
|
*****************************************************************************/
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* FILE: csr_wifi_hip_card_sdio.c
|
|
*
|
|
* PURPOSE: Implementation of the Card API for SDIO.
|
|
*
|
|
* NOTES:
|
|
* CardInit() is called from the SDIO probe callback when a card is
|
|
* inserted. This performs the basic SDIO initialisation, enabling i/o
|
|
* etc.
|
|
*
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
#include <linux/slab.h>
|
|
#include "csr_wifi_hip_unifi.h"
|
|
#include "csr_wifi_hip_conversions.h"
|
|
#include "csr_wifi_hip_unifiversion.h"
|
|
#include "csr_wifi_hip_card.h"
|
|
#include "csr_wifi_hip_card_sdio.h"
|
|
#include "csr_wifi_hip_chiphelper.h"
|
|
|
|
|
|
/* Time to wait between attempts to read MAILBOX0 */
|
|
#define MAILBOX1_TIMEOUT 10 /* in millisecs */
|
|
#define MAILBOX1_ATTEMPTS 200 /* 2 seconds */
|
|
|
|
#define MAILBOX2_TIMEOUT 5 /* in millisecs */
|
|
#define MAILBOX2_ATTEMPTS 10 /* 50ms */
|
|
|
|
#define RESET_SETTLE_DELAY 25 /* in millisecs */
|
|
|
|
static CsrResult card_init_slots(card_t *card);
|
|
static CsrResult card_hw_init(card_t *card);
|
|
static CsrResult firmware_present_in_flash(card_t *card);
|
|
static void bootstrap_chip_hw(card_t *card);
|
|
static CsrResult unifi_reset_hardware(card_t *card);
|
|
static CsrResult unifi_hip_init(card_t *card);
|
|
static CsrResult card_access_panic(card_t *card);
|
|
static CsrResult unifi_read_chip_version(card_t *card);
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* unifi_alloc_card
|
|
*
|
|
* Allocate and initialise the card context structure.
|
|
*
|
|
* Arguments:
|
|
* sdio Pointer to SDIO context pointer to pass to low
|
|
* level i/o functions.
|
|
* ospriv Pointer to O/S private struct to pass when calling
|
|
* callbacks to the higher level system.
|
|
*
|
|
* Returns:
|
|
* Pointer to card struct, which represents the driver context or
|
|
* NULL if the allocation failed.
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
card_t* unifi_alloc_card(CsrSdioFunction *sdio, void *ospriv)
|
|
{
|
|
card_t *card;
|
|
u32 i;
|
|
|
|
func_enter();
|
|
|
|
|
|
card = kzalloc(sizeof(card_t), GFP_KERNEL);
|
|
if (card == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
card->sdio_if = sdio;
|
|
card->ospriv = ospriv;
|
|
|
|
card->unifi_interrupt_seq = 1;
|
|
|
|
/* Make these invalid. */
|
|
card->proc_select = (u32)(-1);
|
|
card->dmem_page = (u32)(-1);
|
|
card->pmem_page = (u32)(-1);
|
|
|
|
card->bh_reason_host = 0;
|
|
card->bh_reason_unifi = 0;
|
|
|
|
for (i = 0; i < sizeof(card->tx_q_paused_flag) / sizeof(card->tx_q_paused_flag[0]); i++)
|
|
{
|
|
card->tx_q_paused_flag[i] = 0;
|
|
}
|
|
card->memory_resources_allocated = 0;
|
|
|
|
card->low_power_mode = UNIFI_LOW_POWER_DISABLED;
|
|
card->periodic_wake_mode = UNIFI_PERIODIC_WAKE_HOST_DISABLED;
|
|
|
|
card->host_state = UNIFI_HOST_STATE_AWAKE;
|
|
card->intmode = CSR_WIFI_INTMODE_DEFAULT;
|
|
|
|
/*
|
|
* Memory resources for buffers are allocated when the chip is initialised
|
|
* because we need configuration information from the firmware.
|
|
*/
|
|
|
|
/*
|
|
* Initialise wait queues and lists
|
|
*/
|
|
card->fh_command_queue.q_body = card->fh_command_q_body;
|
|
card->fh_command_queue.q_length = UNIFI_SOFT_COMMAND_Q_LENGTH;
|
|
|
|
for (i = 0; i < UNIFI_NO_OF_TX_QS; i++)
|
|
{
|
|
card->fh_traffic_queue[i].q_body = card->fh_traffic_q_body[i];
|
|
card->fh_traffic_queue[i].q_length = UNIFI_SOFT_TRAFFIC_Q_LENGTH;
|
|
}
|
|
|
|
|
|
/* Initialise mini-coredump pointers in case no coredump buffers
|
|
* are requested by the OS layer.
|
|
*/
|
|
card->request_coredump_on_reset = 0;
|
|
card->dump_next_write = NULL;
|
|
card->dump_cur_read = NULL;
|
|
card->dump_buf = NULL;
|
|
|
|
#ifdef UNIFI_DEBUG
|
|
/* Determine offset of LSB in pointer for later alignment sanity check.
|
|
* Synergy integer types have specific widths, which cause compiler
|
|
* warnings when casting pointer types, e.g. on 64-bit systems.
|
|
*/
|
|
{
|
|
u32 val = 0x01234567;
|
|
|
|
if (*((u8 *)&val) == 0x01)
|
|
{
|
|
card->lsb = sizeof(void *) - 1; /* BE */
|
|
}
|
|
else
|
|
{
|
|
card->lsb = 0; /* LE */
|
|
}
|
|
}
|
|
#endif
|
|
func_exit();
|
|
return card;
|
|
} /* unifi_alloc_card() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* unifi_init_card
|
|
*
|
|
* Reset the hardware and perform HIP initialization
|
|
*
|
|
* Arguments:
|
|
* card Pointer to card struct
|
|
*
|
|
* Returns:
|
|
* CsrResult code
|
|
* CSR_RESULT_SUCCESS if successful
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
CsrResult unifi_init_card(card_t *card, s32 led_mask)
|
|
{
|
|
CsrResult r;
|
|
|
|
func_enter();
|
|
|
|
if (card == NULL)
|
|
{
|
|
func_exit_r(CSR_WIFI_HIP_RESULT_INVALID_VALUE);
|
|
return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
|
|
}
|
|
|
|
r = unifi_init(card);
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
|
|
r = unifi_hip_init(card);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to start host protocol.\n");
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
|
|
func_exit();
|
|
return CSR_RESULT_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* unifi_init
|
|
*
|
|
* Init the hardware.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to card struct
|
|
*
|
|
* Returns:
|
|
* CsrResult code
|
|
* CSR_RESULT_SUCCESS if successful
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
CsrResult unifi_init(card_t *card)
|
|
{
|
|
CsrResult r;
|
|
CsrResult csrResult;
|
|
|
|
func_enter();
|
|
|
|
if (card == NULL)
|
|
{
|
|
func_exit_r(CSR_WIFI_HIP_RESULT_INVALID_VALUE);
|
|
return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
|
|
}
|
|
|
|
/*
|
|
* Disable the SDIO interrupts while initialising UniFi.
|
|
* Re-enable them when f/w is running.
|
|
*/
|
|
csrResult = CsrSdioInterruptDisable(card->sdio_if);
|
|
if (csrResult == CSR_SDIO_RESULT_NO_DEVICE)
|
|
{
|
|
return CSR_WIFI_HIP_RESULT_NO_DEVICE;
|
|
}
|
|
|
|
/*
|
|
* UniFi's PLL may start with a slow clock (~ 1 MHz) so initially
|
|
* set the SDIO bus clock to a similar value or SDIO accesses may
|
|
* fail.
|
|
*/
|
|
csrResult = CsrSdioMaxBusClockFrequencySet(card->sdio_if, UNIFI_SDIO_CLOCK_SAFE_HZ);
|
|
if (csrResult != CSR_RESULT_SUCCESS)
|
|
{
|
|
r = ConvertCsrSdioToCsrHipResult(card, csrResult);
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
card->sdio_clock_speed = UNIFI_SDIO_CLOCK_SAFE_HZ;
|
|
|
|
/*
|
|
* Reset UniFi. Note, this only resets the WLAN function part of the chip,
|
|
* the SDIO interface is not reset.
|
|
*/
|
|
unifi_trace(card->ospriv, UDBG1, "Resetting UniFi\n");
|
|
r = unifi_reset_hardware(card);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to reset UniFi\n");
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
|
|
/* Reset the power save mode, to be active until the MLME-reset is complete */
|
|
r = unifi_configure_low_power_mode(card,
|
|
UNIFI_LOW_POWER_DISABLED, UNIFI_PERIODIC_WAKE_HOST_DISABLED);
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to set power save mode\n");
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* Set initial value of page registers.
|
|
* The page registers will be maintained by unifi_read...() and
|
|
* unifi_write...().
|
|
*/
|
|
card->proc_select = (u32)(-1);
|
|
card->dmem_page = (u32)(-1);
|
|
card->pmem_page = (u32)(-1);
|
|
r = unifi_write_direct16(card, ChipHelper_HOST_WINDOW3_PAGE(card->helper) * 2, 0);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to write SHARED_DMEM_PAGE\n");
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
r = unifi_write_direct16(card, ChipHelper_HOST_WINDOW2_PAGE(card->helper) * 2, 0);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to write PROG_MEM2_PAGE\n");
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* If the driver has reset UniFi due to previous SDIO failure, this may
|
|
* have been due to a chip watchdog reset. In this case, the driver may
|
|
* have requested a mini-coredump which needs to be captured now the
|
|
* SDIO interface is alive.
|
|
*/
|
|
(void)unifi_coredump_handle_request(card);
|
|
|
|
/*
|
|
* Probe to see if the UniFi has ROM/flash to boot from. CSR6xxx should do.
|
|
*/
|
|
r = firmware_present_in_flash(card);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r == CSR_WIFI_HIP_RESULT_NOT_FOUND)
|
|
{
|
|
unifi_error(card->ospriv, "No firmware found\n");
|
|
}
|
|
else if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Probe for Flash failed\n");
|
|
}
|
|
|
|
func_exit_r(r);
|
|
return r;
|
|
} /* unifi_init() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* unifi_download
|
|
*
|
|
* Load the firmware.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to card struct
|
|
* led_mask Loader LED mask
|
|
*
|
|
* Returns:
|
|
* CSR_RESULT_SUCCESS on success
|
|
* CsrResult error code on failure.
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
CsrResult unifi_download(card_t *card, s32 led_mask)
|
|
{
|
|
CsrResult r;
|
|
void *dlpriv;
|
|
|
|
func_enter();
|
|
|
|
if (card == NULL)
|
|
{
|
|
func_exit_r(CSR_WIFI_HIP_RESULT_INVALID_VALUE);
|
|
return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
|
|
}
|
|
|
|
/* Set the loader led mask */
|
|
card->loader_led_mask = led_mask;
|
|
|
|
/* Get the firmware file information */
|
|
unifi_trace(card->ospriv, UDBG1, "downloading firmware...\n");
|
|
|
|
dlpriv = unifi_dl_fw_read_start(card, UNIFI_FW_STA);
|
|
if (dlpriv == NULL)
|
|
{
|
|
func_exit_r(CSR_WIFI_HIP_RESULT_NOT_FOUND);
|
|
return CSR_WIFI_HIP_RESULT_NOT_FOUND;
|
|
}
|
|
|
|
/* Download the firmware. */
|
|
r = unifi_dl_firmware(card, dlpriv);
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to download firmware\n");
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
|
|
/* Free the firmware file information. */
|
|
unifi_fw_read_stop(card->ospriv, dlpriv);
|
|
|
|
func_exit();
|
|
|
|
return CSR_RESULT_SUCCESS;
|
|
} /* unifi_download() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* unifi_hip_init
|
|
*
|
|
* This function performs the f/w initialisation sequence as described
|
|
* in the Unifi Host Interface Protocol Specification.
|
|
* It allocates memory for host-side slot data and signal queues.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to card struct
|
|
*
|
|
* Returns:
|
|
* CSR_RESULT_SUCCESS on success or else a CSR error code
|
|
*
|
|
* Notes:
|
|
* The firmware must have been downloaded.
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
static CsrResult unifi_hip_init(card_t *card)
|
|
{
|
|
CsrResult r;
|
|
CsrResult csrResult;
|
|
|
|
func_enter();
|
|
|
|
r = card_hw_init(card);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to establish communication with UniFi\n");
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
#ifdef CSR_PRE_ALLOC_NET_DATA
|
|
/* if there is any preallocated netdata left from the prev session free it now */
|
|
prealloc_netdata_free(card);
|
|
#endif
|
|
/*
|
|
* Allocate memory for host-side slot data and signal queues.
|
|
* We need the config info read from the firmware to know how much
|
|
* memory to allocate.
|
|
*/
|
|
r = card_init_slots(card);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Init slots failed: %d\n", r);
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
|
|
unifi_trace(card->ospriv, UDBG2, "Sending first UniFi interrupt\n");
|
|
|
|
r = unifi_set_host_state(card, UNIFI_HOST_STATE_AWAKE);
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
|
|
/* Enable the SDIO interrupts now that the f/w is running. */
|
|
csrResult = CsrSdioInterruptEnable(card->sdio_if);
|
|
if (csrResult == CSR_SDIO_RESULT_NO_DEVICE)
|
|
{
|
|
return CSR_WIFI_HIP_RESULT_NO_DEVICE;
|
|
}
|
|
|
|
/* Signal the UniFi to start handling messages */
|
|
r = CardGenInt(card);
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
|
|
func_exit();
|
|
|
|
return CSR_RESULT_SUCCESS;
|
|
} /* unifi_hip_init() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* _build_sdio_config_data
|
|
*
|
|
* Unpack the SDIO configuration information from a buffer read from
|
|
* UniFi into a host structure.
|
|
* The data is byte-swapped for a big-endian host if necessary by the
|
|
* UNPACK... macros.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to card struct
|
|
* cfg_data Destination structure to unpack into.
|
|
* cfg_data_buf Source buffer to read from. This should be the raw
|
|
* data read from UniFi.
|
|
*
|
|
* Returns:
|
|
* None.
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
static void _build_sdio_config_data(sdio_config_data_t *cfg_data,
|
|
const u8 *cfg_data_buf)
|
|
{
|
|
s16 offset = 0;
|
|
|
|
cfg_data->version = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(cfg_data_buf + offset);
|
|
offset += SIZEOF_UINT16;
|
|
|
|
cfg_data->sdio_ctrl_offset = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(cfg_data_buf + offset);
|
|
offset += SIZEOF_UINT16;
|
|
|
|
cfg_data->fromhost_sigbuf_handle = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(cfg_data_buf + offset);
|
|
offset += SIZEOF_UINT16;
|
|
|
|
cfg_data->tohost_sigbuf_handle = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(cfg_data_buf + offset);
|
|
offset += SIZEOF_UINT16;
|
|
|
|
cfg_data->num_fromhost_sig_frags = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(cfg_data_buf + offset);
|
|
offset += SIZEOF_UINT16;
|
|
|
|
cfg_data->num_tohost_sig_frags = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(cfg_data_buf + offset);
|
|
offset += SIZEOF_UINT16;
|
|
|
|
cfg_data->num_fromhost_data_slots = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(cfg_data_buf + offset);
|
|
offset += SIZEOF_UINT16;
|
|
|
|
cfg_data->num_tohost_data_slots = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(cfg_data_buf + offset);
|
|
offset += SIZEOF_UINT16;
|
|
|
|
cfg_data->data_slot_size = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(cfg_data_buf + offset);
|
|
offset += SIZEOF_UINT16;
|
|
|
|
cfg_data->initialised = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(cfg_data_buf + offset);
|
|
offset += SIZEOF_UINT16;
|
|
|
|
cfg_data->overlay_size = CSR_GET_UINT32_FROM_LITTLE_ENDIAN(cfg_data_buf + offset);
|
|
offset += SIZEOF_UINT32;
|
|
|
|
cfg_data->data_slot_round = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(cfg_data_buf + offset);
|
|
offset += SIZEOF_UINT16;
|
|
|
|
cfg_data->sig_frag_size = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(cfg_data_buf + offset);
|
|
offset += SIZEOF_UINT16;
|
|
|
|
cfg_data->tohost_signal_padding = CSR_GET_UINT16_FROM_LITTLE_ENDIAN(cfg_data_buf + offset);
|
|
} /* _build_sdio_config_data() */
|
|
|
|
|
|
/*
|
|
* - Function ----------------------------------------------------------------
|
|
* card_hw_init()
|
|
*
|
|
* Perform the initialisation procedure described in the UniFi Host
|
|
* Interface Protocol document (section 3.3.8) and read the run-time
|
|
* configuration information from the UniFi. This is stuff like number
|
|
* of bulk data slots etc.
|
|
*
|
|
* The card enumeration and SD initialisation has already been done by
|
|
* the SDIO library, see card_sdio_init().
|
|
*
|
|
* The initialisation is done when firmware is ready, i.e. this may need
|
|
* to be called after a f/w download operation.
|
|
*
|
|
* The initialisation procedure goes like this:
|
|
* - Wait for UniFi to start-up by polling SHARED_MAILBOX1
|
|
* - Find the symbol table and look up SLT_SDIO_SLOT_CONFIG
|
|
* - Read the config structure
|
|
* - Check the "SDIO initialised" flag, if not zero do a h/w reset and
|
|
* start again
|
|
* - Decide the number of bulk data slots to allocate, allocate them and
|
|
* set "SDIO initialised" flag (and generate an interrupt) to say so.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to card struct
|
|
*
|
|
* Returns:
|
|
* CSR_RESULT_SUCEESS on success,
|
|
* a CSR error code on failure
|
|
*
|
|
* Notes:
|
|
* All data in the f/w is stored in a little endian format, without any
|
|
* padding bytes. Every read from this memory has to be transformed in
|
|
* host (cpu specific) format, before it is stored in driver's parameters
|
|
* or/and structures. Athough unifi_card_read16() and unifi_read32() do perform
|
|
* the convertion internally, unifi_readn() does not.
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
static CsrResult card_hw_init(card_t *card)
|
|
{
|
|
u32 slut_address;
|
|
u16 initialised;
|
|
u16 finger_print;
|
|
symbol_t slut;
|
|
sdio_config_data_t *cfg_data;
|
|
u8 cfg_data_buf[SDIO_CONFIG_DATA_SIZE];
|
|
CsrResult r;
|
|
void *dlpriv;
|
|
s16 major, minor;
|
|
s16 search_4slut_again;
|
|
CsrResult csrResult;
|
|
|
|
func_enter();
|
|
|
|
/*
|
|
* The device revision from the TPLMID_MANF and TPLMID_CARD fields
|
|
* of the CIS are available as
|
|
* card->sdio_if->pDevice->ManfID
|
|
* card->sdio_if->pDevice->AppID
|
|
*/
|
|
|
|
/*
|
|
* Run in a loop so we can patch.
|
|
*/
|
|
do
|
|
{
|
|
/* Reset these each time around the loop. */
|
|
search_4slut_again = 0;
|
|
cfg_data = NULL;
|
|
|
|
r = card_wait_for_firmware_to_start(card, &slut_address);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Firmware hasn't started\n");
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
unifi_trace(card->ospriv, UDBG4, "SLUT addr 0x%lX\n", slut_address);
|
|
|
|
/*
|
|
* Firmware has started, but doesn't know full clock configuration yet
|
|
* as some of the information may be in the MIB. Therefore we set an
|
|
* initial SDIO clock speed, faster than UNIFI_SDIO_CLOCK_SAFE_HZ, for
|
|
* the patch download and subsequent firmware initialisation, and
|
|
* full speed UNIFI_SDIO_CLOCK_MAX_HZ will be set once the f/w tells us
|
|
* that it is ready.
|
|
*/
|
|
csrResult = CsrSdioMaxBusClockFrequencySet(card->sdio_if, UNIFI_SDIO_CLOCK_INIT_HZ);
|
|
if (csrResult != CSR_RESULT_SUCCESS)
|
|
{
|
|
r = ConvertCsrSdioToCsrHipResult(card, csrResult);
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
card->sdio_clock_speed = UNIFI_SDIO_CLOCK_INIT_HZ;
|
|
|
|
/*
|
|
* Check the SLUT fingerprint.
|
|
* The slut_address is a generic pointer so we must use unifi_card_read16().
|
|
*/
|
|
unifi_trace(card->ospriv, UDBG4, "Looking for SLUT finger print\n");
|
|
finger_print = 0;
|
|
r = unifi_card_read16(card, slut_address, &finger_print);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to read SLUT finger print\n");
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
|
|
if (finger_print != SLUT_FINGERPRINT)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to find Symbol lookup table fingerprint\n");
|
|
func_exit_r(CSR_RESULT_FAILURE);
|
|
return CSR_RESULT_FAILURE;
|
|
}
|
|
|
|
/* Symbol table starts imedately after the fingerprint */
|
|
slut_address += 2;
|
|
|
|
/* Search the table until either the end marker is found, or the
|
|
* loading of patch firmware invalidates the current table.
|
|
*/
|
|
while (!search_4slut_again)
|
|
{
|
|
u16 s;
|
|
u32 l;
|
|
|
|
r = unifi_card_read16(card, slut_address, &s);
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
slut_address += 2;
|
|
|
|
if (s == CSR_SLT_END)
|
|
{
|
|
unifi_trace(card->ospriv, UDBG3, " found CSR_SLT_END\n");
|
|
break;
|
|
}
|
|
|
|
r = unifi_read32(card, slut_address, &l);
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
slut_address += 4;
|
|
|
|
slut.id = s;
|
|
slut.obj = l;
|
|
|
|
unifi_trace(card->ospriv, UDBG3, " found SLUT id %02d.%08lx\n", slut.id, slut.obj);
|
|
switch (slut.id)
|
|
{
|
|
case CSR_SLT_SDIO_SLOT_CONFIG:
|
|
cfg_data = &card->config_data;
|
|
/*
|
|
* unifi_card_readn reads n bytes from the card, where data is stored
|
|
* in a little endian format, without any padding bytes. So, we
|
|
* can not just pass the cfg_data pointer or use the
|
|
* sizeof(sdio_config_data_t) since the structure in the host can
|
|
* be big endian formatted or have padding bytes for alignment.
|
|
* We use a char buffer to read the data from the card.
|
|
*/
|
|
r = unifi_card_readn(card, slut.obj, cfg_data_buf, SDIO_CONFIG_DATA_SIZE);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to read config data\n");
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
/* .. and then we copy the data to the host structure */
|
|
_build_sdio_config_data(cfg_data, cfg_data_buf);
|
|
|
|
/* Make sure the from host data slots are what we expect
|
|
we reserve 2 for commands and there should be at least
|
|
1 left for each access category */
|
|
if ((cfg_data->num_fromhost_data_slots < UNIFI_RESERVED_COMMAND_SLOTS)
|
|
|| (cfg_data->num_fromhost_data_slots - UNIFI_RESERVED_COMMAND_SLOTS) / UNIFI_NO_OF_TX_QS == 0)
|
|
{
|
|
unifi_error(card->ospriv, "From host data slots %d\n", cfg_data->num_fromhost_data_slots);
|
|
unifi_error(card->ospriv, "need to be (queues * x + 2) (UNIFI_RESERVED_COMMAND_SLOTS for commands)\n");
|
|
func_exit_r(CSR_RESULT_FAILURE);
|
|
return CSR_RESULT_FAILURE;
|
|
}
|
|
|
|
/* Configure SDIO to-block-size padding */
|
|
if (card->sdio_io_block_pad)
|
|
{
|
|
/*
|
|
* Firmware limits the maximum padding size via data_slot_round.
|
|
* Therefore when padding to whole block sizes, the block size
|
|
* must be configured correctly by adjusting CSR_WIFI_HIP_SDIO_BLOCK_SIZE.
|
|
*/
|
|
if (cfg_data->data_slot_round < card->sdio_io_block_size)
|
|
{
|
|
unifi_error(card->ospriv,
|
|
"Configuration error: Block size of %d exceeds f/w data_slot_round of %d\n",
|
|
card->sdio_io_block_size, cfg_data->data_slot_round);
|
|
return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
|
|
}
|
|
|
|
/*
|
|
* To force the To-Host signals to be rounded up to the SDIO block
|
|
* size, we need to write the To-Host Signal Padding Fragments
|
|
* field of the SDIO configuration in UniFi.
|
|
*/
|
|
if ((card->sdio_io_block_size % cfg_data->sig_frag_size) != 0)
|
|
{
|
|
unifi_error(card->ospriv, "Configuration error: Can not pad to-host signals.\n");
|
|
func_exit_r(CSR_WIFI_HIP_RESULT_INVALID_VALUE);
|
|
return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
|
|
}
|
|
cfg_data->tohost_signal_padding = (u16) (card->sdio_io_block_size / cfg_data->sig_frag_size);
|
|
unifi_info(card->ospriv, "SDIO block size %d requires %d padding chunks\n",
|
|
card->sdio_io_block_size, cfg_data->tohost_signal_padding);
|
|
r = unifi_card_write16(card, slut.obj + SDIO_TO_HOST_SIG_PADDING_OFFSET, cfg_data->tohost_signal_padding);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to write To-Host Signal Padding Fragments\n");
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
}
|
|
|
|
/* Reconstruct the Generic Pointer address of the
|
|
* SDIO Control Data Struct.
|
|
*/
|
|
card->sdio_ctrl_addr = cfg_data->sdio_ctrl_offset | (UNIFI_SH_DMEM << 24);
|
|
card->init_flag_addr = slut.obj + SDIO_INIT_FLAG_OFFSET;
|
|
break;
|
|
|
|
case CSR_SLT_BUILD_ID_NUMBER:
|
|
{
|
|
u32 n;
|
|
r = unifi_read32(card, slut.obj, &n);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to read build id\n");
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
card->build_id = n;
|
|
}
|
|
break;
|
|
|
|
case CSR_SLT_BUILD_ID_STRING:
|
|
r = unifi_readnz(card, slut.obj, card->build_id_string,
|
|
sizeof(card->build_id_string));
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to read build string\n");
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
break;
|
|
|
|
case CSR_SLT_PERSISTENT_STORE_DB:
|
|
break;
|
|
|
|
case CSR_SLT_BOOT_LOADER_CONTROL:
|
|
|
|
/* This command copies most of the station firmware
|
|
* image from ROM into program RAM. It also clears
|
|
* out the zerod data and sets up the initialised
|
|
* data. */
|
|
r = unifi_do_loader_op(card, slut.obj + 6, UNIFI_BOOT_LOADER_LOAD_STA);
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to write loader load image command\n");
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
|
|
dlpriv = unifi_dl_fw_read_start(card, UNIFI_FW_STA);
|
|
|
|
/* dlpriv might be NULL, we still need to do the do_loader_op step. */
|
|
if (dlpriv != NULL)
|
|
{
|
|
/* Download the firmware. */
|
|
r = unifi_dl_patch(card, dlpriv, slut.obj);
|
|
|
|
/* Free the firmware file information. */
|
|
unifi_fw_read_stop(card->ospriv, dlpriv);
|
|
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to patch firmware\n");
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
}
|
|
|
|
/* This command starts the firmware image that we want (the
|
|
* station by default) with any patches required applied. */
|
|
r = unifi_do_loader_op(card, slut.obj + 6, UNIFI_BOOT_LOADER_RESTART);
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to write loader restart command\n");
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
|
|
/* The now running patch f/w defines a new SLUT data structure -
|
|
* the current one is no longer valid. We must drop out of the
|
|
* processing loop and enumerate the new SLUT (which may appear
|
|
* at a different offset).
|
|
*/
|
|
search_4slut_again = 1;
|
|
break;
|
|
|
|
case CSR_SLT_PANIC_DATA_PHY:
|
|
card->panic_data_phy_addr = slut.obj;
|
|
break;
|
|
|
|
case CSR_SLT_PANIC_DATA_MAC:
|
|
card->panic_data_mac_addr = slut.obj;
|
|
break;
|
|
|
|
default:
|
|
/* do nothing */
|
|
break;
|
|
}
|
|
} /* while */
|
|
} while (search_4slut_again);
|
|
|
|
/* Did we find the Config Data ? */
|
|
if (cfg_data == NULL)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to find SDIO_SLOT_CONFIG Symbol\n");
|
|
func_exit_r(CSR_RESULT_FAILURE);
|
|
return CSR_RESULT_FAILURE;
|
|
}
|
|
|
|
/*
|
|
* Has ths card already been initialised?
|
|
* If so, return an error so we do a h/w reset and start again.
|
|
*/
|
|
r = unifi_card_read16(card, card->init_flag_addr, &initialised);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to read init flag at %08lx\n",
|
|
card->init_flag_addr);
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
if (initialised != 0)
|
|
{
|
|
func_exit_r(CSR_RESULT_FAILURE);
|
|
return CSR_RESULT_FAILURE;
|
|
}
|
|
|
|
|
|
/*
|
|
* Now check the UniFi firmware version
|
|
*/
|
|
major = (cfg_data->version >> 8) & 0xFF;
|
|
minor = cfg_data->version & 0xFF;
|
|
unifi_info(card->ospriv, "UniFi f/w protocol version %d.%d (driver %d.%d)\n",
|
|
major, minor,
|
|
UNIFI_HIP_MAJOR_VERSION, UNIFI_HIP_MINOR_VERSION);
|
|
|
|
unifi_info(card->ospriv, "Firmware build %u: %s\n",
|
|
card->build_id, card->build_id_string);
|
|
|
|
if (major != UNIFI_HIP_MAJOR_VERSION)
|
|
{
|
|
unifi_error(card->ospriv, "UniFi f/w protocol major version (%d) is different from driver (v%d.%d)\n",
|
|
major, UNIFI_HIP_MAJOR_VERSION, UNIFI_HIP_MINOR_VERSION);
|
|
#ifndef CSR_WIFI_DISABLE_HIP_VERSION_CHECK
|
|
func_exit_r(CSR_RESULT_FAILURE);
|
|
return CSR_RESULT_FAILURE;
|
|
#endif
|
|
}
|
|
if (minor < UNIFI_HIP_MINOR_VERSION)
|
|
{
|
|
unifi_error(card->ospriv, "UniFi f/w protocol version (v%d.%d) is older than minimum required by driver (v%d.%d).\n",
|
|
major, minor,
|
|
UNIFI_HIP_MAJOR_VERSION, UNIFI_HIP_MINOR_VERSION);
|
|
#ifndef CSR_WIFI_DISABLE_HIP_VERSION_CHECK
|
|
func_exit_r(CSR_RESULT_FAILURE);
|
|
return CSR_RESULT_FAILURE;
|
|
#endif
|
|
}
|
|
|
|
/* Read panic codes from a previous firmware panic. If the firmware has
|
|
* not panicked since power was applied (e.g. power-off hard reset)
|
|
* the stored panic codes will not be updated.
|
|
*/
|
|
unifi_read_panic(card);
|
|
|
|
func_exit();
|
|
return CSR_RESULT_SUCCESS;
|
|
} /* card_hw_init() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* card_wait_for_unifi_to_reset
|
|
*
|
|
* Waits for a reset to complete by polling the WLAN function enable
|
|
* bit (which is cleared on reset).
|
|
*
|
|
* Arguments:
|
|
* card Pointer to card struct
|
|
*
|
|
* Returns:
|
|
* CSR_RESULT_SUCCESS on success, CSR error code on failure.
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
static CsrResult card_wait_for_unifi_to_reset(card_t *card)
|
|
{
|
|
s16 i;
|
|
CsrResult r;
|
|
u8 io_enable;
|
|
CsrResult csrResult;
|
|
|
|
func_enter();
|
|
|
|
r = CSR_RESULT_SUCCESS;
|
|
for (i = 0; i < MAILBOX2_ATTEMPTS; i++)
|
|
{
|
|
unifi_trace(card->ospriv, UDBG1, "waiting for reset to complete, attempt %d\n", i);
|
|
if (card->chip_id > SDIO_CARD_ID_UNIFI_2)
|
|
{
|
|
/* It's quite likely that this read will timeout for the
|
|
* first few tries - especially if we have reset via
|
|
* DBG_RESET.
|
|
*/
|
|
#if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_SDIO_TRACE)
|
|
unifi_debug_log_to_buf("m0@%02X=", SDIO_IO_READY);
|
|
#endif
|
|
csrResult = CsrSdioF0Read8(card->sdio_if, SDIO_IO_READY, &io_enable);
|
|
#if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_SDIO_TRACE)
|
|
if (csrResult != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_debug_log_to_buf("error=%X\n", csrResult);
|
|
}
|
|
else
|
|
{
|
|
unifi_debug_log_to_buf("%X\n", io_enable);
|
|
}
|
|
#endif
|
|
if (csrResult == CSR_SDIO_RESULT_NO_DEVICE)
|
|
{
|
|
return CSR_WIFI_HIP_RESULT_NO_DEVICE;
|
|
}
|
|
r = CSR_RESULT_SUCCESS;
|
|
if (csrResult != CSR_RESULT_SUCCESS)
|
|
{
|
|
r = ConvertCsrSdioToCsrHipResult(card, csrResult);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
r = sdio_read_f0(card, SDIO_IO_ENABLE, &io_enable);
|
|
}
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r == CSR_RESULT_SUCCESS)
|
|
{
|
|
u16 mbox2;
|
|
s16 enabled = io_enable & (1 << card->function);
|
|
|
|
if (!enabled)
|
|
{
|
|
unifi_trace(card->ospriv, UDBG1,
|
|
"Reset complete (function %d is disabled) in ~ %u msecs\n",
|
|
card->function, i * MAILBOX2_TIMEOUT);
|
|
|
|
/* Enable WLAN function and verify MAILBOX2 is zero'd */
|
|
csrResult = CsrSdioFunctionEnable(card->sdio_if);
|
|
if (csrResult != CSR_RESULT_SUCCESS)
|
|
{
|
|
r = ConvertCsrSdioToCsrHipResult(card, csrResult);
|
|
unifi_error(card->ospriv, "CsrSdioFunctionEnable failed %d\n", r);
|
|
break;
|
|
}
|
|
}
|
|
|
|
r = unifi_read_direct16(card, ChipHelper_SDIO_HIP_HANDSHAKE(card->helper) * 2, &mbox2);
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "read HIP_HANDSHAKE failed %d\n", r);
|
|
break;
|
|
}
|
|
if (mbox2 != 0)
|
|
{
|
|
unifi_error(card->ospriv, "MAILBOX2 non-zero after reset (mbox2 = %04x)\n", mbox2);
|
|
r = CSR_RESULT_FAILURE;
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (card->chip_id > SDIO_CARD_ID_UNIFI_2)
|
|
{
|
|
/* We ignore read failures for the first few reads,
|
|
* they are probably benign. */
|
|
if (i > MAILBOX2_ATTEMPTS / 4)
|
|
{
|
|
unifi_trace(card->ospriv, UDBG1, "Failed to read CCCR IO Ready register while polling for reset\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
unifi_trace(card->ospriv, UDBG1, "Failed to read CCCR IO Enable register while polling for reset\n");
|
|
}
|
|
}
|
|
CsrThreadSleep(MAILBOX2_TIMEOUT);
|
|
}
|
|
|
|
if (r == CSR_RESULT_SUCCESS && i == MAILBOX2_ATTEMPTS)
|
|
{
|
|
unifi_trace(card->ospriv, UDBG1, "Timeout waiting for UniFi to complete reset\n");
|
|
r = CSR_RESULT_FAILURE;
|
|
}
|
|
|
|
func_exit();
|
|
return r;
|
|
} /* card_wait_for_unifi_to_reset() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* card_wait_for_unifi_to_disable
|
|
*
|
|
* Waits for the function to become disabled by polling the
|
|
* IO_READY bit.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to card struct
|
|
*
|
|
* Returns:
|
|
* CSR_RESULT_SUCCESS on success, CSR error code on failure.
|
|
*
|
|
* Notes: This function can only be used with
|
|
* card->chip_id > SDIO_CARD_ID_UNIFI_2
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
static CsrResult card_wait_for_unifi_to_disable(card_t *card)
|
|
{
|
|
s16 i;
|
|
CsrResult r;
|
|
u8 io_enable;
|
|
CsrResult csrResult;
|
|
|
|
func_enter();
|
|
|
|
if (card->chip_id <= SDIO_CARD_ID_UNIFI_2)
|
|
{
|
|
unifi_error(card->ospriv,
|
|
"Function reset method not supported for chip_id=%d\n",
|
|
card->chip_id);
|
|
func_exit();
|
|
return CSR_RESULT_FAILURE;
|
|
}
|
|
|
|
r = CSR_RESULT_SUCCESS;
|
|
for (i = 0; i < MAILBOX2_ATTEMPTS; i++)
|
|
{
|
|
unifi_trace(card->ospriv, UDBG1, "waiting for disable to complete, attempt %d\n", i);
|
|
|
|
/*
|
|
* It's quite likely that this read will timeout for the
|
|
* first few tries - especially if we have reset via
|
|
* DBG_RESET.
|
|
*/
|
|
#if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_SDIO_TRACE)
|
|
unifi_debug_log_to_buf("r0@%02X=", SDIO_IO_READY);
|
|
#endif
|
|
csrResult = CsrSdioF0Read8(card->sdio_if, SDIO_IO_READY, &io_enable);
|
|
#if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_SDIO_TRACE)
|
|
if (csrResult != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_debug_log_to_buf("error=%X\n", csrResult);
|
|
}
|
|
else
|
|
{
|
|
unifi_debug_log_to_buf("%X\n", io_enable);
|
|
}
|
|
#endif
|
|
if (csrResult == CSR_SDIO_RESULT_NO_DEVICE)
|
|
{
|
|
return CSR_WIFI_HIP_RESULT_NO_DEVICE;
|
|
}
|
|
if (csrResult == CSR_RESULT_SUCCESS)
|
|
{
|
|
s16 enabled = io_enable & (1 << card->function);
|
|
r = CSR_RESULT_SUCCESS;
|
|
if (!enabled)
|
|
{
|
|
unifi_trace(card->ospriv, UDBG1,
|
|
"Disable complete (function %d is disabled) in ~ %u msecs\n",
|
|
card->function, i * MAILBOX2_TIMEOUT);
|
|
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* We ignore read failures for the first few reads,
|
|
* they are probably benign.
|
|
*/
|
|
r = ConvertCsrSdioToCsrHipResult(card, csrResult);
|
|
if (i > (MAILBOX2_ATTEMPTS / 4))
|
|
{
|
|
unifi_trace(card->ospriv, UDBG1,
|
|
"Failed to read CCCR IO Ready register while polling for disable\n");
|
|
}
|
|
}
|
|
CsrThreadSleep(MAILBOX2_TIMEOUT);
|
|
}
|
|
|
|
if ((r == CSR_RESULT_SUCCESS) && (i == MAILBOX2_ATTEMPTS))
|
|
{
|
|
unifi_trace(card->ospriv, UDBG1, "Timeout waiting for UniFi to complete disable\n");
|
|
r = CSR_RESULT_FAILURE;
|
|
}
|
|
|
|
func_exit();
|
|
return r;
|
|
} /* card_wait_for_unifi_to_reset() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* card_wait_for_firmware_to_start
|
|
*
|
|
* Polls the MAILBOX1 register for a non-zero value.
|
|
* Then reads MAILBOX0 and forms the two values into a 32-bit address
|
|
* which is returned to the caller.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to card struct
|
|
* paddr Pointer to receive the UniFi address formed
|
|
* by concatenating MAILBOX1 and MAILBOX0.
|
|
*
|
|
* Returns:
|
|
* CSR_RESULT_SUCCESS on success, CSR error code on failure.
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
CsrResult card_wait_for_firmware_to_start(card_t *card, u32 *paddr)
|
|
{
|
|
s32 i;
|
|
u16 mbox0, mbox1;
|
|
CsrResult r;
|
|
|
|
func_enter();
|
|
|
|
/*
|
|
* Wait for UniFi to initialise its data structures by polling
|
|
* the SHARED_MAILBOX1 register.
|
|
* Experience shows this is typically 120ms.
|
|
*/
|
|
CsrThreadSleep(MAILBOX1_TIMEOUT);
|
|
|
|
mbox1 = 0;
|
|
unifi_trace(card->ospriv, UDBG1, "waiting for MAILBOX1 to be non-zero...\n");
|
|
for (i = 0; i < MAILBOX1_ATTEMPTS; i++)
|
|
{
|
|
r = unifi_read_direct16(card, ChipHelper_MAILBOX1(card->helper) * 2, &mbox1);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
/* These reads can fail if UniFi isn't up yet, so try again */
|
|
unifi_warning(card->ospriv, "Failed to read UniFi Mailbox1 register\n");
|
|
}
|
|
|
|
if ((r == CSR_RESULT_SUCCESS) && (mbox1 != 0))
|
|
{
|
|
unifi_trace(card->ospriv, UDBG1, "MAILBOX1 ready (0x%04X) in %u millisecs\n",
|
|
mbox1, i * MAILBOX1_TIMEOUT);
|
|
|
|
/* Read the MAILBOX1 again in case we caught the value as it
|
|
* changed. */
|
|
r = unifi_read_direct16(card, ChipHelper_MAILBOX1(card->helper) * 2, &mbox1);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to read UniFi Mailbox1 register for second time\n");
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
unifi_trace(card->ospriv, UDBG1, "MAILBOX1 value=0x%04X\n", mbox1);
|
|
|
|
break;
|
|
}
|
|
|
|
CsrThreadSleep(MAILBOX1_TIMEOUT);
|
|
if ((i % 100) == 99)
|
|
{
|
|
unifi_trace(card->ospriv, UDBG2, "MAILBOX1 not ready (0x%X), still trying...\n", mbox1);
|
|
}
|
|
}
|
|
|
|
if ((r == CSR_RESULT_SUCCESS) && (mbox1 == 0))
|
|
{
|
|
unifi_trace(card->ospriv, UDBG1, "Timeout waiting for firmware to start, Mailbox1 still 0 after %d ms\n",
|
|
MAILBOX1_ATTEMPTS * MAILBOX1_TIMEOUT);
|
|
func_exit_r(CSR_RESULT_FAILURE);
|
|
return CSR_RESULT_FAILURE;
|
|
}
|
|
|
|
|
|
/*
|
|
* Complete the reset handshake by setting MAILBOX2 to 0xFFFF
|
|
*/
|
|
r = unifi_write_direct16(card, ChipHelper_SDIO_HIP_HANDSHAKE(card->helper) * 2, 0xFFFF);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to write f/w startup handshake to MAILBOX2\n");
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* Read the Symbol Look Up Table (SLUT) offset.
|
|
* Top 16 bits are in mbox1, read the lower 16 bits from mbox0.
|
|
*/
|
|
mbox0 = 0;
|
|
r = unifi_read_direct16(card, ChipHelper_MAILBOX0(card->helper) * 2, &mbox0);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to read UniFi Mailbox0 register\n");
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
|
|
*paddr = (((u32)mbox1 << 16) | mbox0);
|
|
|
|
func_exit();
|
|
return CSR_RESULT_SUCCESS;
|
|
} /* card_wait_for_firmware_to_start() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* unifi_capture_panic
|
|
*
|
|
* Attempt to capture panic codes from the firmware. This may involve
|
|
* warm reset of the chip to regain access following a watchdog reset.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to card struct
|
|
*
|
|
* Returns:
|
|
* CSR_RESULT_SUCCESS if panic codes were captured, or none available
|
|
* CSR_RESULT_FAILURE if the driver could not access function 1
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
CsrResult unifi_capture_panic(card_t *card)
|
|
{
|
|
func_enter();
|
|
|
|
/* The firmware must have previously initialised to read the panic addresses
|
|
* from the SLUT
|
|
*/
|
|
if (!card->panic_data_phy_addr || !card->panic_data_mac_addr)
|
|
{
|
|
func_exit();
|
|
return CSR_RESULT_SUCCESS;
|
|
}
|
|
|
|
/* Ensure we can access function 1 following a panic/watchdog reset */
|
|
if (card_access_panic(card) == CSR_RESULT_SUCCESS)
|
|
{
|
|
/* Read the panic codes */
|
|
unifi_read_panic(card);
|
|
}
|
|
else
|
|
{
|
|
unifi_info(card->ospriv, "Unable to read panic codes");
|
|
}
|
|
|
|
func_exit();
|
|
return CSR_RESULT_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* card_access_panic
|
|
* Attempt to read the WLAN SDIO function in order to read panic codes
|
|
* and perform various reset steps to regain access if the read fails.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to card struct
|
|
*
|
|
* Returns:
|
|
* CSR_RESULT_SUCCESS if panic codes can be read
|
|
* CSR error code if panic codes can not be read
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
static CsrResult card_access_panic(card_t *card)
|
|
{
|
|
u16 data_u16 = 0;
|
|
s32 i;
|
|
CsrResult r, sr;
|
|
|
|
func_enter();
|
|
|
|
/* A chip version of zero means that the version never got succesfully read
|
|
* during reset. In this case give up because it will not be possible to
|
|
* verify the chip version.
|
|
*/
|
|
if (!card->chip_version)
|
|
{
|
|
unifi_info(card->ospriv, "Unknown chip version\n");
|
|
return CSR_RESULT_FAILURE;
|
|
}
|
|
|
|
/* Ensure chip is awake or access to function 1 will fail */
|
|
r = unifi_set_host_state(card, UNIFI_HOST_STATE_AWAKE);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "unifi_set_host_state() failed %d\n", r);
|
|
return CSR_RESULT_FAILURE; /* Card is probably unpowered */
|
|
}
|
|
CsrThreadSleep(20);
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
sr = CsrSdioRead16(card->sdio_if, CHIP_HELPER_UNIFI_GBL_CHIP_VERSION * 2, &data_u16);
|
|
if (sr != CSR_RESULT_SUCCESS || data_u16 != card->chip_version)
|
|
{
|
|
unifi_info(card->ospriv, "Failed to read valid chip version sr=%d (0x%04x want 0x%04x) try %d\n",
|
|
sr, data_u16, card->chip_version, i);
|
|
|
|
/* Set clock speed low */
|
|
sr = CsrSdioMaxBusClockFrequencySet(card->sdio_if, UNIFI_SDIO_CLOCK_SAFE_HZ);
|
|
if (sr != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "CsrSdioMaxBusClockFrequencySet() failed1 %d\n", sr);
|
|
r = ConvertCsrSdioToCsrHipResult(card, sr);
|
|
}
|
|
card->sdio_clock_speed = UNIFI_SDIO_CLOCK_SAFE_HZ;
|
|
|
|
/* First try re-enabling function in case a f/w watchdog reset disabled it */
|
|
if (i == 0)
|
|
{
|
|
unifi_info(card->ospriv, "Try function enable\n");
|
|
sr = CsrSdioFunctionEnable(card->sdio_if);
|
|
if (sr != CSR_RESULT_SUCCESS)
|
|
{
|
|
r = ConvertCsrSdioToCsrHipResult(card, sr);
|
|
unifi_error(card->ospriv, "CsrSdioFunctionEnable failed %d (HIP %d)\n", sr, r);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* Second try, set awake */
|
|
unifi_info(card->ospriv, "Try set awake\n");
|
|
|
|
/* Ensure chip is awake */
|
|
r = unifi_set_host_state(card, UNIFI_HOST_STATE_AWAKE);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "unifi_set_host_state() failed2 %d\n", r);
|
|
}
|
|
|
|
/* Set clock speed low in case setting the host state raised it, which
|
|
* would only happen if host state was previously TORPID
|
|
*/
|
|
sr = CsrSdioMaxBusClockFrequencySet(card->sdio_if, UNIFI_SDIO_CLOCK_SAFE_HZ);
|
|
if (sr != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "CsrSdioMaxBusClockFrequencySet() failed2 %d\n", sr);
|
|
}
|
|
card->sdio_clock_speed = UNIFI_SDIO_CLOCK_SAFE_HZ;
|
|
|
|
if (i == 1)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/* Perform a s/w reset to preserve as much as the card state as possible,
|
|
* (mainly the preserve RAM). The context will be lost for coredump - but as we
|
|
* were unable to access the WLAN function for panic, the coredump would have
|
|
* also failed without a reset.
|
|
*/
|
|
unifi_info(card->ospriv, "Try s/w reset\n");
|
|
|
|
r = unifi_card_hard_reset(card);
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "unifi_card_hard_reset() failed %d\n", r);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (i > 0)
|
|
{
|
|
unifi_info(card->ospriv, "Read chip version 0x%x after %d retries\n", data_u16, i);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
r = ConvertCsrSdioToCsrHipResult(card, sr);
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* unifi_read_panic
|
|
* Reads, saves and prints panic codes stored by the firmware in UniFi's
|
|
* preserve RAM by the last panic that occurred since chip was powered.
|
|
* Nothing is saved if the panic codes are read as zero.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to card struct
|
|
*
|
|
* Returns:
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
void unifi_read_panic(card_t *card)
|
|
{
|
|
CsrResult r;
|
|
u16 p_code, p_arg;
|
|
|
|
func_enter();
|
|
|
|
/* The firmware must have previously initialised to read the panic addresses
|
|
* from the SLUT
|
|
*/
|
|
if (!card->panic_data_phy_addr || !card->panic_data_mac_addr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* Get the panic data from PHY */
|
|
r = unifi_card_read16(card, card->panic_data_phy_addr, &p_code);
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "capture_panic: unifi_read16 %08x failed %d\n", card->panic_data_phy_addr, r);
|
|
p_code = 0;
|
|
}
|
|
if (p_code)
|
|
{
|
|
r = unifi_card_read16(card, card->panic_data_phy_addr + 2, &p_arg);
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "capture_panic: unifi_read16 %08x failed %d\n", card->panic_data_phy_addr + 2, r);
|
|
}
|
|
unifi_error(card->ospriv, "Last UniFi PHY PANIC %04x arg %04x\n", p_code, p_arg);
|
|
card->last_phy_panic_code = p_code;
|
|
card->last_phy_panic_arg = p_arg;
|
|
}
|
|
|
|
/* Get the panic data from MAC */
|
|
r = unifi_card_read16(card, card->panic_data_mac_addr, &p_code);
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "capture_panic: unifi_read16 %08x failed %d\n", card->panic_data_mac_addr, r);
|
|
p_code = 0;
|
|
}
|
|
if (p_code)
|
|
{
|
|
r = unifi_card_read16(card, card->panic_data_mac_addr + 2, &p_arg);
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "capture_panic: unifi_read16 %08x failed %d\n", card->panic_data_mac_addr + 2, r);
|
|
}
|
|
unifi_error(card->ospriv, "Last UniFi MAC PANIC %04x arg %04x\n", p_code, p_arg);
|
|
card->last_mac_panic_code = p_code;
|
|
card->last_mac_panic_arg = p_arg;
|
|
}
|
|
|
|
func_exit();
|
|
}
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* card_allocate_memory_resources
|
|
*
|
|
* Allocates memory for the from-host, to-host bulk data slots,
|
|
* soft queue buffers and bulk data buffers.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to card struct
|
|
*
|
|
* Returns:
|
|
* CSR_RESULT_SUCCESS on success, CSR error code on failure.
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
static CsrResult card_allocate_memory_resources(card_t *card)
|
|
{
|
|
s16 n, i, k, r;
|
|
sdio_config_data_t *cfg_data;
|
|
|
|
func_enter();
|
|
|
|
/* Reset any state carried forward from a previous life */
|
|
card->fh_command_queue.q_rd_ptr = 0;
|
|
card->fh_command_queue.q_wr_ptr = 0;
|
|
(void)scnprintf(card->fh_command_queue.name, UNIFI_QUEUE_NAME_MAX_LENGTH,
|
|
"fh_cmd_q");
|
|
for (i = 0; i < UNIFI_NO_OF_TX_QS; i++)
|
|
{
|
|
card->fh_traffic_queue[i].q_rd_ptr = 0;
|
|
card->fh_traffic_queue[i].q_wr_ptr = 0;
|
|
(void)scnprintf(card->fh_traffic_queue[i].name,
|
|
UNIFI_QUEUE_NAME_MAX_LENGTH, "fh_data_q%d", i);
|
|
}
|
|
#ifndef CSR_WIFI_HIP_TA_DISABLE
|
|
unifi_ta_sampling_init(card);
|
|
#endif
|
|
/* Convenience short-cut */
|
|
cfg_data = &card->config_data;
|
|
|
|
/*
|
|
* Allocate memory for the from-host and to-host signal buffers.
|
|
*/
|
|
card->fh_buffer.buf = kmalloc(UNIFI_FH_BUF_SIZE, GFP_KERNEL);
|
|
if (card->fh_buffer.buf == NULL)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to allocate memory for F-H signals\n");
|
|
func_exit_r(CSR_WIFI_HIP_RESULT_NO_MEMORY);
|
|
return CSR_WIFI_HIP_RESULT_NO_MEMORY;
|
|
}
|
|
card->fh_buffer.bufsize = UNIFI_FH_BUF_SIZE;
|
|
card->fh_buffer.ptr = card->fh_buffer.buf;
|
|
card->fh_buffer.count = 0;
|
|
|
|
card->th_buffer.buf = kmalloc(UNIFI_FH_BUF_SIZE, GFP_KERNEL);
|
|
if (card->th_buffer.buf == NULL)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to allocate memory for T-H signals\n");
|
|
func_exit_r(CSR_WIFI_HIP_RESULT_NO_MEMORY);
|
|
return CSR_WIFI_HIP_RESULT_NO_MEMORY;
|
|
}
|
|
card->th_buffer.bufsize = UNIFI_FH_BUF_SIZE;
|
|
card->th_buffer.ptr = card->th_buffer.buf;
|
|
card->th_buffer.count = 0;
|
|
|
|
|
|
/*
|
|
* Allocate memory for the from-host and to-host bulk data slots.
|
|
* This is done as separate kmallocs because lots of smaller
|
|
* allocations are more likely to succeed than one huge one.
|
|
*/
|
|
|
|
/* Allocate memory for the array of pointers */
|
|
n = cfg_data->num_fromhost_data_slots;
|
|
|
|
unifi_trace(card->ospriv, UDBG3, "Alloc from-host resources, %d slots.\n", n);
|
|
card->from_host_data = kmalloc(n * sizeof(slot_desc_t), GFP_KERNEL);
|
|
if (card->from_host_data == NULL)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to allocate memory for F-H bulk data array\n");
|
|
func_exit_r(CSR_WIFI_HIP_RESULT_NO_MEMORY);
|
|
return CSR_WIFI_HIP_RESULT_NO_MEMORY;
|
|
}
|
|
|
|
/* Initialise from-host bulk data slots */
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
UNIFI_INIT_BULK_DATA(&card->from_host_data[i].bd);
|
|
}
|
|
|
|
/* Allocate memory for the array used for slot host tag mapping */
|
|
card->fh_slot_host_tag_record = kmalloc(n * sizeof(u32), GFP_KERNEL);
|
|
|
|
if (card->fh_slot_host_tag_record == NULL)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to allocate memory for F-H slot host tag mapping array\n");
|
|
func_exit_r(CSR_WIFI_HIP_RESULT_NO_MEMORY);
|
|
return CSR_WIFI_HIP_RESULT_NO_MEMORY;
|
|
}
|
|
|
|
/* Initialise host tag entries for from-host bulk data slots */
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
card->fh_slot_host_tag_record[i] = CSR_WIFI_HIP_RESERVED_HOST_TAG;
|
|
}
|
|
|
|
|
|
/* Allocate memory for the array of pointers */
|
|
n = cfg_data->num_tohost_data_slots;
|
|
|
|
unifi_trace(card->ospriv, UDBG3, "Alloc to-host resources, %d slots.\n", n);
|
|
card->to_host_data = kmalloc(n * sizeof(bulk_data_desc_t), GFP_KERNEL);
|
|
if (card->to_host_data == NULL)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to allocate memory for T-H bulk data array\n");
|
|
func_exit_r(CSR_WIFI_HIP_RESULT_NO_MEMORY);
|
|
return CSR_WIFI_HIP_RESULT_NO_MEMORY;
|
|
}
|
|
|
|
/* Initialise to-host bulk data slots */
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
UNIFI_INIT_BULK_DATA(&card->to_host_data[i]);
|
|
}
|
|
|
|
/*
|
|
* Initialise buffers for soft Q
|
|
*/
|
|
for (i = 0; i < UNIFI_SOFT_COMMAND_Q_LENGTH; i++)
|
|
{
|
|
for (r = 0; r < UNIFI_MAX_DATA_REFERENCES; r++)
|
|
{
|
|
UNIFI_INIT_BULK_DATA(&card->fh_command_q_body[i].bulkdata[r]);
|
|
}
|
|
}
|
|
|
|
for (k = 0; k < UNIFI_NO_OF_TX_QS; k++)
|
|
{
|
|
for (i = 0; i < UNIFI_SOFT_TRAFFIC_Q_LENGTH; i++)
|
|
{
|
|
for (r = 0; r < UNIFI_MAX_DATA_REFERENCES; r++)
|
|
{
|
|
UNIFI_INIT_BULK_DATA(&card->fh_traffic_q_body[k][i].bulkdata[r]);
|
|
}
|
|
}
|
|
}
|
|
|
|
card->memory_resources_allocated = 1;
|
|
|
|
func_exit();
|
|
return CSR_RESULT_SUCCESS;
|
|
} /* card_allocate_memory_resources() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* unifi_free_bulk_data
|
|
*
|
|
* Free the data associated to a bulk data structure.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to card struct
|
|
* bulk_data_slot Pointer to bulk data structure
|
|
*
|
|
* Returns:
|
|
* None.
|
|
*
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
static void unifi_free_bulk_data(card_t *card, bulk_data_desc_t *bulk_data_slot)
|
|
{
|
|
if (bulk_data_slot->data_length != 0)
|
|
{
|
|
unifi_net_data_free(card->ospriv, bulk_data_slot);
|
|
}
|
|
} /* unifi_free_bulk_data() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* card_free_memory_resources
|
|
*
|
|
* Frees memory allocated for the from-host, to-host bulk data slots,
|
|
* soft queue buffers and bulk data buffers.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to card struct
|
|
*
|
|
* Returns:
|
|
* None.
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
static void card_free_memory_resources(card_t *card)
|
|
{
|
|
func_enter();
|
|
|
|
unifi_trace(card->ospriv, UDBG1, "Freeing card memory resources.\n");
|
|
|
|
/* Clear our internal queues */
|
|
unifi_cancel_pending_signals(card);
|
|
|
|
|
|
kfree(card->to_host_data);
|
|
card->to_host_data = NULL;
|
|
|
|
kfree(card->from_host_data);
|
|
card->from_host_data = NULL;
|
|
|
|
/* free the memory for slot host tag mapping array */
|
|
kfree(card->fh_slot_host_tag_record);
|
|
card->fh_slot_host_tag_record = NULL;
|
|
|
|
kfree(card->fh_buffer.buf);
|
|
card->fh_buffer.ptr = card->fh_buffer.buf = NULL;
|
|
card->fh_buffer.bufsize = 0;
|
|
card->fh_buffer.count = 0;
|
|
|
|
kfree(card->th_buffer.buf);
|
|
card->th_buffer.ptr = card->th_buffer.buf = NULL;
|
|
card->th_buffer.bufsize = 0;
|
|
card->th_buffer.count = 0;
|
|
|
|
|
|
card->memory_resources_allocated = 0;
|
|
|
|
func_exit();
|
|
} /* card_free_memory_resources() */
|
|
|
|
|
|
static void card_init_soft_queues(card_t *card)
|
|
{
|
|
s16 i;
|
|
|
|
func_enter();
|
|
|
|
unifi_trace(card->ospriv, UDBG1, "Initialising internal signal queues.\n");
|
|
/* Reset any state carried forward from a previous life */
|
|
card->fh_command_queue.q_rd_ptr = 0;
|
|
card->fh_command_queue.q_wr_ptr = 0;
|
|
(void)scnprintf(card->fh_command_queue.name, UNIFI_QUEUE_NAME_MAX_LENGTH,
|
|
"fh_cmd_q");
|
|
for (i = 0; i < UNIFI_NO_OF_TX_QS; i++)
|
|
{
|
|
card->fh_traffic_queue[i].q_rd_ptr = 0;
|
|
card->fh_traffic_queue[i].q_wr_ptr = 0;
|
|
(void)scnprintf(card->fh_traffic_queue[i].name,
|
|
UNIFI_QUEUE_NAME_MAX_LENGTH, "fh_data_q%d", i);
|
|
}
|
|
#ifndef CSR_WIFI_HIP_TA_DISABLE
|
|
unifi_ta_sampling_init(card);
|
|
#endif
|
|
func_exit();
|
|
}
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* unifi_cancel_pending_signals
|
|
*
|
|
* Free the signals and associated bulk data, pending in the core.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to card struct
|
|
*
|
|
* Returns:
|
|
* None.
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
void unifi_cancel_pending_signals(card_t *card)
|
|
{
|
|
s16 i, n, r;
|
|
func_enter();
|
|
|
|
unifi_trace(card->ospriv, UDBG1, "Canceling pending signals.\n");
|
|
|
|
if (card->to_host_data)
|
|
{
|
|
/*
|
|
* Free any bulk data buffers allocated for the t-h slots
|
|
* This will clear all buffers that did not make it to
|
|
* unifi_receive_event() before cancel was request.
|
|
*/
|
|
n = card->config_data.num_tohost_data_slots;
|
|
unifi_trace(card->ospriv, UDBG3, "Freeing to-host resources, %d slots.\n", n);
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
unifi_free_bulk_data(card, &card->to_host_data[i]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If any of the from-host bulk data has reached the card->from_host_data
|
|
* but not UniFi, we need to free the buffers here.
|
|
*/
|
|
if (card->from_host_data)
|
|
{
|
|
/* Free any bulk data buffers allocated for the f-h slots */
|
|
n = card->config_data.num_fromhost_data_slots;
|
|
unifi_trace(card->ospriv, UDBG3, "Freeing from-host resources, %d slots.\n", n);
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
unifi_free_bulk_data(card, &card->from_host_data[i].bd);
|
|
}
|
|
|
|
for (i = 0; i < UNIFI_NO_OF_TX_QS; i++)
|
|
{
|
|
card->dynamic_slot_data.from_host_used_slots[i] = 0;
|
|
card->dynamic_slot_data.from_host_max_slots[i] = 0;
|
|
card->dynamic_slot_data.from_host_reserved_slots[i] = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Free any bulk data buffers allocated in the soft queues.
|
|
* This covers the case where a bulk data pointer has reached the soft queue
|
|
* but not the card->from_host_data.
|
|
*/
|
|
unifi_trace(card->ospriv, UDBG3, "Freeing cmd q resources.\n");
|
|
for (i = 0; i < UNIFI_SOFT_COMMAND_Q_LENGTH; i++)
|
|
{
|
|
for (r = 0; r < UNIFI_MAX_DATA_REFERENCES; r++)
|
|
{
|
|
unifi_free_bulk_data(card, &card->fh_command_q_body[i].bulkdata[r]);
|
|
}
|
|
}
|
|
|
|
unifi_trace(card->ospriv, UDBG3, "Freeing traffic q resources.\n");
|
|
for (n = 0; n < UNIFI_NO_OF_TX_QS; n++)
|
|
{
|
|
for (i = 0; i < UNIFI_SOFT_TRAFFIC_Q_LENGTH; i++)
|
|
{
|
|
for (r = 0; r < UNIFI_MAX_DATA_REFERENCES; r++)
|
|
{
|
|
unifi_free_bulk_data(card, &card->fh_traffic_q_body[n][i].bulkdata[r]);
|
|
}
|
|
}
|
|
}
|
|
|
|
card_init_soft_queues(card);
|
|
|
|
func_exit();
|
|
} /* unifi_cancel_pending_signals() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* unifi_free_card
|
|
*
|
|
* Free the memory allocated for the card structure and buffers.
|
|
*
|
|
* Notes:
|
|
* The porting layer is responsible for freeing any mini-coredump buffers
|
|
* allocated when it called unifi_coredump_init(), by calling
|
|
* unifi_coredump_free() before calling this function.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to card struct
|
|
*
|
|
* Returns:
|
|
* None.
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
void unifi_free_card(card_t *card)
|
|
{
|
|
func_enter();
|
|
#ifdef CSR_PRE_ALLOC_NET_DATA
|
|
prealloc_netdata_free(card);
|
|
#endif
|
|
/* Free any memory allocated. */
|
|
card_free_memory_resources(card);
|
|
|
|
/* Warn if caller didn't free coredump buffers */
|
|
if (card->dump_buf)
|
|
{
|
|
unifi_error(card->ospriv, "Caller should call unifi_coredump_free()\n");
|
|
unifi_coredump_free(card); /* free anyway to prevent memory leak */
|
|
}
|
|
|
|
kfree(card);
|
|
|
|
func_exit();
|
|
} /* unifi_free_card() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* card_init_slots
|
|
*
|
|
* Allocate memory for host-side slot data and signal queues.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to card object
|
|
*
|
|
* Returns:
|
|
* CSR error code.
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
static CsrResult card_init_slots(card_t *card)
|
|
{
|
|
CsrResult r;
|
|
u8 i;
|
|
|
|
func_enter();
|
|
|
|
/* Allocate the buffers we need, only once. */
|
|
if (card->memory_resources_allocated == 1)
|
|
{
|
|
card_free_memory_resources(card);
|
|
}
|
|
else
|
|
{
|
|
/* Initialise our internal command and traffic queues */
|
|
card_init_soft_queues(card);
|
|
}
|
|
|
|
r = card_allocate_memory_resources(card);
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to allocate card memory resources.\n");
|
|
card_free_memory_resources(card);
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
|
|
if (card->sdio_ctrl_addr == 0)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to find config struct!\n");
|
|
func_exit_r(CSR_WIFI_HIP_RESULT_INVALID_VALUE);
|
|
return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
|
|
}
|
|
|
|
/*
|
|
* Set initial counts.
|
|
*/
|
|
|
|
card->from_host_data_head = 0;
|
|
|
|
/* Get initial signal counts from UniFi, in case it has not been reset. */
|
|
{
|
|
u16 s;
|
|
|
|
/* Get the from-host-signals-written count */
|
|
r = unifi_card_read16(card, card->sdio_ctrl_addr + 0, &s);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to read from-host sig written count\n");
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
card->from_host_signals_w = (s16)s;
|
|
|
|
/* Get the to-host-signals-written count */
|
|
r = unifi_card_read16(card, card->sdio_ctrl_addr + 6, &s);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to read to-host sig read count\n");
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
card->to_host_signals_r = (s16)s;
|
|
}
|
|
|
|
/* Set Initialised flag. */
|
|
r = unifi_card_write16(card, card->init_flag_addr, 0x0001);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to write initialised flag\n");
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
|
|
/* Dynamic queue reservation */
|
|
memset(&card->dynamic_slot_data, 0, sizeof(card_dynamic_slot_t));
|
|
|
|
for (i = 0; i < UNIFI_NO_OF_TX_QS; i++)
|
|
{
|
|
card->dynamic_slot_data.from_host_max_slots[i] = card->config_data.num_fromhost_data_slots -
|
|
UNIFI_RESERVED_COMMAND_SLOTS;
|
|
card->dynamic_slot_data.queue_stable[i] = FALSE;
|
|
}
|
|
|
|
card->dynamic_slot_data.packets_interval = UNIFI_PACKETS_INTERVAL;
|
|
|
|
func_exit();
|
|
return CSR_RESULT_SUCCESS;
|
|
} /* card_init_slots() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* unifi_set_udi_hook
|
|
*
|
|
* Registers the udi hook that reports the sent signals to the core.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to the card context struct
|
|
* udi_fn Pointer to the callback function.
|
|
*
|
|
* Returns:
|
|
* CSR_WIFI_HIP_RESULT_INVALID_VALUE if the card pointer is invalid,
|
|
* CSR_RESULT_SUCCESS on success.
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
CsrResult unifi_set_udi_hook(card_t *card, udi_func_t udi_fn)
|
|
{
|
|
if (card == NULL)
|
|
{
|
|
return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
|
|
}
|
|
|
|
if (card->udi_hook == NULL)
|
|
{
|
|
card->udi_hook = udi_fn;
|
|
}
|
|
|
|
return CSR_RESULT_SUCCESS;
|
|
} /* unifi_set_udi_hook() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* unifi_remove_udi_hook
|
|
*
|
|
* Removes the udi hook that reports the sent signals from the core.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to the card context struct
|
|
* udi_fn Pointer to the callback function.
|
|
*
|
|
* Returns:
|
|
* CSR_WIFI_HIP_RESULT_INVALID_VALUE if the card pointer is invalid,
|
|
* CSR_RESULT_SUCCESS on success.
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
CsrResult unifi_remove_udi_hook(card_t *card, udi_func_t udi_fn)
|
|
{
|
|
if (card == NULL)
|
|
{
|
|
return CSR_WIFI_HIP_RESULT_INVALID_VALUE;
|
|
}
|
|
|
|
if (card->udi_hook == udi_fn)
|
|
{
|
|
card->udi_hook = NULL;
|
|
}
|
|
|
|
return CSR_RESULT_SUCCESS;
|
|
} /* unifi_remove_udi_hook() */
|
|
|
|
|
|
static void CardReassignDynamicReservation(card_t *card)
|
|
{
|
|
u8 i;
|
|
|
|
func_enter();
|
|
|
|
unifi_trace(card->ospriv, UDBG5, "Packets Txed %d %d %d %d\n",
|
|
card->dynamic_slot_data.packets_txed[0],
|
|
card->dynamic_slot_data.packets_txed[1],
|
|
card->dynamic_slot_data.packets_txed[2],
|
|
card->dynamic_slot_data.packets_txed[3]);
|
|
|
|
/* Clear reservation and recalculate max slots */
|
|
for (i = 0; i < UNIFI_NO_OF_TX_QS; i++)
|
|
{
|
|
card->dynamic_slot_data.queue_stable[i] = FALSE;
|
|
card->dynamic_slot_data.from_host_reserved_slots[i] = 0;
|
|
card->dynamic_slot_data.from_host_max_slots[i] = card->config_data.num_fromhost_data_slots -
|
|
UNIFI_RESERVED_COMMAND_SLOTS;
|
|
card->dynamic_slot_data.packets_txed[i] = 0;
|
|
|
|
unifi_trace(card->ospriv, UDBG5, "CardReassignDynamicReservation: queue %d reserved %d Max %d\n", i,
|
|
card->dynamic_slot_data.from_host_reserved_slots[i],
|
|
card->dynamic_slot_data.from_host_max_slots[i]);
|
|
}
|
|
|
|
card->dynamic_slot_data.total_packets_txed = 0;
|
|
func_exit();
|
|
}
|
|
|
|
|
|
/* Algorithm to dynamically reserve slots. The logic is based mainly on the outstanding queue
|
|
* length. Slots are reserved for particular queues during an interval and cleared after the interval.
|
|
* Each queue has three associated variables.. a) used slots - the number of slots currently occupied
|
|
* by the queue b) reserved slots - number of slots reserved specifically for the queue c) max slots - total
|
|
* slots that this queue can actually use (may be higher than reserved slots and is dependent on reserved slots
|
|
* for other queues).
|
|
* This function is called when there are no slots available for a queue. It checks to see if there are enough
|
|
* unreserved slots sufficient for this request. If available these slots are reserved for the queue.
|
|
* If there are not enough unreserved slots, a fair share for each queue is calculated based on the total slots
|
|
* and the number of active queues (any queue with existing reservation is considered active). Queues needing
|
|
* less than their fair share are allowed to have the previously reserved slots. The remaining slots are
|
|
* distributed evenly among queues that need more than the fair share
|
|
*
|
|
* A better scheme would take current bandwidth per AC into consideration when reserving slots. An
|
|
* implementation scheme could consider the relative time/service period for slots in an AC. If the firmware
|
|
* services other ACs faster than a particular AC (packets wait in the slots longer) then it is fair to reserve
|
|
* less slots for the AC
|
|
*/
|
|
static void CardCheckDynamicReservation(card_t *card, unifi_TrafficQueue queue)
|
|
{
|
|
u16 q_len, active_queues = 0, excess_queue_slots, div_extra_slots,
|
|
queue_fair_share, reserved_slots = 0, q, excess_need_queues = 0, unmovable_slots = 0;
|
|
s32 i;
|
|
q_t *sigq;
|
|
u16 num_data_slots = card->config_data.num_fromhost_data_slots - UNIFI_RESERVED_COMMAND_SLOTS;
|
|
|
|
func_enter();
|
|
|
|
/* Calculate the pending queue length */
|
|
sigq = &card->fh_traffic_queue[queue];
|
|
q_len = CSR_WIFI_HIP_Q_SLOTS_USED(sigq);
|
|
|
|
if (q_len <= card->dynamic_slot_data.from_host_reserved_slots[queue])
|
|
{
|
|
unifi_trace(card->ospriv, UDBG5, "queue %d q_len %d already has that many reserved slots, exiting\n", queue, q_len);
|
|
func_exit();
|
|
return;
|
|
}
|
|
|
|
/* Upper limit */
|
|
if (q_len > num_data_slots)
|
|
{
|
|
q_len = num_data_slots;
|
|
}
|
|
|
|
for (i = 0; i < UNIFI_NO_OF_TX_QS; i++)
|
|
{
|
|
if (i != (s32)queue)
|
|
{
|
|
reserved_slots += card->dynamic_slot_data.from_host_reserved_slots[i];
|
|
}
|
|
if ((i == (s32)queue) || (card->dynamic_slot_data.from_host_reserved_slots[i] > 0))
|
|
{
|
|
active_queues++;
|
|
}
|
|
}
|
|
|
|
unifi_trace(card->ospriv, UDBG5, "CardCheckDynamicReservation: queue %d q_len %d\n", queue, q_len);
|
|
unifi_trace(card->ospriv, UDBG5, "Active queues %d reserved slots on other queues %d\n",
|
|
active_queues, reserved_slots);
|
|
|
|
if (reserved_slots + q_len <= num_data_slots)
|
|
{
|
|
card->dynamic_slot_data.from_host_reserved_slots[queue] = q_len;
|
|
if (q_len == num_data_slots)
|
|
{
|
|
/* This is the common case when just 1 stream is going */
|
|
card->dynamic_slot_data.queue_stable[queue] = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
queue_fair_share = num_data_slots / active_queues;
|
|
unifi_trace(card->ospriv, UDBG5, "queue fair share %d\n", queue_fair_share);
|
|
|
|
/* Evenly distribute slots among active queues */
|
|
/* Find out the queues that need excess of fair share. Also find slots allocated
|
|
* to queues less than their fair share, these slots cannot be reallocated (unmovable slots) */
|
|
|
|
card->dynamic_slot_data.from_host_reserved_slots[queue] = q_len;
|
|
|
|
for (i = 0; i < UNIFI_NO_OF_TX_QS; i++)
|
|
{
|
|
if (card->dynamic_slot_data.from_host_reserved_slots[i] > queue_fair_share)
|
|
{
|
|
excess_need_queues++;
|
|
}
|
|
else
|
|
{
|
|
unmovable_slots += card->dynamic_slot_data.from_host_reserved_slots[i];
|
|
}
|
|
}
|
|
|
|
unifi_trace(card->ospriv, UDBG5, "Excess need queues %d\n", excess_need_queues);
|
|
|
|
/* Now find the slots per excess demand queue */
|
|
excess_queue_slots = (num_data_slots - unmovable_slots) / excess_need_queues;
|
|
div_extra_slots = (num_data_slots - unmovable_slots) - excess_queue_slots * excess_need_queues;
|
|
for (i = UNIFI_NO_OF_TX_QS - 1; i >= 0; i--)
|
|
{
|
|
if (card->dynamic_slot_data.from_host_reserved_slots[i] > excess_queue_slots)
|
|
{
|
|
card->dynamic_slot_data.from_host_reserved_slots[i] = excess_queue_slots;
|
|
if (div_extra_slots > 0)
|
|
{
|
|
card->dynamic_slot_data.from_host_reserved_slots[i]++;
|
|
div_extra_slots--;
|
|
}
|
|
/* No more slots will be allocated to this queue during the current interval */
|
|
card->dynamic_slot_data.queue_stable[i] = TRUE;
|
|
unifi_trace(card->ospriv, UDBG5, "queue stable %d\n", i);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Redistribute max slots */
|
|
for (i = 0; i < UNIFI_NO_OF_TX_QS; i++)
|
|
{
|
|
reserved_slots = 0;
|
|
for (q = 0; q < UNIFI_NO_OF_TX_QS; q++)
|
|
{
|
|
if (i != q)
|
|
{
|
|
reserved_slots += card->dynamic_slot_data.from_host_reserved_slots[q];
|
|
}
|
|
}
|
|
|
|
card->dynamic_slot_data.from_host_max_slots[i] = num_data_slots - reserved_slots;
|
|
unifi_trace(card->ospriv, UDBG5, "queue %d reserved %d Max %d\n", i,
|
|
card->dynamic_slot_data.from_host_reserved_slots[i],
|
|
card->dynamic_slot_data.from_host_max_slots[i]);
|
|
}
|
|
|
|
func_exit();
|
|
}
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* CardClearFromHostDataSlot
|
|
*
|
|
* Clear a the given data slot, making it available again.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to Card object
|
|
* slot Index of the signal slot to clear.
|
|
*
|
|
* Returns:
|
|
* None.
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
void CardClearFromHostDataSlot(card_t *card, const s16 slot)
|
|
{
|
|
u8 queue = card->from_host_data[slot].queue;
|
|
const void *os_data_ptr = card->from_host_data[slot].bd.os_data_ptr;
|
|
|
|
func_enter();
|
|
|
|
if (card->from_host_data[slot].bd.data_length == 0)
|
|
{
|
|
unifi_warning(card->ospriv,
|
|
"Surprise: request to clear an already free FH data slot: %d\n",
|
|
slot);
|
|
func_exit();
|
|
return;
|
|
}
|
|
|
|
if (os_data_ptr == NULL)
|
|
{
|
|
unifi_warning(card->ospriv,
|
|
"Clearing FH data slot %d: has null payload, len=%d\n",
|
|
slot, card->from_host_data[slot].bd.data_length);
|
|
}
|
|
|
|
/* Free card->from_host_data[slot].bd.os_net_ptr here. */
|
|
/* Mark slot as free by setting length to 0. */
|
|
unifi_free_bulk_data(card, &card->from_host_data[slot].bd);
|
|
if (queue < UNIFI_NO_OF_TX_QS)
|
|
{
|
|
if (card->dynamic_slot_data.from_host_used_slots[queue] == 0)
|
|
{
|
|
unifi_error(card->ospriv, "Goofed up used slots q = %d used slots = %d\n",
|
|
queue,
|
|
card->dynamic_slot_data.from_host_used_slots[queue]);
|
|
}
|
|
else
|
|
{
|
|
card->dynamic_slot_data.from_host_used_slots[queue]--;
|
|
}
|
|
card->dynamic_slot_data.packets_txed[queue]++;
|
|
card->dynamic_slot_data.total_packets_txed++;
|
|
if (card->dynamic_slot_data.total_packets_txed >= card->dynamic_slot_data.packets_interval)
|
|
{
|
|
CardReassignDynamicReservation(card);
|
|
}
|
|
}
|
|
|
|
unifi_trace(card->ospriv, UDBG4, "CardClearFromHostDataSlot: slot %d recycled %p\n", slot, os_data_ptr);
|
|
|
|
func_exit();
|
|
} /* CardClearFromHostDataSlot() */
|
|
|
|
|
|
#ifdef CSR_WIFI_REQUEUE_PACKET_TO_HAL
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* CardClearFromHostDataSlotWithoutFreeingBulkData
|
|
*
|
|
* Clear the given data slot with out freeing the bulk data.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to Card object
|
|
* slot Index of the signal slot to clear.
|
|
*
|
|
* Returns:
|
|
* None.
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
void CardClearFromHostDataSlotWithoutFreeingBulkData(card_t *card, const s16 slot)
|
|
{
|
|
u8 queue = card->from_host_data[slot].queue;
|
|
|
|
/* Initialise the from_host data slot so it can be re-used,
|
|
* Set length field in from_host_data array to 0.
|
|
*/
|
|
UNIFI_INIT_BULK_DATA(&card->from_host_data[slot].bd);
|
|
|
|
queue = card->from_host_data[slot].queue;
|
|
|
|
if (queue < UNIFI_NO_OF_TX_QS)
|
|
{
|
|
if (card->dynamic_slot_data.from_host_used_slots[queue] == 0)
|
|
{
|
|
unifi_error(card->ospriv, "Goofed up used slots q = %d used slots = %d\n",
|
|
queue,
|
|
card->dynamic_slot_data.from_host_used_slots[queue]);
|
|
}
|
|
else
|
|
{
|
|
card->dynamic_slot_data.from_host_used_slots[queue]--;
|
|
}
|
|
card->dynamic_slot_data.packets_txed[queue]++;
|
|
card->dynamic_slot_data.total_packets_txed++;
|
|
if (card->dynamic_slot_data.total_packets_txed >=
|
|
card->dynamic_slot_data.packets_interval)
|
|
{
|
|
CardReassignDynamicReservation(card);
|
|
}
|
|
}
|
|
} /* CardClearFromHostDataSlotWithoutFreeingBulkData() */
|
|
|
|
|
|
#endif
|
|
|
|
u16 CardGetDataSlotSize(card_t *card)
|
|
{
|
|
return card->config_data.data_slot_size;
|
|
} /* CardGetDataSlotSize() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* CardGetFreeFromHostDataSlots
|
|
*
|
|
* Retrieve the number of from-host bulk data slots available.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to the card context struct
|
|
*
|
|
* Returns:
|
|
* Number of free from-host bulk data slots.
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
u16 CardGetFreeFromHostDataSlots(card_t *card)
|
|
{
|
|
u16 i, n = 0;
|
|
|
|
func_enter();
|
|
|
|
/* First two slots reserved for MLME */
|
|
for (i = 0; i < card->config_data.num_fromhost_data_slots; i++)
|
|
{
|
|
if (card->from_host_data[i].bd.data_length == 0)
|
|
{
|
|
/* Free slot */
|
|
n++;
|
|
}
|
|
}
|
|
|
|
func_exit();
|
|
return n;
|
|
} /* CardGetFreeFromHostDataSlots() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* CardAreAllFromHostDataSlotsEmpty
|
|
*
|
|
* Returns the state of from-host bulk data slots.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to the card context struct
|
|
*
|
|
* Returns:
|
|
* 1 The from-host bulk data slots are all empty (available).
|
|
* 0 Some or all the from-host bulk data slots are in use.
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
u16 CardAreAllFromHostDataSlotsEmpty(card_t *card)
|
|
{
|
|
u16 i;
|
|
|
|
for (i = 0; i < card->config_data.num_fromhost_data_slots; i++)
|
|
{
|
|
if (card->from_host_data[i].bd.data_length != 0)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
} /* CardGetFreeFromHostDataSlots() */
|
|
|
|
|
|
static CsrResult unifi_identify_hw(card_t *card)
|
|
{
|
|
func_enter();
|
|
|
|
card->chip_id = card->sdio_if->sdioId.cardId;
|
|
card->function = card->sdio_if->sdioId.sdioFunction;
|
|
card->sdio_io_block_size = card->sdio_if->blockSize;
|
|
|
|
/* If SDIO controller doesn't support byte mode CMD53, pad transfers to block sizes */
|
|
card->sdio_io_block_pad = (card->sdio_if->features & CSR_SDIO_FEATURE_BYTE_MODE)?FALSE : TRUE;
|
|
|
|
/*
|
|
* Setup the chip helper so that we can access the registers (and
|
|
* also tell what sub-type of HIP we should use).
|
|
*/
|
|
card->helper = ChipHelper_GetVersionSdio((u8)card->chip_id);
|
|
if (!card->helper)
|
|
{
|
|
unifi_error(card->ospriv, "Null ChipHelper\n");
|
|
}
|
|
|
|
unifi_info(card->ospriv, "Chip ID 0x%02X Function %u Block Size %u Name %s(%s)\n",
|
|
card->chip_id, card->function, card->sdio_io_block_size,
|
|
ChipHelper_MarketingName(card->helper),
|
|
ChipHelper_FriendlyName(card->helper));
|
|
|
|
func_exit();
|
|
return CSR_RESULT_SUCCESS;
|
|
} /* unifi_identify_hw() */
|
|
|
|
|
|
static CsrResult unifi_prepare_hw(card_t *card)
|
|
{
|
|
CsrResult r;
|
|
CsrResult csrResult;
|
|
enum unifi_host_state old_state = card->host_state;
|
|
|
|
func_enter();
|
|
|
|
r = unifi_identify_hw(card);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to identify hw\n");
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
|
|
unifi_trace(card->ospriv, UDBG1,
|
|
"%s mode SDIO\n", card->sdio_io_block_pad?"Block" : "Byte");
|
|
/*
|
|
* Chip must be a awake or blocks that are asleep may not get
|
|
* reset. We can only do this after we have read the chip_id.
|
|
*/
|
|
r = unifi_set_host_state(card, UNIFI_HOST_STATE_AWAKE);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
|
|
if (old_state == UNIFI_HOST_STATE_TORPID)
|
|
{
|
|
/* Ensure the initial clock rate is set; if a reset occured when the chip was
|
|
* TORPID, unifi_set_host_state() may have raised it to MAX.
|
|
*/
|
|
csrResult = CsrSdioMaxBusClockFrequencySet(card->sdio_if, UNIFI_SDIO_CLOCK_INIT_HZ);
|
|
if (csrResult != CSR_RESULT_SUCCESS)
|
|
{
|
|
r = ConvertCsrSdioToCsrHipResult(card, csrResult);
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
card->sdio_clock_speed = UNIFI_SDIO_CLOCK_INIT_HZ;
|
|
}
|
|
|
|
/*
|
|
* The WLAN function must be enabled to access MAILBOX2 and DEBUG_RST
|
|
* registers.
|
|
*/
|
|
csrResult = CsrSdioFunctionEnable(card->sdio_if);
|
|
if (csrResult == CSR_SDIO_RESULT_NO_DEVICE)
|
|
{
|
|
return CSR_WIFI_HIP_RESULT_NO_DEVICE;
|
|
}
|
|
if (csrResult != CSR_RESULT_SUCCESS)
|
|
{
|
|
r = ConvertCsrSdioToCsrHipResult(card, csrResult);
|
|
/* Can't enable WLAN function. Try resetting the SDIO block. */
|
|
unifi_error(card->ospriv, "Failed to re-enable function %d.\n", card->function);
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* Poke some registers to make sure the PLL has started,
|
|
* otherwise memory accesses are likely to fail.
|
|
*/
|
|
bootstrap_chip_hw(card);
|
|
|
|
/* Try to read the chip version from register. */
|
|
r = unifi_read_chip_version(card);
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
|
|
func_exit();
|
|
return CSR_RESULT_SUCCESS;
|
|
} /* unifi_prepare_hw() */
|
|
|
|
|
|
static CsrResult unifi_read_chip_version(card_t *card)
|
|
{
|
|
u32 gbl_chip_version;
|
|
CsrResult r;
|
|
u16 ver;
|
|
|
|
func_enter();
|
|
|
|
gbl_chip_version = ChipHelper_GBL_CHIP_VERSION(card->helper);
|
|
|
|
/* Try to read the chip version from register. */
|
|
if (gbl_chip_version != 0)
|
|
{
|
|
r = unifi_read_direct16(card, gbl_chip_version * 2, &ver);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to read GBL_CHIP_VERSION\n");
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
card->chip_version = ver;
|
|
}
|
|
else
|
|
{
|
|
unifi_info(card->ospriv, "Unknown Chip ID, cannot locate GBL_CHIP_VERSION\n");
|
|
r = CSR_RESULT_FAILURE;
|
|
}
|
|
|
|
unifi_info(card->ospriv, "Chip Version 0x%04X\n", card->chip_version);
|
|
|
|
func_exit_r(r);
|
|
return r;
|
|
} /* unifi_read_chip_version() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* unifi_reset_hardware
|
|
*
|
|
* Execute the UniFi reset sequence.
|
|
*
|
|
* Note: This may fail if the chip is going TORPID so retry at
|
|
* least once.
|
|
*
|
|
* Arguments:
|
|
* card - pointer to card context structure
|
|
*
|
|
* Returns:
|
|
* CSR_RESULT_SUCCESS on success, CSR error otherwise.
|
|
*
|
|
* Notes:
|
|
* Some platforms (e.g. Windows Vista) do not allow access to registers
|
|
* that are necessary for a software soft reset.
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
static CsrResult unifi_reset_hardware(card_t *card)
|
|
{
|
|
CsrResult r;
|
|
u16 new_block_size = UNIFI_IO_BLOCK_SIZE;
|
|
CsrResult csrResult;
|
|
|
|
func_enter();
|
|
|
|
/* Errors returned by unifi_prepare_hw() are not critical at this point */
|
|
r = unifi_prepare_hw(card);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
|
|
/* First try SDIO controller reset, which may power cycle the UniFi, assert
|
|
* its reset line, or not be implemented depending on the platform.
|
|
*/
|
|
unifi_info(card->ospriv, "Calling CsrSdioHardReset\n");
|
|
csrResult = CsrSdioHardReset(card->sdio_if);
|
|
if (csrResult == CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_info(card->ospriv, "CsrSdioHardReset succeeded on reseting UniFi\n");
|
|
r = unifi_prepare_hw(card);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "unifi_prepare_hw failed after hard reset\n");
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
}
|
|
else if (csrResult == CSR_SDIO_RESULT_NO_DEVICE)
|
|
{
|
|
return CSR_WIFI_HIP_RESULT_NO_DEVICE;
|
|
}
|
|
else
|
|
{
|
|
/* Falling back to software hard reset methods */
|
|
unifi_info(card->ospriv, "Falling back to software hard reset\n");
|
|
r = unifi_card_hard_reset(card);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "software hard reset failed\n");
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
|
|
/* If we fell back to unifi_card_hard_reset() methods, chip version may
|
|
* not have been read. (Note in the unlikely event that it is zero,
|
|
* it will be harmlessly read again)
|
|
*/
|
|
if (card->chip_version == 0)
|
|
{
|
|
r = unifi_read_chip_version(card);
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef CSR_WIFI_HIP_SDIO_BLOCK_SIZE
|
|
new_block_size = CSR_WIFI_HIP_SDIO_BLOCK_SIZE;
|
|
#endif
|
|
|
|
/* After hard reset, we need to restore the SDIO block size */
|
|
csrResult = CsrSdioBlockSizeSet(card->sdio_if, new_block_size);
|
|
r = ConvertCsrSdioToCsrHipResult(card, csrResult);
|
|
|
|
/* Warn if a different block size was achieved by the transport */
|
|
if (card->sdio_if->blockSize != new_block_size)
|
|
{
|
|
unifi_info(card->ospriv,
|
|
"Actually got block size %d\n", card->sdio_if->blockSize);
|
|
}
|
|
|
|
/* sdio_io_block_size always needs be updated from the achieved block size,
|
|
* as it is used by the OS layer to allocate memory in unifi_net_malloc().
|
|
* Controllers which don't support block mode (e.g. CSPI) will report a
|
|
* block size of zero.
|
|
*/
|
|
if (card->sdio_if->blockSize == 0)
|
|
{
|
|
unifi_info(card->ospriv, "Block size 0, block mode not available\n");
|
|
|
|
/* Set sdio_io_block_size to 1 so that unifi_net_data_malloc() has a
|
|
* sensible rounding value. Elsewhere padding will already be
|
|
* disabled because the controller supports byte mode.
|
|
*/
|
|
card->sdio_io_block_size = 1;
|
|
|
|
/* Controller features must declare support for byte mode */
|
|
if (!(card->sdio_if->features & CSR_SDIO_FEATURE_BYTE_MODE))
|
|
{
|
|
unifi_error(card->ospriv, "Requires byte mode\n");
|
|
r = CSR_WIFI_HIP_RESULT_INVALID_VALUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Padding will be enabled if CSR_SDIO_FEATURE_BYTE_MODE isn't set */
|
|
card->sdio_io_block_size = card->sdio_if->blockSize;
|
|
}
|
|
|
|
|
|
func_exit_r(r);
|
|
return r;
|
|
} /* unifi_reset_hardware() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* card_reset_method_io_enable
|
|
*
|
|
* Issue a hard reset to the hw writing the IO_ENABLE.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to Card object
|
|
*
|
|
* Returns:
|
|
* 0 on success,
|
|
* CSR_WIFI_HIP_RESULT_NO_DEVICE if the card was ejected
|
|
* CSR_RESULT_FAILURE if an SDIO error occurred or if a response
|
|
* was not seen in the expected time
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
static CsrResult card_reset_method_io_enable(card_t *card)
|
|
{
|
|
CsrResult r;
|
|
CsrResult csrResult;
|
|
|
|
func_enter();
|
|
|
|
/*
|
|
* This resets only function 1, so should be used in
|
|
* preference to the method below (CSR_FUNC_EN)
|
|
*/
|
|
unifi_trace(card->ospriv, UDBG1, "Hard reset (IO_ENABLE)\n");
|
|
|
|
csrResult = CsrSdioFunctionDisable(card->sdio_if);
|
|
if (csrResult == CSR_SDIO_RESULT_NO_DEVICE)
|
|
{
|
|
return CSR_WIFI_HIP_RESULT_NO_DEVICE;
|
|
}
|
|
if (csrResult != CSR_RESULT_SUCCESS)
|
|
{
|
|
r = ConvertCsrSdioToCsrHipResult(card, csrResult);
|
|
unifi_warning(card->ospriv, "SDIO error writing IO_ENABLE: %d\n", r);
|
|
}
|
|
else
|
|
{
|
|
/* Delay here to let the reset take affect. */
|
|
CsrThreadSleep(RESET_SETTLE_DELAY);
|
|
|
|
r = card_wait_for_unifi_to_disable(card);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
|
|
if (r == CSR_RESULT_SUCCESS)
|
|
{
|
|
r = card_wait_for_unifi_to_reset(card);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_trace(card->ospriv, UDBG1, "Hard reset (CSR_FUNC_EN)\n");
|
|
|
|
r = sdio_write_f0(card, SDIO_CSR_FUNC_EN, 0);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_warning(card->ospriv, "SDIO error writing SDIO_CSR_FUNC_EN: %d\n", r);
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
else
|
|
{
|
|
/* Delay here to let the reset take affect. */
|
|
CsrThreadSleep(RESET_SETTLE_DELAY);
|
|
|
|
r = card_wait_for_unifi_to_reset(card);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_warning(card->ospriv, "card_reset_method_io_enable failed to reset UniFi\n");
|
|
}
|
|
|
|
func_exit();
|
|
return r;
|
|
} /* card_reset_method_io_enable() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* card_reset_method_dbg_reset
|
|
*
|
|
* Issue a hard reset to the hw writing the DBG_RESET.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to Card object
|
|
*
|
|
* Returns:
|
|
* CSR_RESULT_SUCCESS on success,
|
|
* CSR_WIFI_HIP_RESULT_NO_DEVICE if the card was ejected
|
|
* CSR_RESULT_FAILURE if an SDIO error occurred or if a response
|
|
* was not seen in the expected time
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
static CsrResult card_reset_method_dbg_reset(card_t *card)
|
|
{
|
|
CsrResult r;
|
|
|
|
func_enter();
|
|
|
|
/*
|
|
* Prepare UniFi for h/w reset
|
|
*/
|
|
if (card->host_state == UNIFI_HOST_STATE_TORPID)
|
|
{
|
|
r = unifi_set_host_state(card, UNIFI_HOST_STATE_DROWSY);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to set UNIFI_HOST_STATE_DROWSY\n");
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
CsrThreadSleep(5);
|
|
}
|
|
|
|
r = unifi_card_stop_processor(card, UNIFI_PROC_BOTH);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Can't stop processors\n");
|
|
func_exit();
|
|
return r;
|
|
}
|
|
|
|
unifi_trace(card->ospriv, UDBG1, "Hard reset (DBG_RESET)\n");
|
|
|
|
/*
|
|
* This register write may fail. The debug reset resets
|
|
* parts of the Function 0 sections of the chip, and
|
|
* therefore the response cannot be sent back to the host.
|
|
*/
|
|
r = unifi_write_direct_8_or_16(card, ChipHelper_DBG_RESET(card->helper) * 2, 1);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_warning(card->ospriv, "SDIO error writing DBG_RESET: %d\n", r);
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
|
|
/* Delay here to let the reset take affect. */
|
|
CsrThreadSleep(RESET_SETTLE_DELAY);
|
|
|
|
r = card_wait_for_unifi_to_reset(card);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_warning(card->ospriv, "card_reset_method_dbg_reset failed to reset UniFi\n");
|
|
}
|
|
|
|
func_exit();
|
|
return r;
|
|
} /* card_reset_method_dbg_reset() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* unifi_card_hard_reset
|
|
*
|
|
* Issue reset to hardware, by writing to registers on the card.
|
|
* Power to the card is preserved.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to Card object
|
|
*
|
|
* Returns:
|
|
* CSR_RESULT_SUCCESS on success,
|
|
* CSR_WIFI_HIP_RESULT_NO_DEVICE if the card was ejected
|
|
* CSR_RESULT_FAILURE if an SDIO error occurred or if a response
|
|
* was not seen in the expected time
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
CsrResult unifi_card_hard_reset(card_t *card)
|
|
{
|
|
CsrResult r;
|
|
const struct chip_helper_reset_values *init_data;
|
|
u32 chunks;
|
|
|
|
func_enter();
|
|
|
|
/* Clear cache of page registers */
|
|
card->proc_select = (u32)(-1);
|
|
card->dmem_page = (u32)(-1);
|
|
card->pmem_page = (u32)(-1);
|
|
|
|
/*
|
|
* We need to have a valid card->helper before we use software hard reset.
|
|
* If unifi_identify_hw() fails to get the card ID, it probably means
|
|
* that there is no way to talk to the h/w.
|
|
*/
|
|
r = unifi_identify_hw(card);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "unifi_card_hard_reset failed to identify h/w\n");
|
|
func_exit();
|
|
return r;
|
|
}
|
|
|
|
/* Search for some reset code. */
|
|
chunks = ChipHelper_HostResetSequence(card->helper, &init_data);
|
|
if (chunks != 0)
|
|
{
|
|
unifi_error(card->ospriv,
|
|
"Hard reset (Code download) is unsupported\n");
|
|
|
|
func_exit_r(CSR_RESULT_FAILURE);
|
|
return CSR_RESULT_FAILURE;
|
|
}
|
|
|
|
if (card->chip_id > SDIO_CARD_ID_UNIFI_2)
|
|
{
|
|
/* The HIP spec considers this a bus-specific reset.
|
|
* This resets only function 1, so should be used in
|
|
* preference to the method below (CSR_FUNC_EN)
|
|
* If this method fails, it means that the f/w is probably
|
|
* not running. In this case, try the DBG_RESET method.
|
|
*/
|
|
r = card_reset_method_io_enable(card);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r == CSR_RESULT_SUCCESS)
|
|
{
|
|
func_exit();
|
|
return r;
|
|
}
|
|
}
|
|
|
|
/* Software hard reset */
|
|
r = card_reset_method_dbg_reset(card);
|
|
|
|
func_exit_r(r);
|
|
return r;
|
|
} /* unifi_card_hard_reset() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
*
|
|
* CardGenInt
|
|
*
|
|
* Prod the card.
|
|
* This function causes an internal interrupt to be raised in the
|
|
* UniFi chip. It is used to signal the firmware that some action has
|
|
* been completed.
|
|
* The UniFi Host Interface asks that the value used increments for
|
|
* debugging purposes.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to Card object
|
|
*
|
|
* Returns:
|
|
* CSR_RESULT_SUCCESS on success,
|
|
* CSR_WIFI_HIP_RESULT_NO_DEVICE if the card was ejected
|
|
* CSR_RESULT_FAILURE if an SDIO error occurred or if a response
|
|
* was not seen in the expected time
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
CsrResult CardGenInt(card_t *card)
|
|
{
|
|
CsrResult r;
|
|
|
|
func_enter();
|
|
|
|
if (card->chip_id > SDIO_CARD_ID_UNIFI_2)
|
|
{
|
|
r = sdio_write_f0(card, SDIO_CSR_FROM_HOST_SCRATCH0,
|
|
(u8)card->unifi_interrupt_seq);
|
|
}
|
|
else
|
|
{
|
|
r = unifi_write_direct_8_or_16(card,
|
|
ChipHelper_SHARED_IO_INTERRUPT(card->helper) * 2,
|
|
(u8)card->unifi_interrupt_seq);
|
|
}
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "SDIO error writing UNIFI_SHARED_IO_INTERRUPT: %d\n", r);
|
|
func_exit_r(r);
|
|
return r;
|
|
}
|
|
|
|
card->unifi_interrupt_seq++;
|
|
|
|
func_exit();
|
|
return CSR_RESULT_SUCCESS;
|
|
} /* CardGenInt() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* CardEnableInt
|
|
*
|
|
* Enable the outgoing SDIO interrupt from UniFi to the host.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to Card object
|
|
*
|
|
* Returns:
|
|
* CSR_RESULT_SUCCESS on success,
|
|
* CSR_WIFI_HIP_RESULT_NO_DEVICE if the card was ejected
|
|
* CSR_RESULT_FAILURE if an SDIO error occurred,
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
CsrResult CardEnableInt(card_t *card)
|
|
{
|
|
CsrResult r;
|
|
u8 int_enable;
|
|
|
|
r = sdio_read_f0(card, SDIO_INT_ENABLE, &int_enable);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "SDIO error reading SDIO_INT_ENABLE\n");
|
|
return r;
|
|
}
|
|
|
|
int_enable |= (1 << card->function) | UNIFI_SD_INT_ENABLE_IENM;
|
|
|
|
r = sdio_write_f0(card, SDIO_INT_ENABLE, int_enable);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "SDIO error writing SDIO_INT_ENABLE\n");
|
|
return r;
|
|
}
|
|
|
|
return CSR_RESULT_SUCCESS;
|
|
} /* CardEnableInt() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* CardDisableInt
|
|
*
|
|
* Disable the outgoing SDIO interrupt from UniFi to the host.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to Card object
|
|
*
|
|
* Returns:
|
|
* CSR_RESULT_SUCCESS on success,
|
|
* CSR_WIFI_HIP_RESULT_NO_DEVICE if the card was ejected
|
|
* CSR_RESULT_FAILURE if an SDIO error occurred,
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
CsrResult CardDisableInt(card_t *card)
|
|
{
|
|
CsrResult r;
|
|
u8 int_enable;
|
|
|
|
r = sdio_read_f0(card, SDIO_INT_ENABLE, &int_enable);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "SDIO error reading SDIO_INT_ENABLE\n");
|
|
return r;
|
|
}
|
|
|
|
int_enable &= ~(1 << card->function);
|
|
|
|
r = sdio_write_f0(card, SDIO_INT_ENABLE, int_enable);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "SDIO error writing SDIO_INT_ENABLE\n");
|
|
return r;
|
|
}
|
|
|
|
return CSR_RESULT_SUCCESS;
|
|
} /* CardDisableInt() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* CardPendingInt
|
|
*
|
|
* Determine whether UniFi is currently asserting the SDIO interrupt
|
|
* request.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to Card object
|
|
* pintr Pointer to location to write interrupt status,
|
|
* TRUE if interrupt pending,
|
|
* FALSE if no interrupt pending.
|
|
* Returns:
|
|
* CSR_RESULT_SUCCESS interrupt status read successfully
|
|
* CSR_WIFI_HIP_RESULT_NO_DEVICE if the card was ejected
|
|
* CSR_RESULT_FAILURE if an SDIO error occurred,
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
CsrResult CardPendingInt(card_t *card, u8 *pintr)
|
|
{
|
|
CsrResult r;
|
|
u8 pending;
|
|
|
|
*pintr = FALSE;
|
|
|
|
r = sdio_read_f0(card, SDIO_INT_PENDING, &pending);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "SDIO error reading SDIO_INT_PENDING\n");
|
|
return r;
|
|
}
|
|
|
|
*pintr = (pending & (1 << card->function))?TRUE : FALSE;
|
|
|
|
return CSR_RESULT_SUCCESS;
|
|
} /* CardPendingInt() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* CardClearInt
|
|
*
|
|
* Clear the UniFi SDIO interrupt request.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to Card object
|
|
*
|
|
* Returns:
|
|
* CSR_RESULT_SUCCESS if pending interrupt was cleared, or no pending interrupt.
|
|
* CSR_WIFI_HIP_RESULT_NO_DEVICE if the card was ejected
|
|
* CSR_RESULT_FAILURE if an SDIO error occurred,
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
CsrResult CardClearInt(card_t *card)
|
|
{
|
|
CsrResult r;
|
|
u8 intr;
|
|
|
|
if (card->chip_id > SDIO_CARD_ID_UNIFI_2)
|
|
{
|
|
/* CardPendingInt() sets intr, if there is a pending interrupt */
|
|
r = CardPendingInt(card, &intr);
|
|
if (intr == FALSE)
|
|
{
|
|
return r;
|
|
}
|
|
|
|
r = sdio_write_f0(card, SDIO_CSR_HOST_INT_CLEAR, 1);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "SDIO error writing SDIO_CSR_HOST_INT_CLEAR\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
r = unifi_write_direct_8_or_16(card,
|
|
ChipHelper_SDIO_HOST_INT(card->helper) * 2,
|
|
0);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "SDIO error writing UNIFI_SDIO_HOST_INT\n");
|
|
}
|
|
}
|
|
|
|
return r;
|
|
} /* CardClearInt() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* CardIntEnabled
|
|
*
|
|
* Determine whether UniFi is currently asserting the SDIO interrupt
|
|
* request.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to Card object
|
|
* enabled Pointer to location to write interrupt enable status,
|
|
* TRUE if interrupts enabled,
|
|
* FALSE if interupts disabled.
|
|
*
|
|
* Returns:
|
|
* CSR_WIFI_HIP_RESULT_NO_DEVICE if the card was ejected
|
|
* CSR_RESULT_FAILURE if an SDIO error occurred,
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
CsrResult CardIntEnabled(card_t *card, u8 *enabled)
|
|
{
|
|
CsrResult r;
|
|
u8 int_enable;
|
|
|
|
r = sdio_read_f0(card, SDIO_INT_ENABLE, &int_enable);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "SDIO error reading SDIO_INT_ENABLE\n");
|
|
return r;
|
|
}
|
|
|
|
*enabled = (int_enable & (1 << card->function))?TRUE : FALSE;
|
|
|
|
return CSR_RESULT_SUCCESS;
|
|
} /* CardIntEnabled() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* CardWriteBulkData
|
|
* Allocate slot in the pending bulkdata arrays and assign it to a signal's
|
|
* bulkdata reference. The slot is then ready for UniFi's bulkdata commands
|
|
* to transfer the data to/from the host.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to Card object
|
|
* csptr Pending signal pointer, including bulkdata ref
|
|
* queue Traffic queue that this signal is using
|
|
*
|
|
* Returns:
|
|
* CSR_RESULT_SUCCESS if a free slot was assigned
|
|
* CSR_RESULT_FAILURE if no slot was available
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
CsrResult CardWriteBulkData(card_t *card, card_signal_t *csptr, unifi_TrafficQueue queue)
|
|
{
|
|
u16 i, slots[UNIFI_MAX_DATA_REFERENCES], j = 0;
|
|
u8 *packed_sigptr, num_slots_required = 0;
|
|
bulk_data_desc_t *bulkdata = csptr->bulkdata;
|
|
s16 h, nslots;
|
|
|
|
func_enter();
|
|
|
|
/* Count the number of slots required */
|
|
for (i = 0; i < UNIFI_MAX_DATA_REFERENCES; i++)
|
|
{
|
|
if (bulkdata[i].data_length != 0)
|
|
{
|
|
num_slots_required++;
|
|
}
|
|
}
|
|
|
|
/* Get the slot numbers */
|
|
if (num_slots_required != 0)
|
|
{
|
|
/* Last 2 slots for MLME */
|
|
if (queue == UNIFI_TRAFFIC_Q_MLME)
|
|
{
|
|
h = card->config_data.num_fromhost_data_slots - UNIFI_RESERVED_COMMAND_SLOTS;
|
|
for (i = 0; i < card->config_data.num_fromhost_data_slots; i++)
|
|
{
|
|
if (card->from_host_data[h].bd.data_length == 0)
|
|
{
|
|
/* Free data slot, claim it */
|
|
slots[j++] = h;
|
|
if (j == num_slots_required)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (++h >= card->config_data.num_fromhost_data_slots)
|
|
{
|
|
h = 0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (card->dynamic_slot_data.from_host_used_slots[queue]
|
|
< card->dynamic_slot_data.from_host_max_slots[queue])
|
|
{
|
|
/* Data commands get a free slot only after a few checks */
|
|
nslots = card->config_data.num_fromhost_data_slots - UNIFI_RESERVED_COMMAND_SLOTS;
|
|
|
|
h = card->from_host_data_head;
|
|
|
|
for (i = 0; i < nslots; i++)
|
|
{
|
|
if (card->from_host_data[h].bd.data_length == 0)
|
|
{
|
|
/* Free data slot, claim it */
|
|
slots[j++] = h;
|
|
if (j == num_slots_required)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (++h >= nslots)
|
|
{
|
|
h = 0;
|
|
}
|
|
}
|
|
card->from_host_data_head = h;
|
|
}
|
|
}
|
|
|
|
/* Required number of slots are not available, bail out */
|
|
if (j != num_slots_required)
|
|
{
|
|
unifi_trace(card->ospriv, UDBG5, "CardWriteBulkData: didn't find free slot/s\n");
|
|
|
|
/* If we haven't already reached the stable state we can ask for reservation */
|
|
if ((queue != UNIFI_TRAFFIC_Q_MLME) && (card->dynamic_slot_data.queue_stable[queue] == FALSE))
|
|
{
|
|
CardCheckDynamicReservation(card, queue);
|
|
}
|
|
|
|
for (i = 0; i < card->config_data.num_fromhost_data_slots; i++)
|
|
{
|
|
unifi_trace(card->ospriv, UDBG5, "fh data slot %d: %d\n", i, card->from_host_data[i].bd.data_length);
|
|
}
|
|
func_exit();
|
|
return CSR_RESULT_FAILURE;
|
|
}
|
|
}
|
|
|
|
packed_sigptr = csptr->sigbuf;
|
|
|
|
/* Fill in the slots with data */
|
|
j = 0;
|
|
for (i = 0; i < UNIFI_MAX_DATA_REFERENCES; i++)
|
|
{
|
|
if (bulkdata[i].data_length == 0)
|
|
{
|
|
/* Zero-out the DATAREF in the signal */
|
|
SET_PACKED_DATAREF_SLOT(packed_sigptr, i, 0);
|
|
SET_PACKED_DATAREF_LEN(packed_sigptr, i, 0);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* Fill in the slot number in the SIGNAL structure but
|
|
* preserve the offset already in there
|
|
*/
|
|
SET_PACKED_DATAREF_SLOT(packed_sigptr, i, slots[j] | (((u16)packed_sigptr[SIZEOF_SIGNAL_HEADER + (i * SIZEOF_DATAREF) + 1]) << 8));
|
|
SET_PACKED_DATAREF_LEN(packed_sigptr, i, bulkdata[i].data_length);
|
|
|
|
/* Do not copy the data, just store the information to them */
|
|
card->from_host_data[slots[j]].bd.os_data_ptr = bulkdata[i].os_data_ptr;
|
|
card->from_host_data[slots[j]].bd.os_net_buf_ptr = bulkdata[i].os_net_buf_ptr;
|
|
card->from_host_data[slots[j]].bd.data_length = bulkdata[i].data_length;
|
|
card->from_host_data[slots[j]].bd.net_buf_length = bulkdata[i].net_buf_length;
|
|
card->from_host_data[slots[j]].queue = queue;
|
|
|
|
unifi_trace(card->ospriv, UDBG4, "CardWriteBulkData sig=0x%x, fh slot %d = %p\n",
|
|
GET_SIGNAL_ID(packed_sigptr), i, bulkdata[i].os_data_ptr);
|
|
|
|
/* Sanity-check that the bulk data desc being assigned to the slot
|
|
* actually has a payload.
|
|
*/
|
|
if (!bulkdata[i].os_data_ptr)
|
|
{
|
|
unifi_error(card->ospriv, "Assign null os_data_ptr (len=%d) fh slot %d, i=%d, q=%d, sig=0x%x",
|
|
bulkdata[i].data_length, slots[j], i, queue, GET_SIGNAL_ID(packed_sigptr));
|
|
}
|
|
|
|
j++;
|
|
if (queue < UNIFI_NO_OF_TX_QS)
|
|
{
|
|
card->dynamic_slot_data.from_host_used_slots[queue]++;
|
|
}
|
|
}
|
|
}
|
|
|
|
func_exit();
|
|
|
|
return CSR_RESULT_SUCCESS;
|
|
} /* CardWriteBulkData() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* card_find_data_slot
|
|
*
|
|
* Dereference references to bulk data slots into pointers to real data.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to the card struct.
|
|
* slot Slot number from a signal structure
|
|
*
|
|
* Returns:
|
|
* Pointer to entry in bulk_data_slot array.
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
bulk_data_desc_t* card_find_data_slot(card_t *card, s16 slot)
|
|
{
|
|
s16 sn;
|
|
bulk_data_desc_t *bd;
|
|
|
|
sn = slot & 0x7FFF;
|
|
|
|
/* ?? check sanity of slot number ?? */
|
|
|
|
if (slot & SLOT_DIR_TO_HOST)
|
|
{
|
|
bd = &card->to_host_data[sn];
|
|
}
|
|
else
|
|
{
|
|
bd = &card->from_host_data[sn].bd;
|
|
}
|
|
|
|
return bd;
|
|
} /* card_find_data_slot() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* firmware_present_in_flash
|
|
*
|
|
* Probe for external Flash that looks like it might contain firmware.
|
|
*
|
|
* If Flash is not present, reads always return 0x0008.
|
|
* If Flash is present, but empty, reads return 0xFFFF.
|
|
* Anything else is considered to be firmware.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to card struct
|
|
*
|
|
* Returns:
|
|
* CSR_RESULT_SUCCESS firmware is present in ROM or flash
|
|
* CSR_WIFI_HIP_RESULT_NOT_FOUND firmware is not present in ROM or flash
|
|
* CSR_WIFI_HIP_RESULT_NO_DEVICE if the card was ejected
|
|
* CSR_RESULT_FAILURE if an SDIO error occurred
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
static CsrResult firmware_present_in_flash(card_t *card)
|
|
{
|
|
CsrResult r;
|
|
u16 m1, m5;
|
|
|
|
if (ChipHelper_HasRom(card->helper))
|
|
{
|
|
return CSR_RESULT_SUCCESS;
|
|
}
|
|
if (!ChipHelper_HasFlash(card->helper))
|
|
{
|
|
return CSR_WIFI_HIP_RESULT_NOT_FOUND;
|
|
}
|
|
|
|
/*
|
|
* Examine the Flash locations that are the power-on default reset
|
|
* vectors of the XAP processors.
|
|
* These are words 1 and 5 in Flash.
|
|
*/
|
|
r = unifi_card_read16(card, UNIFI_MAKE_GP(EXT_FLASH, 2), &m1);
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
return r;
|
|
}
|
|
|
|
r = unifi_card_read16(card, UNIFI_MAKE_GP(EXT_FLASH, 10), &m5);
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
return r;
|
|
}
|
|
|
|
/* Check for uninitialised/missing flash */
|
|
if ((m1 == 0x0008) || (m1 == 0xFFFF) ||
|
|
(m1 == 0x0004) || (m5 == 0x0004) ||
|
|
(m5 == 0x0008) || (m5 == 0xFFFF))
|
|
{
|
|
return CSR_WIFI_HIP_RESULT_NOT_FOUND;
|
|
}
|
|
|
|
return CSR_RESULT_SUCCESS;
|
|
} /* firmware_present_in_flash() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* bootstrap_chip_hw
|
|
*
|
|
* Perform chip specific magic to "Get It Working" TM. This will
|
|
* increase speed of PLLs in analogue and maybe enable some
|
|
* on-chip regulators.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to card struct
|
|
*
|
|
* Returns:
|
|
* None.
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
static void bootstrap_chip_hw(card_t *card)
|
|
{
|
|
const struct chip_helper_init_values *vals;
|
|
u32 i, len;
|
|
void *sdio = card->sdio_if;
|
|
CsrResult csrResult;
|
|
|
|
len = ChipHelper_ClockStartupSequence(card->helper, &vals);
|
|
if (len != 0)
|
|
{
|
|
for (i = 0; i < len; i++)
|
|
{
|
|
csrResult = CsrSdioWrite16(sdio, vals[i].addr * 2, vals[i].value);
|
|
if (csrResult != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_warning(card->ospriv, "Failed to write bootstrap value %d\n", i);
|
|
/* Might not be fatal */
|
|
}
|
|
|
|
CsrThreadSleep(1);
|
|
}
|
|
}
|
|
} /* bootstrap_chip_hw() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* unifi_card_stop_processor
|
|
*
|
|
* Stop the UniFi XAP processors.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to card struct
|
|
* which One of UNIFI_PROC_MAC, UNIFI_PROC_PHY, UNIFI_PROC_BOTH
|
|
*
|
|
* Returns:
|
|
* CSR_RESULT_SUCCESS if successful, or CSR error code
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
CsrResult unifi_card_stop_processor(card_t *card, enum unifi_dbg_processors_select which)
|
|
{
|
|
CsrResult r = CSR_RESULT_SUCCESS;
|
|
u8 status;
|
|
s16 retry = 100;
|
|
|
|
while (retry--)
|
|
{
|
|
/* Select both XAPs */
|
|
r = unifi_set_proc_select(card, which);
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
break;
|
|
}
|
|
|
|
/* Stop processors */
|
|
r = unifi_write_direct16(card, ChipHelper_DBG_EMU_CMD(card->helper) * 2, 2);
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
break;
|
|
}
|
|
|
|
/* Read status */
|
|
r = unifi_read_direct_8_or_16(card,
|
|
ChipHelper_DBG_HOST_STOP_STATUS(card->helper) * 2,
|
|
&status);
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if ((status & 1) == 1)
|
|
{
|
|
/* Success! */
|
|
return CSR_RESULT_SUCCESS;
|
|
}
|
|
|
|
/* Processors didn't stop, try again */
|
|
}
|
|
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
/* An SDIO error occurred */
|
|
unifi_error(card->ospriv, "Failed to stop processors: SDIO error\n");
|
|
}
|
|
else
|
|
{
|
|
/* If we reach here, we didn't the status in time. */
|
|
unifi_error(card->ospriv, "Failed to stop processors: timeout waiting for stopped status\n");
|
|
r = CSR_RESULT_FAILURE;
|
|
}
|
|
|
|
return r;
|
|
} /* unifi_card_stop_processor() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* card_start_processor
|
|
*
|
|
* Start the UniFi XAP processors.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to card struct
|
|
* which One of UNIFI_PROC_MAC, UNIFI_PROC_PHY, UNIFI_PROC_BOTH
|
|
*
|
|
* Returns:
|
|
* CSR_RESULT_SUCCESS or CSR error code
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
CsrResult card_start_processor(card_t *card, enum unifi_dbg_processors_select which)
|
|
{
|
|
CsrResult r;
|
|
|
|
/* Select both XAPs */
|
|
r = unifi_set_proc_select(card, which);
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "unifi_set_proc_select failed: %d.\n", r);
|
|
return r;
|
|
}
|
|
|
|
|
|
r = unifi_write_direct_8_or_16(card,
|
|
ChipHelper_DBG_EMU_CMD(card->helper) * 2, 8);
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
return r;
|
|
}
|
|
|
|
r = unifi_write_direct_8_or_16(card,
|
|
ChipHelper_DBG_EMU_CMD(card->helper) * 2, 0);
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
return r;
|
|
}
|
|
|
|
return CSR_RESULT_SUCCESS;
|
|
} /* card_start_processor() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* unifi_set_interrupt_mode
|
|
*
|
|
* Configure the interrupt processing mode used by the HIP
|
|
*
|
|
* Arguments:
|
|
* card Pointer to card struct
|
|
* mode Interrupt mode to apply
|
|
*
|
|
* Returns:
|
|
* None
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
void unifi_set_interrupt_mode(card_t *card, u32 mode)
|
|
{
|
|
if (mode == CSR_WIFI_INTMODE_RUN_BH_ONCE)
|
|
{
|
|
unifi_info(card->ospriv, "Scheduled interrupt mode");
|
|
}
|
|
card->intmode = mode;
|
|
} /* unifi_set_interrupt_mode() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* unifi_start_processors
|
|
*
|
|
* Start all UniFi XAP processors.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to card struct
|
|
*
|
|
* Returns:
|
|
* CSR_RESULT_SUCCESS on success, CSR error code on error
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
CsrResult unifi_start_processors(card_t *card)
|
|
{
|
|
return card_start_processor(card, UNIFI_PROC_BOTH);
|
|
} /* unifi_start_processors() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* unifi_request_max_sdio_clock
|
|
*
|
|
* Requests that the maximum SDIO clock rate is set at the next suitable
|
|
* opportunity (e.g. when the BH next runs, so as not to interfere with
|
|
* any current operation).
|
|
*
|
|
* Arguments:
|
|
* card Pointer to card struct
|
|
*
|
|
* Returns:
|
|
* None
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
void unifi_request_max_sdio_clock(card_t *card)
|
|
{
|
|
card->request_max_clock = 1;
|
|
} /* unifi_request_max_sdio_clock() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* unifi_set_host_state
|
|
*
|
|
* Set the host deep-sleep state.
|
|
*
|
|
* If transitioning to TORPID, the SDIO driver will be notified
|
|
* that the SD bus will be unused (idle) and conversely, when
|
|
* transitioning from TORPID that the bus will be used (active).
|
|
*
|
|
* Arguments:
|
|
* card Pointer to card struct
|
|
* state New deep-sleep state.
|
|
*
|
|
* Returns:
|
|
* CSR_RESULT_SUCCESS on success
|
|
* CSR_WIFI_HIP_RESULT_NO_DEVICE if the card was ejected
|
|
* CSR_RESULT_FAILURE if an SDIO error occurred
|
|
*
|
|
* Notes:
|
|
* We need to reduce the SDIO clock speed before trying to wake up the
|
|
* chip. Actually, in the implementation below we reduce the clock speed
|
|
* not just before we try to wake up the chip, but when we put the chip to
|
|
* deep sleep. This means that if the f/w wakes up on its' own, we waste
|
|
* a reduce/increace cycle. However, trying to eliminate this overhead is
|
|
* proved difficult, as the current state machine in the HIP lib does at
|
|
* least a CMD52 to disable the interrupts before we configure the host
|
|
* state.
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
CsrResult unifi_set_host_state(card_t *card, enum unifi_host_state state)
|
|
{
|
|
CsrResult r = CSR_RESULT_SUCCESS;
|
|
CsrResult csrResult;
|
|
static const char *const states[] = {
|
|
"AWAKE", "DROWSY", "TORPID"
|
|
};
|
|
static const u8 state_csr_host_wakeup[] = {
|
|
1, 3, 0
|
|
};
|
|
static const u8 state_io_abort[] = {
|
|
0, 2, 3
|
|
};
|
|
|
|
unifi_trace(card->ospriv, UDBG4, "State %s to %s\n",
|
|
states[card->host_state], states[state]);
|
|
|
|
if (card->host_state == UNIFI_HOST_STATE_TORPID)
|
|
{
|
|
CsrSdioFunctionActive(card->sdio_if);
|
|
}
|
|
|
|
/* Write the new state to UniFi. */
|
|
if (card->chip_id > SDIO_CARD_ID_UNIFI_2)
|
|
{
|
|
r = sdio_write_f0(card, SDIO_CSR_HOST_WAKEUP,
|
|
(u8)((card->function << 4) | state_csr_host_wakeup[state]));
|
|
}
|
|
else
|
|
{
|
|
r = sdio_write_f0(card, SDIO_IO_ABORT, state_io_abort[state]);
|
|
}
|
|
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to write UniFi deep sleep state\n");
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* If the chip was in state TORPID then we can now increase
|
|
* the maximum bus clock speed.
|
|
*/
|
|
if (card->host_state == UNIFI_HOST_STATE_TORPID)
|
|
{
|
|
csrResult = CsrSdioMaxBusClockFrequencySet(card->sdio_if,
|
|
UNIFI_SDIO_CLOCK_MAX_HZ);
|
|
r = ConvertCsrSdioToCsrHipResult(card, csrResult);
|
|
/* Non-fatal error */
|
|
if (r != CSR_RESULT_SUCCESS && r != CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
unifi_warning(card->ospriv,
|
|
"Failed to increase the SDIO clock speed\n");
|
|
}
|
|
else
|
|
{
|
|
card->sdio_clock_speed = UNIFI_SDIO_CLOCK_MAX_HZ;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Cache the current state in the card structure to avoid
|
|
* unnecessary SDIO reads.
|
|
*/
|
|
card->host_state = state;
|
|
|
|
if (state == UNIFI_HOST_STATE_TORPID)
|
|
{
|
|
/*
|
|
* If the chip is now in state TORPID then we must now decrease
|
|
* the maximum bus clock speed.
|
|
*/
|
|
csrResult = CsrSdioMaxBusClockFrequencySet(card->sdio_if,
|
|
UNIFI_SDIO_CLOCK_SAFE_HZ);
|
|
r = ConvertCsrSdioToCsrHipResult(card, csrResult);
|
|
if (r != CSR_RESULT_SUCCESS && r != CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
unifi_warning(card->ospriv,
|
|
"Failed to decrease the SDIO clock speed\n");
|
|
}
|
|
else
|
|
{
|
|
card->sdio_clock_speed = UNIFI_SDIO_CLOCK_SAFE_HZ;
|
|
}
|
|
CsrSdioFunctionIdle(card->sdio_if);
|
|
}
|
|
}
|
|
|
|
return r;
|
|
} /* unifi_set_host_state() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* unifi_card_info
|
|
*
|
|
* Update the card information data structure
|
|
*
|
|
* Arguments:
|
|
* card Pointer to card struct
|
|
* card_info Pointer to info structure to update
|
|
*
|
|
* Returns:
|
|
* None
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
void unifi_card_info(card_t *card, card_info_t *card_info)
|
|
{
|
|
card_info->chip_id = card->chip_id;
|
|
card_info->chip_version = card->chip_version;
|
|
card_info->fw_build = card->build_id;
|
|
card_info->fw_hip_version = card->config_data.version;
|
|
card_info->sdio_block_size = card->sdio_io_block_size;
|
|
} /* unifi_card_info() */
|
|
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------
|
|
* unifi_check_io_status
|
|
*
|
|
* Check UniFi for spontaneous reset and pending interrupt.
|
|
*
|
|
* Arguments:
|
|
* card Pointer to card struct
|
|
* status Pointer to location to write chip status:
|
|
* 0 if UniFi is running, and no interrupt pending
|
|
* 1 if UniFi has spontaneously reset
|
|
* 2 if there is a pending interrupt
|
|
* Returns:
|
|
* CSR_RESULT_SUCCESS if OK, or CSR error
|
|
* ---------------------------------------------------------------------------
|
|
*/
|
|
CsrResult unifi_check_io_status(card_t *card, s32 *status)
|
|
{
|
|
u8 io_en;
|
|
CsrResult r;
|
|
u8 pending;
|
|
|
|
*status = 0;
|
|
|
|
r = sdio_read_f0(card, SDIO_IO_ENABLE, &io_en);
|
|
if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE)
|
|
{
|
|
return r;
|
|
}
|
|
if (r != CSR_RESULT_SUCCESS)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to read SDIO_IO_ENABLE to check for spontaneous reset\n");
|
|
return r;
|
|
}
|
|
|
|
if ((io_en & (1 << card->function)) == 0)
|
|
{
|
|
s32 fw_count;
|
|
*status = 1;
|
|
unifi_error(card->ospriv, "UniFi has spontaneously reset.\n");
|
|
|
|
/*
|
|
* These reads are very likely to fail. We want to know if the function is really
|
|
* disabled or the SDIO driver just returns rubbish.
|
|
*/
|
|
fw_count = unifi_read_shared_count(card, card->sdio_ctrl_addr + 4);
|
|
if (fw_count < 0)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to read to-host sig written count\n");
|
|
}
|
|
else
|
|
{
|
|
unifi_error(card->ospriv, "thsw: %u (driver thinks is %u)\n",
|
|
fw_count, card->to_host_signals_w);
|
|
}
|
|
fw_count = unifi_read_shared_count(card, card->sdio_ctrl_addr + 2);
|
|
if (fw_count < 0)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to read from-host sig read count\n");
|
|
}
|
|
else
|
|
{
|
|
unifi_error(card->ospriv, "fhsr: %u (driver thinks is %u)\n",
|
|
fw_count, card->from_host_signals_r);
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
unifi_info(card->ospriv, "UniFi function %d is enabled.\n", card->function);
|
|
|
|
/* See if we missed an SDIO interrupt */
|
|
r = CardPendingInt(card, &pending);
|
|
if (pending)
|
|
{
|
|
unifi_error(card->ospriv, "There is an unhandled pending interrupt.\n");
|
|
*status = 2;
|
|
return r;
|
|
}
|
|
|
|
return r;
|
|
} /* unifi_check_io_status() */
|
|
|
|
|
|
void unifi_get_hip_qos_info(card_t *card, unifi_HipQosInfo *hipqosinfo)
|
|
{
|
|
s32 count_fhr;
|
|
s16 t;
|
|
u32 occupied_fh;
|
|
|
|
q_t *sigq;
|
|
u16 nslots, i;
|
|
|
|
memset(hipqosinfo, 0, sizeof(unifi_HipQosInfo));
|
|
|
|
nslots = card->config_data.num_fromhost_data_slots;
|
|
|
|
for (i = 0; i < nslots; i++)
|
|
{
|
|
if (card->from_host_data[i].bd.data_length == 0)
|
|
{
|
|
hipqosinfo->free_fh_bulkdata_slots++;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < UNIFI_NO_OF_TX_QS; i++)
|
|
{
|
|
sigq = &card->fh_traffic_queue[i];
|
|
t = sigq->q_wr_ptr - sigq->q_rd_ptr;
|
|
if (t < 0)
|
|
{
|
|
t += sigq->q_length;
|
|
}
|
|
hipqosinfo->free_fh_sig_queue_slots[i] = (sigq->q_length - t) - 1;
|
|
}
|
|
|
|
count_fhr = unifi_read_shared_count(card, card->sdio_ctrl_addr + 2);
|
|
if (count_fhr < 0)
|
|
{
|
|
unifi_error(card->ospriv, "Failed to read from-host sig read count - %d\n", count_fhr);
|
|
hipqosinfo->free_fh_fw_slots = 0xfa;
|
|
return;
|
|
}
|
|
|
|
occupied_fh = (card->from_host_signals_w - count_fhr) % 128;
|
|
|
|
hipqosinfo->free_fh_fw_slots = (u16)(card->config_data.num_fromhost_sig_frags - occupied_fh);
|
|
}
|
|
|
|
|
|
|
|
CsrResult ConvertCsrSdioToCsrHipResult(card_t *card, CsrResult csrResult)
|
|
{
|
|
CsrResult r = CSR_RESULT_FAILURE;
|
|
|
|
switch (csrResult)
|
|
{
|
|
case CSR_RESULT_SUCCESS:
|
|
r = CSR_RESULT_SUCCESS;
|
|
break;
|
|
/* Timeout errors */
|
|
case CSR_SDIO_RESULT_TIMEOUT:
|
|
/* Integrity errors */
|
|
case CSR_SDIO_RESULT_CRC_ERROR:
|
|
r = CSR_RESULT_FAILURE;
|
|
break;
|
|
case CSR_SDIO_RESULT_NO_DEVICE:
|
|
r = CSR_WIFI_HIP_RESULT_NO_DEVICE;
|
|
break;
|
|
case CSR_SDIO_RESULT_INVALID_VALUE:
|
|
r = CSR_WIFI_HIP_RESULT_INVALID_VALUE;
|
|
break;
|
|
case CSR_RESULT_FAILURE:
|
|
r = CSR_RESULT_FAILURE;
|
|
break;
|
|
default:
|
|
unifi_warning(card->ospriv, "Unrecognised csrResult error code: %d\n", csrResult);
|
|
break;
|
|
}
|
|
|
|
return r;
|
|
} /* ConvertCsrSdioToCsrHipResult() */
|
|
|
|
|