/***************************************************************************** (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 #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; 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 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; if (card == NULL) { return CSR_WIFI_HIP_RESULT_INVALID_VALUE; } r = unifi_init(card); if (r != CSR_RESULT_SUCCESS) { return r; } r = unifi_hip_init(card); if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE) { return r; } if (r != CSR_RESULT_SUCCESS) { unifi_error(card->ospriv, "Failed to start host protocol.\n"); return r; } 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; if (card == NULL) { 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); 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"); 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"); 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"); 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"); 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"); } 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; if (card == NULL) { 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) { 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"); return r; } /* Free the firmware file information. */ unifi_fw_read_stop(card->ospriv, dlpriv); 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; 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"); 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); 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) { 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) { return r; } 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 conversion 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; /* * 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"); 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); 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"); return r; } if (finger_print != SLUT_FINGERPRINT) { unifi_error(card->ospriv, "Failed to find Symbol lookup table fingerprint\n"); 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) { 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) { 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"); 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"); 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"); 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"); 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"); 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"); 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"); 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"); 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"); 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"); 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); return r; } if (initialised != 0) { 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 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 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); 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; 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; } 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; 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); 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; } 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; /* * 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"); 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); 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"); 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"); return r; } *paddr = (((u32)mbox1 << 16) | mbox0); 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) { /* 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 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"); } 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; /* A chip version of zero means that the version never got successfully 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); 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; /* 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; } } /* * --------------------------------------------------------------------------- * 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; /* 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"); 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"); 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"); 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"); 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"); 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; 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) { 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; } /* card_free_memory_resources() */ static void card_init_soft_queues(card_t *card) { s16 i; 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 } /* * --------------------------------------------------------------------------- * 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; 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); } /* 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) { #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); } /* 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; /* 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); return r; } if (card->sdio_ctrl_addr == 0) { unifi_error(card->ospriv, "Failed to find config struct!\n"); 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"); 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"); 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"); 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; 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; 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; } /* 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; /* 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); 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]); } } /* * --------------------------------------------------------------------------- * 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; 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); 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); } /* 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; /* 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++; } } 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) { 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)); 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; 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"); 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 occurred 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); 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); 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) { return r; } return CSR_RESULT_SUCCESS; } /* unifi_prepare_hw() */ static CsrResult unifi_read_chip_version(card_t *card) { u32 gbl_chip_version; CsrResult r; u16 ver; 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"); 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); 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; /* 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 resetting 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"); 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"); 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) { 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; } 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; /* * 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); 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"); } 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; /* * 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"); 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"); 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); 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"); } 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; /* 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"); 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"); 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) { return r; } } /* Software hard reset */ r = card_reset_method_dbg_reset(card); 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; 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); return r; } card->unifi_interrupt_seq++; 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; /* 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); } 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]++; } } } 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() */