| /***************************************************************************** |
| |
| (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_mem.c |
| * |
| * PURPOSE: Implementation of the Card API for SDIO. |
| * |
| * --------------------------------------------------------------------------- |
| */ |
| #include "csr_wifi_hip_unifi.h" |
| #include "csr_wifi_hip_card.h" |
| |
| #define SDIO_RETRIES 3 |
| #define CSR_WIFI_HIP_SDIO_TRACE_DATA_LENGTH 16 |
| |
| |
| #define retryable_sdio_error(_csrResult) (((_csrResult) == CSR_SDIO_RESULT_CRC_ERROR) || ((_csrResult) == CSR_SDIO_RESULT_TIMEOUT)) |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * retrying_read8 |
| * retrying_write8 |
| * |
| * These functions provide the first level of retry for SDIO operations. |
| * If an SDIO command fails for reason of a response timeout or CRC |
| * error, it is retried immediately. If three attempts fail we report a |
| * failure. |
| * If the command failed for any other reason, the failure is reported |
| * immediately. |
| * |
| * Arguments: |
| * card Pointer to card structure. |
| * funcnum The SDIO function to access. |
| * Function 0 is the Card Configuration Register space, |
| * function 1/2 is the UniFi register space. |
| * addr Address to access |
| * pdata Pointer in which to return the value read. |
| * data Value to write. |
| * |
| * Returns: |
| * CSR_RESULT_SUCCESS on success, non-zero error code on error: |
| * CSR_WIFI_HIP_RESULT_NO_DEVICE card was ejected |
| * CSR_RESULT_FAILURE an SDIO error occurred |
| * --------------------------------------------------------------------------- |
| */ |
| static CsrResult retrying_read8(card_t *card, s16 funcnum, u32 addr, u8 *pdata) |
| { |
| CsrSdioFunction *sdio = card->sdio_if; |
| CsrResult r = CSR_RESULT_SUCCESS; |
| s16 retries; |
| CsrResult csrResult = CSR_RESULT_SUCCESS; |
| |
| retries = 0; |
| while (retries++ < SDIO_RETRIES) |
| { |
| if (funcnum == 0) |
| { |
| #if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_SDIO_TRACE) |
| unifi_debug_log_to_buf("r0@%02X", addr); |
| #endif |
| csrResult = CsrSdioF0Read8(sdio, addr, pdata); |
| } |
| else |
| { |
| #ifdef CSR_WIFI_TRANSPORT_CSPI |
| unifi_error(card->ospriv, |
| "retrying_read_f0_8: F1 8-bit reads are not allowed.\n"); |
| return CSR_RESULT_FAILURE; |
| #else |
| #if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_SDIO_TRACE) |
| unifi_debug_log_to_buf("r@%02X", addr); |
| #endif |
| csrResult = CsrSdioRead8(sdio, addr, pdata); |
| #endif |
| } |
| #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", *pdata); |
| } |
| #endif |
| if (csrResult == CSR_SDIO_RESULT_NO_DEVICE) |
| { |
| return CSR_WIFI_HIP_RESULT_NO_DEVICE; |
| } |
| /* |
| * Try again for retryable (CRC or TIMEOUT) errors, |
| * break on success or fatal error |
| */ |
| if (!retryable_sdio_error(csrResult)) |
| { |
| #ifdef CSR_WIFI_HIP_DATA_PLANE_PROFILE |
| card->cmd_prof.cmd52_count++; |
| #endif |
| break; |
| } |
| unifi_trace(card->ospriv, UDBG2, "retryable SDIO error reading F%d 0x%lX\n", funcnum, addr); |
| } |
| |
| if ((csrResult == CSR_RESULT_SUCCESS) && (retries > 1)) |
| { |
| unifi_warning(card->ospriv, "Read succeeded after %d attempts\n", retries); |
| } |
| |
| if (csrResult != CSR_RESULT_SUCCESS) |
| { |
| unifi_error(card->ospriv, "Failed to read from UniFi (addr 0x%lX) after %d tries\n", |
| addr, retries - 1); |
| /* Report any SDIO error as a general i/o error */ |
| r = CSR_RESULT_FAILURE; |
| } |
| |
| return r; |
| } /* retrying_read8() */ |
| |
| |
| static CsrResult retrying_write8(card_t *card, s16 funcnum, u32 addr, u8 data) |
| { |
| CsrSdioFunction *sdio = card->sdio_if; |
| CsrResult r = CSR_RESULT_SUCCESS; |
| s16 retries; |
| CsrResult csrResult = CSR_RESULT_SUCCESS; |
| |
| retries = 0; |
| while (retries++ < SDIO_RETRIES) |
| { |
| if (funcnum == 0) |
| { |
| #if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_SDIO_TRACE) |
| unifi_debug_log_to_buf("w0@%02X=%X", addr, data); |
| #endif |
| csrResult = CsrSdioF0Write8(sdio, addr, data); |
| } |
| else |
| { |
| #ifdef CSR_WIFI_TRANSPORT_CSPI |
| unifi_error(card->ospriv, |
| "retrying_write_f0_8: F1 8-bit writes are not allowed.\n"); |
| return CSR_RESULT_FAILURE; |
| #else |
| #if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_SDIO_TRACE) |
| unifi_debug_log_to_buf("w@%02X=%X", addr, data); |
| #endif |
| csrResult = CsrSdioWrite8(sdio, addr, data); |
| #endif |
| } |
| #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", csrResult); |
| } |
| unifi_debug_string_to_buf("\n"); |
| #endif |
| if (csrResult == CSR_SDIO_RESULT_NO_DEVICE) |
| { |
| return CSR_WIFI_HIP_RESULT_NO_DEVICE; |
| } |
| /* |
| * Try again for retryable (CRC or TIMEOUT) errors, |
| * break on success or fatal error |
| */ |
| if (!retryable_sdio_error(csrResult)) |
| { |
| #ifdef CSR_WIFI_HIP_DATA_PLANE_PROFILE |
| card->cmd_prof.cmd52_count++; |
| #endif |
| break; |
| } |
| unifi_trace(card->ospriv, UDBG2, "retryable SDIO error writing %02X to F%d 0x%lX\n", |
| data, funcnum, addr); |
| } |
| |
| if ((csrResult == CSR_RESULT_SUCCESS) && (retries > 1)) |
| { |
| unifi_warning(card->ospriv, "Write succeeded after %d attempts\n", retries); |
| } |
| |
| if (csrResult != CSR_RESULT_SUCCESS) |
| { |
| unifi_error(card->ospriv, "Failed to write to UniFi (addr 0x%lX) after %d tries\n", |
| addr, retries - 1); |
| /* Report any SDIO error as a general i/o error */ |
| r = CSR_RESULT_FAILURE; |
| } |
| |
| return r; |
| } /* retrying_write8() */ |
| |
| |
| static CsrResult retrying_read16(card_t *card, s16 funcnum, |
| u32 addr, u16 *pdata) |
| { |
| CsrSdioFunction *sdio = card->sdio_if; |
| CsrResult r = CSR_RESULT_SUCCESS; |
| s16 retries; |
| CsrResult csrResult = CSR_RESULT_SUCCESS; |
| |
| retries = 0; |
| while (retries++ < SDIO_RETRIES) |
| { |
| #if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_SDIO_TRACE) |
| unifi_debug_log_to_buf("r@%02X", addr); |
| #endif |
| csrResult = CsrSdioRead16(sdio, addr, pdata); |
| #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", *pdata); |
| } |
| #endif |
| if (csrResult == CSR_SDIO_RESULT_NO_DEVICE) |
| { |
| return CSR_WIFI_HIP_RESULT_NO_DEVICE; |
| } |
| |
| /* |
| * Try again for retryable (CRC or TIMEOUT) errors, |
| * break on success or fatal error |
| */ |
| if (!retryable_sdio_error(csrResult)) |
| { |
| #ifdef CSR_WIFI_HIP_DATA_PLANE_PROFILE |
| card->cmd_prof.cmd52_count++; |
| #endif |
| break; |
| } |
| unifi_trace(card->ospriv, UDBG2, "retryable SDIO error reading F%d 0x%lX\n", funcnum, addr); |
| } |
| |
| if ((csrResult == CSR_RESULT_SUCCESS) && (retries > 1)) |
| { |
| unifi_warning(card->ospriv, "Read succeeded after %d attempts\n", retries); |
| } |
| |
| if (csrResult != CSR_RESULT_SUCCESS) |
| { |
| unifi_error(card->ospriv, "Failed to read from UniFi (addr 0x%lX) after %d tries\n", |
| addr, retries - 1); |
| /* Report any SDIO error as a general i/o error */ |
| r = CSR_RESULT_FAILURE; |
| } |
| |
| return r; |
| } /* retrying_read16() */ |
| |
| |
| static CsrResult retrying_write16(card_t *card, s16 funcnum, |
| u32 addr, u16 data) |
| { |
| CsrSdioFunction *sdio = card->sdio_if; |
| CsrResult r = CSR_RESULT_SUCCESS; |
| s16 retries; |
| CsrResult csrResult = CSR_RESULT_SUCCESS; |
| |
| retries = 0; |
| while (retries++ < SDIO_RETRIES) |
| { |
| #if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_SDIO_TRACE) |
| unifi_debug_log_to_buf("w@%02X=%X", addr, data); |
| #endif |
| csrResult = CsrSdioWrite16(sdio, addr, data); |
| #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", csrResult); |
| } |
| unifi_debug_string_to_buf("\n"); |
| #endif |
| if (csrResult == CSR_SDIO_RESULT_NO_DEVICE) |
| { |
| return CSR_WIFI_HIP_RESULT_NO_DEVICE; |
| } |
| |
| /* |
| * Try again for retryable (CRC or TIMEOUT) errors, |
| * break on success or fatal error |
| */ |
| if (!retryable_sdio_error(csrResult)) |
| { |
| #ifdef CSR_WIFI_HIP_DATA_PLANE_PROFILE |
| card->cmd_prof.cmd52_count++; |
| #endif |
| break; |
| } |
| unifi_trace(card->ospriv, UDBG2, "retryable SDIO error writing %02X to F%d 0x%lX\n", |
| data, funcnum, addr); |
| } |
| |
| if ((csrResult == CSR_RESULT_SUCCESS) && (retries > 1)) |
| { |
| unifi_warning(card->ospriv, "Write succeeded after %d attempts\n", retries); |
| } |
| |
| if (csrResult != CSR_RESULT_SUCCESS) |
| { |
| unifi_error(card->ospriv, "Failed to write to UniFi (addr 0x%lX) after %d tries\n", |
| addr, retries - 1); |
| /* Report any SDIO error as a general i/o error */ |
| r = CSR_RESULT_FAILURE; |
| } |
| |
| return r; |
| } /* retrying_write16() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * sdio_read_f0 |
| * |
| * Reads a byte value from the CCCR (func 0) area of UniFi. |
| * |
| * Arguments: |
| * card Pointer to card structure. |
| * addr Address to read from |
| * pdata Pointer in which to store the read value. |
| * |
| * Returns: |
| * CSR_RESULT_SUCCESS on success, non-zero error code on error: |
| * CSR_WIFI_HIP_RESULT_NO_DEVICE card was ejected |
| * CSR_RESULT_FAILURE an SDIO error occurred |
| * --------------------------------------------------------------------------- |
| */ |
| CsrResult sdio_read_f0(card_t *card, u32 addr, u8 *pdata) |
| { |
| #if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_DATA_PLANE_PROFILE) |
| card->cmd_prof.cmd52_f0_r_count++; |
| #endif |
| return retrying_read8(card, 0, addr, pdata); |
| } /* sdio_read_f0() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * sdio_write_f0 |
| * |
| * Writes a byte value to the CCCR (func 0) area of UniFi. |
| * |
| * Arguments: |
| * card Pointer to card structure. |
| * addr Address to read from |
| * data Data value to write. |
| * |
| * Returns: |
| * CSR_RESULT_SUCCESS on success, non-zero error code on error: |
| * CSR_WIFI_HIP_RESULT_NO_DEVICE card was ejected |
| * CSR_RESULT_FAILURE an SDIO error occurred |
| * --------------------------------------------------------------------------- |
| */ |
| CsrResult sdio_write_f0(card_t *card, u32 addr, u8 data) |
| { |
| #if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_DATA_PLANE_PROFILE) |
| card->cmd_prof.cmd52_f0_w_count++; |
| #endif |
| return retrying_write8(card, 0, addr, data); |
| } /* sdio_write_f0() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * unifi_read_direct_8_or_16 |
| * |
| * Read a 8-bit value from the UniFi SDIO interface. |
| * |
| * Arguments: |
| * card Pointer to card structure. |
| * addr Address to read from |
| * pdata Pointer in which to return data. |
| * |
| * Returns: |
| * CSR_RESULT_SUCCESS on success, non-zero error code on error: |
| * --------------------------------------------------------------------------- |
| */ |
| CsrResult unifi_read_direct_8_or_16(card_t *card, u32 addr, u8 *pdata) |
| { |
| #ifdef CSR_WIFI_TRANSPORT_CSPI |
| u16 w; |
| CsrResult r; |
| |
| r = retrying_read16(card, card->function, addr, &w); |
| *pdata = (u8)(w & 0xFF); |
| return r; |
| #else |
| return retrying_read8(card, card->function, addr, pdata); |
| #endif |
| } /* unifi_read_direct_8_or_16() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * unifi_write_direct_8_or_16 |
| * |
| * Write a byte value to the UniFi SDIO interface. |
| * |
| * Arguments: |
| * card Pointer to card structure. |
| * addr Address to write to |
| * data Value to write. |
| * |
| * Returns: |
| * CSR_RESULT_SUCCESS on success, non-zero error code on error |
| * |
| * Notes: |
| * If 8-bit write is used, the even address *must* be written second. |
| * This is because writes to odd bytes are cached and not committed |
| * to memory until the preceding even address is written. |
| * --------------------------------------------------------------------------- |
| */ |
| CsrResult unifi_write_direct_8_or_16(card_t *card, u32 addr, u8 data) |
| { |
| if (addr & 1) |
| { |
| unifi_warning(card->ospriv, |
| "Warning: Byte write to an odd address (0x%lX) is dangerous\n", |
| addr); |
| } |
| |
| #ifdef CSR_WIFI_TRANSPORT_CSPI |
| return retrying_write16(card, card->function, addr, (u16)data); |
| #else |
| return retrying_write8(card, card->function, addr, data); |
| #endif |
| } /* unifi_write_direct_8_or_16() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * unifi_read_direct16 |
| * |
| * Read a 16-bit value from the UniFi SDIO interface. |
| * |
| * Arguments: |
| * card Pointer to card structure. |
| * addr Address to read from |
| * pdata Pointer in which to return data. |
| * |
| * Returns: |
| * CSR_RESULT_SUCCESS on success, non-zero error code on error: |
| * CSR_WIFI_HIP_RESULT_NO_DEVICE card was ejected |
| * CSR_RESULT_FAILURE an SDIO error occurred |
| * |
| * Notes: |
| * The even address *must* be read first. This is because reads from |
| * odd bytes are cached and read from memory when the preceding |
| * even address is read. |
| * --------------------------------------------------------------------------- |
| */ |
| CsrResult unifi_read_direct16(card_t *card, u32 addr, u16 *pdata) |
| { |
| return retrying_read16(card, card->function, addr, pdata); |
| } /* unifi_read_direct16() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * unifi_write_direct16 |
| * |
| * Write a 16-bit value to the UniFi SDIO interface. |
| * |
| * Arguments: |
| * card Pointer to card structure. |
| * addr Address to write to |
| * data Value to write. |
| * |
| * Returns: |
| * CSR_RESULT_SUCCESS on success, non-zero error code on error: |
| * CSR_WIFI_HIP_RESULT_NO_DEVICE card was ejected |
| * CSR_RESULT_FAILURE an SDIO error occurred |
| * |
| * Notes: |
| * The even address *must* be written second. This is because writes to |
| * odd bytes are cached and not committed to memory until the preceding |
| * even address is written. |
| * --------------------------------------------------------------------------- |
| */ |
| CsrResult unifi_write_direct16(card_t *card, u32 addr, u16 data) |
| { |
| return retrying_write16(card, card->function, addr, data); |
| } /* unifi_write_direct16() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * unifi_read_direct32 |
| * |
| * Read a 32-bit value from the UniFi SDIO interface. |
| * |
| * Arguments: |
| * card Pointer to card structure. |
| * addr Address to read from |
| * pdata Pointer in which to return data. |
| * |
| * Returns: |
| * CSR_RESULT_SUCCESS on success, non-zero error code on error: |
| * CSR_WIFI_HIP_RESULT_NO_DEVICE card was ejected |
| * CSR_RESULT_FAILURE an SDIO error occurred |
| * --------------------------------------------------------------------------- |
| */ |
| CsrResult unifi_read_direct32(card_t *card, u32 addr, u32 *pdata) |
| { |
| CsrResult r; |
| u16 w0, w1; |
| |
| r = retrying_read16(card, card->function, addr, &w0); |
| if (r != CSR_RESULT_SUCCESS) |
| { |
| return r; |
| } |
| |
| r = retrying_read16(card, card->function, addr + 2, &w1); |
| if (r != CSR_RESULT_SUCCESS) |
| { |
| return r; |
| } |
| |
| *pdata = ((u32)w1 << 16) | (u32)w0; |
| |
| return CSR_RESULT_SUCCESS; |
| } /* unifi_read_direct32() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * unifi_read_directn_match |
| * |
| * Read multiple 8-bit values from the UniFi SDIO interface, |
| * stopping when either we have read 'len' bytes or we have read |
| * a octet equal to 'match'. If 'match' is not a valid octet |
| * then this function is the same as 'unifi_read_directn'. |
| * |
| * Arguments: |
| * card Pointer to card structure. |
| * addr Start address to read from. |
| * pdata Pointer to which to write data. |
| * len Maximum umber of bytes to read |
| * match The value to stop reading at. |
| * num Pointer to buffer to write number of bytes read |
| * |
| * Returns: |
| * number of octets read on success, negative error code on error: |
| * CSR_WIFI_HIP_RESULT_NO_DEVICE card was ejected |
| * CSR_RESULT_FAILURE an SDIO error occurred |
| * |
| * Notes: |
| * The even address *must* be read first. This is because reads from |
| * odd bytes are cached and read from memory when the preceding |
| * even address is read. |
| * --------------------------------------------------------------------------- |
| */ |
| static CsrResult unifi_read_directn_match(card_t *card, u32 addr, void *pdata, u16 len, s8 m, u32 *num) |
| { |
| CsrResult r; |
| u32 i; |
| u8 *cptr; |
| u16 w; |
| |
| *num = 0; |
| |
| cptr = (u8 *)pdata; |
| for (i = 0; i < len; i += 2) |
| { |
| r = retrying_read16(card, card->function, addr, &w); |
| if (r != CSR_RESULT_SUCCESS) |
| { |
| return r; |
| } |
| |
| *cptr++ = ((u8)w & 0xFF); |
| if ((m >= 0) && (((s8)w & 0xFF) == m)) |
| { |
| break; |
| } |
| |
| if (i + 1 == len) |
| { |
| /* The len is odd. Ignore the last high byte */ |
| break; |
| } |
| |
| *cptr++ = ((u8)(w >> 8) & 0xFF); |
| if ((m >= 0) && (((s8)(w >> 8) & 0xFF) == m)) |
| { |
| break; |
| } |
| |
| addr += 2; |
| } |
| |
| *num = (s32)(cptr - (u8 *)pdata); |
| return CSR_RESULT_SUCCESS; |
| } |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * unifi_read_directn |
| * |
| * Read multiple 8-bit values from the UniFi SDIO interface. |
| * |
| * Arguments: |
| * card Pointer to card structure. |
| * addr Start address to read from. |
| * pdata Pointer to which to write data. |
| * len Number of bytes to read |
| * |
| * Returns: |
| * 0 on success, non-zero error code on error: |
| * CSR_WIFI_HIP_RESULT_NO_DEVICE card was ejected |
| * CSR_RESULT_FAILURE an SDIO error occurred |
| * |
| * Notes: |
| * The even address *must* be read first. This is because reads from |
| * odd bytes are cached and read from memory when the preceding |
| * even address is read. |
| * --------------------------------------------------------------------------- |
| */ |
| CsrResult unifi_read_directn(card_t *card, u32 addr, void *pdata, u16 len) |
| { |
| u32 num; |
| |
| return unifi_read_directn_match(card, addr, pdata, len, -1, &num); |
| } /* unifi_read_directn() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * unifi_write_directn |
| * |
| * Write multiple 8-bit values to the UniFi SDIO interface. |
| * |
| * Arguments: |
| * card Pointer to card structure. |
| * addr Start address to write to. |
| * pdata Source data pointer. |
| * len Number of bytes to write, must be even. |
| * |
| * Returns: |
| * 0 on success, non-zero error code on error: |
| * CSR_WIFI_HIP_RESULT_NO_DEVICE card was ejected |
| * CSR_RESULT_FAILURE an SDIO error occurred |
| * |
| * Notes: |
| * The UniFi has a peculiar 16-bit bus architecture. Writes are only |
| * committed to memory when an even address is accessed. Writes to |
| * odd addresses are cached and only committed if the next write is |
| * to the preceding address. |
| * This means we must write data as pairs of bytes in reverse order. |
| * --------------------------------------------------------------------------- |
| */ |
| CsrResult unifi_write_directn(card_t *card, u32 addr, void *pdata, u16 len) |
| { |
| CsrResult r; |
| u8 *cptr; |
| s16 signed_len; |
| |
| cptr = (u8 *)pdata; |
| signed_len = (s16)len; |
| while (signed_len > 0) |
| { |
| /* This is UniFi-1 specific code. CSPI not supported so 8-bit write allowed */ |
| r = retrying_write16(card, card->function, addr, *cptr); |
| if (r != CSR_RESULT_SUCCESS) |
| { |
| return r; |
| } |
| |
| cptr += 2; |
| addr += 2; |
| signed_len -= 2; |
| } |
| |
| return CSR_RESULT_SUCCESS; |
| } /* unifi_write_directn() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * set_dmem_page |
| * set_pmem_page |
| * |
| * Set up the page register for the shared data memory window or program |
| * memory window. |
| * |
| * Arguments: |
| * card Pointer to card structure. |
| * dmem_addr UniFi shared-data-memory address to access. |
| * pmem_addr UniFi program memory address to access. This includes |
| * External FLASH memory at 0x000000 |
| * Processor program memory at 0x200000 |
| * External SRAM at memory 0x400000 |
| * paddr Location to write an SDIO address (24-bit) for |
| * use in a unifi_read_direct or unifi_write_direct call. |
| * |
| * Returns: |
| * CSR_RESULT_SUCCESS on success |
| * CSR_WIFI_HIP_RESULT_NO_DEVICE card was ejected |
| * CSR_RESULT_FAILURE an SDIO error occurred |
| * --------------------------------------------------------------------------- |
| */ |
| static CsrResult set_dmem_page(card_t *card, u32 dmem_addr, u32 *paddr) |
| { |
| u16 page, addr; |
| u32 len; |
| CsrResult r; |
| |
| *paddr = 0; |
| |
| if (!ChipHelper_DecodeWindow(card->helper, |
| CHIP_HELPER_WINDOW_3, |
| CHIP_HELPER_WT_SHARED, |
| dmem_addr / 2, |
| &page, &addr, &len)) |
| { |
| unifi_error(card->ospriv, "Failed to decode SHARED_DMEM_PAGE %08lx\n", dmem_addr); |
| return CSR_WIFI_HIP_RESULT_INVALID_VALUE; |
| } |
| |
| if (page != card->dmem_page) |
| { |
| unifi_trace(card->ospriv, UDBG6, "setting dmem page=0x%X, addr=0x%lX\n", page, addr); |
| |
| /* change page register */ |
| r = unifi_write_direct16(card, ChipHelper_HOST_WINDOW3_PAGE(card->helper) * 2, page); |
| if (r != CSR_RESULT_SUCCESS) |
| { |
| unifi_error(card->ospriv, "Failed to write SHARED_DMEM_PAGE\n"); |
| return r; |
| } |
| |
| card->dmem_page = page; |
| } |
| |
| *paddr = ((s32)addr * 2) + (dmem_addr & 1); |
| |
| return CSR_RESULT_SUCCESS; |
| } /* set_dmem_page() */ |
| |
| |
| static CsrResult set_pmem_page(card_t *card, u32 pmem_addr, |
| enum chip_helper_window_type mem_type, u32 *paddr) |
| { |
| u16 page, addr; |
| u32 len; |
| CsrResult r; |
| |
| *paddr = 0; |
| |
| if (!ChipHelper_DecodeWindow(card->helper, |
| CHIP_HELPER_WINDOW_2, |
| mem_type, |
| pmem_addr / 2, |
| &page, &addr, &len)) |
| { |
| unifi_error(card->ospriv, "Failed to decode PROG MEM PAGE %08lx %d\n", pmem_addr, mem_type); |
| return CSR_WIFI_HIP_RESULT_INVALID_VALUE; |
| } |
| |
| if (page != card->pmem_page) |
| { |
| unifi_trace(card->ospriv, UDBG6, "setting pmem page=0x%X, addr=0x%lX\n", page, addr); |
| |
| /* change page register */ |
| r = unifi_write_direct16(card, ChipHelper_HOST_WINDOW2_PAGE(card->helper) * 2, page); |
| if (r != CSR_RESULT_SUCCESS) |
| { |
| unifi_error(card->ospriv, "Failed to write PROG MEM PAGE\n"); |
| return r; |
| } |
| |
| card->pmem_page = page; |
| } |
| |
| *paddr = ((s32)addr * 2) + (pmem_addr & 1); |
| |
| return CSR_RESULT_SUCCESS; |
| } /* set_pmem_page() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * set_page |
| * |
| * Sets up the appropriate page register to access the given address. |
| * Returns the sdio address at which the unifi address can be accessed. |
| * |
| * Arguments: |
| * card Pointer to card structure. |
| * generic_addr UniFi internal address to access, in Generic Pointer |
| * format, i.e. top byte is space indicator. |
| * paddr Location to write page address |
| * SDIO address (24-bit) for use in a unifi_read_direct or |
| * unifi_write_direct call |
| * |
| * Returns: |
| * CSR_WIFI_HIP_RESULT_NO_DEVICE card was ejected |
| * CSR_RESULT_FAILURE an SDIO error occurred |
| * CSR_WIFI_HIP_RESULT_INVALID_VALUE the address is invalid |
| * --------------------------------------------------------------------------- |
| */ |
| static CsrResult set_page(card_t *card, u32 generic_addr, u32 *paddr) |
| { |
| s32 space; |
| u32 addr; |
| CsrResult r = CSR_RESULT_SUCCESS; |
| |
| if (!paddr) |
| { |
| return CSR_WIFI_HIP_RESULT_INVALID_VALUE; |
| } |
| *paddr = 0; |
| space = UNIFI_GP_SPACE(generic_addr); |
| addr = UNIFI_GP_OFFSET(generic_addr); |
| switch (space) |
| { |
| case UNIFI_SH_DMEM: |
| /* Shared Data Memory is accessed via the Shared Data Memory window */ |
| r = set_dmem_page(card, addr, paddr); |
| if (r != CSR_RESULT_SUCCESS) |
| { |
| return r; |
| } |
| break; |
| |
| case UNIFI_EXT_FLASH: |
| if (!ChipHelper_HasFlash(card->helper)) |
| { |
| unifi_error(card->ospriv, "Bad address space for chip in generic pointer 0x%08lX (helper=0x%x)\n", |
| generic_addr, card->helper); |
| return CSR_WIFI_HIP_RESULT_INVALID_VALUE; |
| } |
| /* External FLASH is accessed via the Program Memory window */ |
| r = set_pmem_page(card, addr, CHIP_HELPER_WT_FLASH, paddr); |
| break; |
| |
| case UNIFI_EXT_SRAM: |
| if (!ChipHelper_HasExtSram(card->helper)) |
| { |
| unifi_error(card->ospriv, "Bad address space for chip in generic pointer 0x%08l (helper=0x%x)\n", |
| generic_addr, card->helper); |
| return CSR_WIFI_HIP_RESULT_INVALID_VALUE; |
| } |
| /* External SRAM is accessed via the Program Memory window */ |
| r = set_pmem_page(card, addr, CHIP_HELPER_WT_EXT_SRAM, paddr); |
| break; |
| |
| case UNIFI_REGISTERS: |
| /* Registers are accessed directly */ |
| *paddr = addr; |
| break; |
| |
| case UNIFI_PHY_DMEM: |
| r = unifi_set_proc_select(card, UNIFI_PROC_PHY); |
| if (r != CSR_RESULT_SUCCESS) |
| { |
| return r; |
| } |
| *paddr = ChipHelper_DATA_MEMORY_RAM_OFFSET(card->helper) * 2 + addr; |
| break; |
| |
| case UNIFI_MAC_DMEM: |
| r = unifi_set_proc_select(card, UNIFI_PROC_MAC); |
| if (r != CSR_RESULT_SUCCESS) |
| { |
| return r; |
| } |
| *paddr = ChipHelper_DATA_MEMORY_RAM_OFFSET(card->helper) * 2 + addr; |
| break; |
| |
| case UNIFI_BT_DMEM: |
| if (!ChipHelper_HasBt(card->helper)) |
| { |
| unifi_error(card->ospriv, "Bad address space for chip in generic pointer 0x%08lX (helper=0x%x)\n", |
| generic_addr, card->helper); |
| return CSR_WIFI_HIP_RESULT_INVALID_VALUE; |
| } |
| r = unifi_set_proc_select(card, UNIFI_PROC_BT); |
| if (r != CSR_RESULT_SUCCESS) |
| { |
| return r; |
| } |
| *paddr = ChipHelper_DATA_MEMORY_RAM_OFFSET(card->helper) * 2 + addr; |
| break; |
| |
| case UNIFI_PHY_PMEM: |
| r = unifi_set_proc_select(card, UNIFI_PROC_PHY); |
| if (r != CSR_RESULT_SUCCESS) |
| { |
| return r; |
| } |
| r = set_pmem_page(card, addr, CHIP_HELPER_WT_CODE_RAM, paddr); |
| break; |
| |
| case UNIFI_MAC_PMEM: |
| r = unifi_set_proc_select(card, UNIFI_PROC_MAC); |
| if (r != CSR_RESULT_SUCCESS) |
| { |
| return r; |
| } |
| r = set_pmem_page(card, addr, CHIP_HELPER_WT_CODE_RAM, paddr); |
| break; |
| |
| case UNIFI_BT_PMEM: |
| if (!ChipHelper_HasBt(card->helper)) |
| { |
| unifi_error(card->ospriv, "Bad address space for chip in generic pointer 0x%08lX (helper=0x%x)\n", |
| generic_addr, card->helper); |
| return CSR_WIFI_HIP_RESULT_INVALID_VALUE; |
| } |
| r = unifi_set_proc_select(card, UNIFI_PROC_BT); |
| if (r != CSR_RESULT_SUCCESS) |
| { |
| return r; |
| } |
| r = set_pmem_page(card, addr, CHIP_HELPER_WT_CODE_RAM, paddr); |
| break; |
| |
| case UNIFI_PHY_ROM: |
| if (!ChipHelper_HasRom(card->helper)) |
| { |
| unifi_error(card->ospriv, "Bad address space for chip in generic pointer 0x%08lX (helper=0x%x)\n", |
| generic_addr, card->helper); |
| return CSR_WIFI_HIP_RESULT_INVALID_VALUE; |
| } |
| r = unifi_set_proc_select(card, UNIFI_PROC_PHY); |
| if (r != CSR_RESULT_SUCCESS) |
| { |
| return r; |
| } |
| r = set_pmem_page(card, addr, CHIP_HELPER_WT_ROM, paddr); |
| break; |
| |
| case UNIFI_MAC_ROM: |
| if (!ChipHelper_HasRom(card->helper)) |
| { |
| unifi_error(card->ospriv, "Bad address space for chip in generic pointer 0x%08lX (helper=0x%x)\n", |
| generic_addr, card->helper); |
| return CSR_WIFI_HIP_RESULT_INVALID_VALUE; |
| } |
| r = unifi_set_proc_select(card, UNIFI_PROC_MAC); |
| if (r != CSR_RESULT_SUCCESS) |
| { |
| return r; |
| } |
| r = set_pmem_page(card, addr, CHIP_HELPER_WT_ROM, paddr); |
| break; |
| |
| case UNIFI_BT_ROM: |
| if (!ChipHelper_HasRom(card->helper) || !ChipHelper_HasBt(card->helper)) |
| { |
| unifi_error(card->ospriv, "Bad address space for chip in generic pointer 0x%08lX (helper=0x%x)\n", |
| generic_addr, card->helper); |
| return CSR_WIFI_HIP_RESULT_INVALID_VALUE; |
| } |
| r = unifi_set_proc_select(card, UNIFI_PROC_BT); |
| if (r != CSR_RESULT_SUCCESS) |
| { |
| return r; |
| } |
| r = set_pmem_page(card, addr, CHIP_HELPER_WT_ROM, paddr); |
| break; |
| |
| default: |
| unifi_error(card->ospriv, "Bad address space %d in generic pointer 0x%08lX (helper=0x%x)\n", |
| space, generic_addr, card->helper); |
| return CSR_WIFI_HIP_RESULT_INVALID_VALUE; |
| } |
| |
| return r; |
| } /* set_page() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * unifi_set_proc_select |
| * |
| * |
| * Arguments: |
| * card Pointer to card structure. |
| * select Which XAP core to select |
| * |
| * Returns: |
| * 0 on success, non-zero error code on error: |
| * CSR_WIFI_HIP_RESULT_NO_DEVICE card was ejected |
| * CSR_RESULT_FAILURE an SDIO error occurred |
| * --------------------------------------------------------------------------- |
| */ |
| CsrResult unifi_set_proc_select(card_t *card, enum unifi_dbg_processors_select select) |
| { |
| CsrResult r; |
| |
| /* Verify the the select value is allowed. */ |
| switch (select) |
| { |
| case UNIFI_PROC_MAC: |
| case UNIFI_PROC_PHY: |
| case UNIFI_PROC_BOTH: |
| break; |
| |
| |
| default: |
| return CSR_WIFI_HIP_RESULT_INVALID_VALUE; |
| } |
| |
| if (card->proc_select != (u32)select) |
| { |
| r = unifi_write_direct16(card, |
| ChipHelper_DBG_HOST_PROC_SELECT(card->helper) * 2, |
| (u8)select); |
| if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE) |
| { |
| return r; |
| } |
| if (r != CSR_RESULT_SUCCESS) |
| { |
| unifi_error(card->ospriv, "Failed to write to Proc Select register\n"); |
| return r; |
| } |
| |
| card->proc_select = (u32)select; |
| } |
| |
| return CSR_RESULT_SUCCESS; |
| } |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * unifi_read_8_or_16 |
| * |
| * Performs a byte read of the given address in shared data memory. |
| * Set up the shared data memory page register as required. |
| * |
| * Arguments: |
| * card Pointer to card structure. |
| * unifi_addr UniFi shared-data-memory address to access. |
| * pdata Pointer to a byte variable for the value read. |
| * |
| * Returns: |
| * CSR_RESULT_SUCCESS on success, non-zero error code on error: |
| * CSR_WIFI_HIP_RESULT_NO_DEVICE card was ejected |
| * CSR_RESULT_FAILURE an SDIO error occurred |
| * CSR_WIFI_HIP_RESULT_INVALID_VALUE a bad generic pointer was specified |
| * --------------------------------------------------------------------------- |
| */ |
| CsrResult unifi_read_8_or_16(card_t *card, u32 unifi_addr, u8 *pdata) |
| { |
| u32 sdio_addr; |
| CsrResult r; |
| #ifdef CSR_WIFI_TRANSPORT_CSPI |
| u16 w; |
| #endif |
| |
| r = set_page(card, unifi_addr, &sdio_addr); |
| if (r != CSR_RESULT_SUCCESS) |
| { |
| return r; |
| } |
| |
| #if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_DATA_PLANE_PROFILE) |
| card->cmd_prof.cmd52_r8or16_count++; |
| #endif |
| #ifdef CSR_WIFI_TRANSPORT_CSPI |
| r = retrying_read16(card, card->function, sdio_addr, &w); |
| *pdata = (u8)(w & 0xFF); |
| return r; |
| #else |
| return retrying_read8(card, card->function, sdio_addr, pdata); |
| #endif |
| } /* unifi_read_8_or_16() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * unifi_write_8_or_16 |
| * |
| * Performs a byte write of the given address in shared data memory. |
| * Set up the shared data memory page register as required. |
| * |
| * Arguments: |
| * card Pointer to card context struct. |
| * unifi_addr UniFi shared-data-memory address to access. |
| * data Value to write. |
| * |
| * Returns: |
| * CSR_RESULT_SUCCESS on success, non-zero error code on error: |
| * CSR_WIFI_HIP_RESULT_NO_DEVICE card was ejected |
| * CSR_RESULT_FAILURE an SDIO error occurred |
| * CSR_WIFI_HIP_RESULT_INVALID_VALUE a bad generic pointer was specified |
| * |
| * Notes: |
| * Beware using unifi_write8() because byte writes are not safe on UniFi. |
| * Writes to odd bytes are cached, writes to even bytes perform a 16-bit |
| * write with the previously cached odd byte. |
| * --------------------------------------------------------------------------- |
| */ |
| CsrResult unifi_write_8_or_16(card_t *card, u32 unifi_addr, u8 data) |
| { |
| u32 sdio_addr; |
| CsrResult r; |
| #ifdef CSR_WIFI_TRANSPORT_CSPI |
| u16 w; |
| #endif |
| |
| r = set_page(card, unifi_addr, &sdio_addr); |
| if (r != CSR_RESULT_SUCCESS) |
| { |
| return r; |
| } |
| |
| if (sdio_addr & 1) |
| { |
| unifi_warning(card->ospriv, |
| "Warning: Byte write to an odd address (0x%lX) is dangerous\n", |
| sdio_addr); |
| } |
| |
| #if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_DATA_PLANE_PROFILE) |
| card->cmd_prof.cmd52_w8or16_count++; |
| #endif |
| #ifdef CSR_WIFI_TRANSPORT_CSPI |
| w = data; |
| return retrying_write16(card, card->function, sdio_addr, w); |
| #else |
| return retrying_write8(card, card->function, sdio_addr, data); |
| #endif |
| } /* unifi_write_8_or_16() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * unifi_card_read16 |
| * |
| * Performs a 16-bit read of the given address in shared data memory. |
| * Set up the shared data memory page register as required. |
| * |
| * Arguments: |
| * card Pointer to card structure. |
| * unifi_addr UniFi shared-data-memory address to access. |
| * pdata Pointer to a 16-bit int variable for the value read. |
| * |
| * Returns: |
| * CSR_RESULT_SUCCESS on success, non-zero error code on error: |
| * CSR_WIFI_HIP_RESULT_NO_DEVICE card was ejected |
| * CSR_RESULT_FAILURE an SDIO error occurred |
| * CSR_WIFI_HIP_RESULT_INVALID_VALUE a bad generic pointer was specified |
| * --------------------------------------------------------------------------- |
| */ |
| CsrResult unifi_card_read16(card_t *card, u32 unifi_addr, u16 *pdata) |
| { |
| u32 sdio_addr; |
| CsrResult r; |
| |
| r = set_page(card, unifi_addr, &sdio_addr); |
| if (r != CSR_RESULT_SUCCESS) |
| { |
| return r; |
| } |
| |
| #if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_DATA_PLANE_PROFILE) |
| card->cmd_prof.cmd52_r16_count++; |
| #endif |
| return unifi_read_direct16(card, sdio_addr, pdata); |
| } /* unifi_card_read16() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * unifi_card_write16 |
| * |
| * Performs a 16-bit write of the given address in shared data memory. |
| * Set up the shared data memory page register as required. |
| * |
| * Arguments: |
| * card Pointer to card structure. |
| * unifi_addr UniFi shared-data-memory address to access. |
| * pdata Pointer to a byte variable for the value write. |
| * |
| * Returns: |
| * CSR_RESULT_SUCCESS on success, non-zero error code on error: |
| * CSR_WIFI_HIP_RESULT_NO_DEVICE card was ejected |
| * CSR_RESULT_FAILURE an SDIO error occurred |
| * CSR_WIFI_HIP_RESULT_INVALID_VALUE a bad generic pointer was specified |
| * --------------------------------------------------------------------------- |
| */ |
| CsrResult unifi_card_write16(card_t *card, u32 unifi_addr, u16 data) |
| { |
| u32 sdio_addr; |
| CsrResult r; |
| |
| r = set_page(card, unifi_addr, &sdio_addr); |
| if (r != CSR_RESULT_SUCCESS) |
| { |
| return r; |
| } |
| |
| #if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_DATA_PLANE_PROFILE) |
| card->cmd_prof.cmd52_w16_count++; |
| #endif |
| return unifi_write_direct16(card, sdio_addr, data); |
| } /* unifi_card_write16() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * unifi_read32 |
| * |
| * Performs a 32-bit read of the given address in shared data memory. |
| * Set up the shared data memory page register as required. |
| * |
| * Arguments: |
| * card Pointer to card structure. |
| * unifi_addr UniFi shared-data-memory address to access. |
| * pdata Pointer to a int variable for the value read. |
| * |
| * Returns: |
| * CSR_RESULT_SUCCESS on success, non-zero error code on error: |
| * CSR_WIFI_HIP_RESULT_NO_DEVICE card was ejected |
| * CSR_RESULT_FAILURE an SDIO error occurred |
| * CSR_WIFI_HIP_RESULT_INVALID_VALUE a bad generic pointer was specified |
| * --------------------------------------------------------------------------- |
| */ |
| CsrResult unifi_read32(card_t *card, u32 unifi_addr, u32 *pdata) |
| { |
| u32 sdio_addr; |
| CsrResult r; |
| |
| r = set_page(card, unifi_addr, &sdio_addr); |
| if (r != CSR_RESULT_SUCCESS) |
| { |
| return r; |
| } |
| |
| #if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_DATA_PLANE_PROFILE) |
| card->cmd_prof.cmd52_r32_count++; |
| #endif |
| return unifi_read_direct32(card, sdio_addr, pdata); |
| } /* unifi_read32() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * unifi_card_readn |
| * unifi_readnz |
| * |
| * Read multiple 8-bit values from the UniFi SDIO interface. |
| * This function interprets the address as a GenericPointer as |
| * defined in the UniFi Host Interface Protocol Specification. |
| * The readnz version of this function will stop when it reads a |
| * zero octet. |
| * |
| * Arguments: |
| * card Pointer to card structure. |
| * unifi_addr UniFi shared-data-memory address to access. |
| * pdata Pointer to which to write data. |
| * len Number of bytes to read |
| * |
| * Returns: |
| * CSR_RESULT_SUCCESS on success, non-zero error code on error: |
| * CSR_WIFI_HIP_RESULT_NO_DEVICE card was ejected |
| * CSR_RESULT_FAILURE an SDIO error occurred |
| * CSR_WIFI_HIP_RESULT_INVALID_VALUE a bad generic pointer was specified |
| * --------------------------------------------------------------------------- |
| */ |
| CsrResult unifi_readn_match(card_t *card, u32 unifi_addr, void *pdata, u16 len, s8 match) |
| { |
| u32 sdio_addr; |
| CsrResult r; |
| u32 num; |
| |
| r = set_page(card, unifi_addr, &sdio_addr); |
| if (r != CSR_RESULT_SUCCESS) |
| { |
| return r; |
| } |
| |
| r = unifi_read_directn_match(card, sdio_addr, pdata, len, match, &num); |
| return r; |
| } /* unifi_readn_match() */ |
| |
| |
| CsrResult unifi_card_readn(card_t *card, u32 unifi_addr, void *pdata, u16 len) |
| { |
| return unifi_readn_match(card, unifi_addr, pdata, len, -1); |
| } /* unifi_card_readn() */ |
| |
| |
| CsrResult unifi_readnz(card_t *card, u32 unifi_addr, void *pdata, u16 len) |
| { |
| return unifi_readn_match(card, unifi_addr, pdata, len, 0); |
| } /* unifi_readnz() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * unifi_read_shared_count |
| * |
| * Read signal count locations, checking for an SDIO error. The |
| * signal count locations only contain a valid number if the |
| * highest bit isn't set. |
| * |
| * Arguments: |
| * card Pointer to card context structure. |
| * addr Shared-memory address to read. |
| * |
| * Returns: |
| * Value read from memory (0-127) or -1 on error |
| * --------------------------------------------------------------------------- |
| */ |
| s32 unifi_read_shared_count(card_t *card, u32 addr) |
| { |
| u8 b; |
| /* I've increased this count, because I have seen cases where |
| * there were three reads in a row with the top bit set. I'm not |
| * sure why this might have happened, but I can't see a problem |
| * with increasing this limit. It's better to take a while to |
| * recover than to fail. */ |
| #define SHARED_READ_RETRY_LIMIT 10 |
| s32 i; |
| |
| /* |
| * Get the to-host-signals-written count. |
| * The top-bit will be set if the firmware was in the process of |
| * changing the value, in which case we read again. |
| */ |
| /* Limit the number of repeats so we don't freeze */ |
| for (i = 0; i < SHARED_READ_RETRY_LIMIT; i++) |
| { |
| CsrResult r; |
| r = unifi_read_8_or_16(card, addr, &b); |
| if (r != CSR_RESULT_SUCCESS) |
| { |
| return -1; |
| } |
| if (!(b & 0x80)) |
| { |
| /* There is a chance that the MSB may have contained invalid data |
| * (overflow) at the time it was read. Therefore mask off the MSB. |
| * This avoids a race between driver read and firmware write of the |
| * word, the value we need is in the lower 8 bits anway. |
| */ |
| return (s32)(b & 0xff); |
| } |
| } |
| |
| return -1; /* this function has changed in WMM mods */ |
| } /* unifi_read_shared_count() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * unifi_writen |
| * |
| * Write multiple 8-bit values to the UniFi SDIO interface using CMD52 |
| * This function interprets the address as a GenericPointer as |
| * defined in the UniFi Host Interface Protocol Specification. |
| * |
| * Arguments: |
| * card Pointer to card structure. |
| * unifi_addr UniFi shared-data-memory address to access. |
| * pdata Pointer to which to write data. |
| * len Number of bytes to write |
| * |
| * Returns: |
| * 0 on success, non-zero error code on error: |
| * CSR_WIFI_HIP_RESULT_NO_DEVICE card was ejected |
| * CSR_RESULT_FAILURE an SDIO error occurred |
| * CSR_WIFI_HIP_RESULT_INVALID_VALUE an odd length or length too big. |
| * --------------------------------------------------------------------------- |
| */ |
| CsrResult unifi_writen(card_t *card, u32 unifi_addr, void *pdata, u16 len) |
| { |
| u32 sdio_addr; |
| CsrResult r; |
| |
| r = set_page(card, unifi_addr, &sdio_addr); |
| if (r != CSR_RESULT_SUCCESS) |
| { |
| return r; |
| } |
| |
| return unifi_write_directn(card, sdio_addr, pdata, len); |
| } /* unifi_writen() */ |
| |
| |
| static CsrResult csr_sdio_block_rw(card_t *card, s16 funcnum, |
| u32 addr, u8 *pdata, |
| u16 count, s16 dir_is_write) |
| { |
| CsrResult csrResult; |
| |
| if (dir_is_write == UNIFI_SDIO_READ) |
| { |
| #if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_SDIO_TRACE) |
| unifi_debug_log_to_buf("r@%02X#%X=", addr, count); |
| #endif |
| #if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_DATA_PLANE_PROFILE) |
| unifi_debug_log_to_buf("R"); |
| #endif |
| csrResult = CsrSdioRead(card->sdio_if, addr, pdata, count); |
| #if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_DATA_PLANE_PROFILE) |
| unifi_debug_log_to_buf("<"); |
| #endif |
| } |
| else |
| { |
| #if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_SDIO_TRACE) |
| unifi_debug_log_to_buf("w@%02X#%X=", addr, count); |
| unifi_debug_hex_to_buf(pdata, count > CSR_WIFI_HIP_SDIO_TRACE_DATA_LENGTH?CSR_WIFI_HIP_SDIO_TRACE_DATA_LENGTH : count); |
| #endif |
| #if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_DATA_PLANE_PROFILE) |
| unifi_debug_log_to_buf("W"); |
| #endif |
| csrResult = CsrSdioWrite(card->sdio_if, addr, pdata, count); |
| #if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) && defined (CSR_WIFI_HIP_DATA_PLANE_PROFILE) |
| unifi_debug_log_to_buf(">"); |
| #endif |
| } |
| #ifdef CSR_WIFI_HIP_DATA_PLANE_PROFILE |
| card->cmd_prof.cmd53_count++; |
| #endif |
| #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", csrResult); |
| } |
| else if (dir_is_write == UNIFI_SDIO_READ) |
| { |
| unifi_debug_hex_to_buf(pdata, count > CSR_WIFI_HIP_SDIO_TRACE_DATA_LENGTH?CSR_WIFI_HIP_SDIO_TRACE_DATA_LENGTH : count); |
| } |
| unifi_debug_string_to_buf("\n"); |
| #endif |
| return csrResult; /* CSR SDIO (not HIP) error code */ |
| } |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * unifi_bulk_rw |
| * |
| * Transfer bulk data to or from the UniFi SDIO interface. |
| * This function is used to read or write signals and bulk data. |
| * |
| * Arguments: |
| * card Pointer to card structure. |
| * handle Value to put in the Register Address field of the CMD53 req. |
| * data Pointer to data to write. |
| * direction One of UNIFI_SDIO_READ or UNIFI_SDIO_WRITE |
| * |
| * Returns: |
| * CSR_RESULT_SUCCESS on success, non-zero error code on error: |
| * CSR_WIFI_HIP_RESULT_NO_DEVICE card was ejected |
| * CSR_RESULT_FAILURE an SDIO error occurred |
| * |
| * Notes: |
| * This function uses SDIO CMD53, which is the block transfer mode. |
| * --------------------------------------------------------------------------- |
| */ |
| CsrResult unifi_bulk_rw(card_t *card, u32 handle, void *pdata, |
| u32 len, s16 direction) |
| { |
| #define CMD53_RETRIES 3 |
| /* |
| * Ideally instead of sleeping, we want to busy wait. |
| * Currently there is no framework API to do this. When it becomes available, |
| * we can use it to busy wait using usecs |
| */ |
| #define REWIND_RETRIES 15 /* when REWIND_DELAY==1msec, or 250 when REWIND_DELAY==50usecs */ |
| #define REWIND_POLLING_RETRIES 5 |
| #define REWIND_DELAY 1 /* msec or 50usecs */ |
| CsrResult csrResult; /* SDIO error code */ |
| CsrResult r = CSR_RESULT_SUCCESS; /* HIP error code */ |
| s16 retries = CMD53_RETRIES; |
| s16 stat_retries; |
| u8 stat; |
| s16 dump_read; |
| #ifdef UNIFI_DEBUG |
| u8 *pdata_lsb = ((u8 *)&pdata) + card->lsb; |
| #endif |
| #ifdef CSR_WIFI_MAKE_FAKE_CMD53_ERRORS |
| static s16 fake_error; |
| #endif |
| |
| dump_read = 0; |
| #ifdef UNIFI_DEBUG |
| if (*pdata_lsb & 1) |
| { |
| unifi_notice(card->ospriv, "CD53 request on a unaligned buffer (addr: 0x%X) dir %s-Host\n", |
| pdata, (direction == UNIFI_SDIO_READ)?"To" : "From"); |
| if (direction == UNIFI_SDIO_WRITE) |
| { |
| dump(pdata, (u16)len); |
| } |
| else |
| { |
| dump_read = 1; |
| } |
| } |
| #endif |
| |
| /* Defensive checks */ |
| if (!pdata) |
| { |
| unifi_error(card->ospriv, "Null pdata for unifi_bulk_rw() len: %d\n", len); |
| return CSR_WIFI_HIP_RESULT_INVALID_VALUE; |
| } |
| if ((len & 1) || (len > 0xffff)) |
| { |
| unifi_error(card->ospriv, "Impossible CMD53 length requested: %d\n", len); |
| return CSR_WIFI_HIP_RESULT_INVALID_VALUE; |
| } |
| |
| while (1) |
| { |
| csrResult = csr_sdio_block_rw(card, card->function, handle, |
| (u8 *)pdata, (u16)len, |
| direction); |
| if (csrResult == CSR_SDIO_RESULT_NO_DEVICE) |
| { |
| return CSR_WIFI_HIP_RESULT_NO_DEVICE; |
| } |
| #ifdef CSR_WIFI_MAKE_FAKE_CMD53_ERRORS |
| if (++fake_error > 100) |
| { |
| fake_error = 90; |
| unifi_warning(card->ospriv, "Faking a CMD53 error,\n"); |
| if (csrResult == CSR_RESULT_SUCCESS) |
| { |
| csrResult = CSR_RESULT_FAILURE; |
| } |
| } |
| #endif |
| if (csrResult == CSR_RESULT_SUCCESS) |
| { |
| if (dump_read) |
| { |
| dump(pdata, (u16)len); |
| } |
| break; |
| } |
| |
| /* |
| * At this point the SDIO driver should have written the I/O Abort |
| * register to notify UniFi that the command has failed. |
| * UniFi-1 and UniFi-2 (not UF6xxx) use the same register to store the |
| * Deep Sleep State. This means we have to restore the Deep Sleep |
| * State (AWAKE in any case since we can not perform a CD53 in any other |
| * state) by rewriting the I/O Abort register to its previous value. |
| */ |
| if (card->chip_id <= SDIO_CARD_ID_UNIFI_2) |
| { |
| (void)unifi_set_host_state(card, UNIFI_HOST_STATE_AWAKE); |
| } |
| |
| /* If csr_sdio_block_rw() failed in a non-retryable way, or retries exhausted |
| * then stop retrying |
| */ |
| if (!retryable_sdio_error(csrResult)) |
| { |
| unifi_error(card->ospriv, "Fatal error in a CMD53 transfer\n"); |
| break; |
| } |
| |
| /* |
| * These happen from time to time, try again |
| */ |
| if (--retries == 0) |
| { |
| break; |
| } |
| |
| unifi_trace(card->ospriv, UDBG4, |
| "Error in a CMD53 transfer, retrying (h:%d,l:%u)...\n", |
| (s16)handle & 0xff, len); |
| |
| /* The transfer failed, rewind and try again */ |
| r = unifi_write_8_or_16(card, card->sdio_ctrl_addr + 8, |
| (u8)(handle & 0xff)); |
| if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE) |
| { |
| return r; |
| } |
| if (r != CSR_RESULT_SUCCESS) |
| { |
| /* |
| * If we can't even do CMD52 (register read/write) then |
| * stop here. |
| */ |
| unifi_error(card->ospriv, "Failed to write REWIND cmd\n"); |
| return r; |
| } |
| |
| /* Signal the UniFi to look for the rewind request. */ |
| r = CardGenInt(card); |
| if (r != CSR_RESULT_SUCCESS) |
| { |
| return r; |
| } |
| |
| /* Wait for UniFi to acknowledge the rewind */ |
| stat_retries = REWIND_RETRIES; |
| while (1) |
| { |
| r = unifi_read_8_or_16(card, card->sdio_ctrl_addr + 8, &stat); |
| if (r == CSR_WIFI_HIP_RESULT_NO_DEVICE) |
| { |
| return r; |
| } |
| if (r != CSR_RESULT_SUCCESS) |
| { |
| unifi_error(card->ospriv, "Failed to read REWIND status\n"); |
| return CSR_RESULT_FAILURE; |
| } |
| |
| if (stat == 0) |
| { |
| break; |
| } |
| if (--stat_retries == 0) |
| { |
| unifi_error(card->ospriv, "Timeout waiting for REWIND ready\n"); |
| return CSR_RESULT_FAILURE; |
| } |
| |
| /* Poll for the ack a few times */ |
| if (stat_retries < REWIND_RETRIES - REWIND_POLLING_RETRIES) |
| { |
| CsrThreadSleep(REWIND_DELAY); |
| } |
| } |
| } |
| |
| /* The call to csr_sdio_block_rw() still failed after retrying */ |
| if (csrResult != CSR_RESULT_SUCCESS) |
| { |
| unifi_error(card->ospriv, "Block %s failed after %d retries\n", |
| (direction == UNIFI_SDIO_READ)?"read" : "write", |
| CMD53_RETRIES - retries); |
| /* Report any SDIO error as a general i/o error */ |
| return CSR_RESULT_FAILURE; |
| } |
| |
| /* Collect some stats */ |
| if (direction == UNIFI_SDIO_READ) |
| { |
| card->sdio_bytes_read += len; |
| } |
| else |
| { |
| card->sdio_bytes_written += len; |
| } |
| |
| return CSR_RESULT_SUCCESS; |
| } /* unifi_bulk_rw() */ |
| |
| |
| /* |
| * --------------------------------------------------------------------------- |
| * unifi_bulk_rw_noretry |
| * |
| * Transfer bulk data to or from the UniFi SDIO interface. |
| * This function is used to read or write signals and bulk data. |
| * |
| * Arguments: |
| * card Pointer to card structure. |
| * handle Value to put in the Register Address field of |
| * the CMD53 req. |
| * data Pointer to data to write. |
| * direction One of UNIFI_SDIO_READ or UNIFI_SDIO_WRITE |
| * |
| * Returns: |
| * 0 on success, non-zero error code on error: |
| * CSR_WIFI_HIP_RESULT_NO_DEVICE card was ejected |
| * CSR_RESULT_FAILURE an SDIO error occurred |
| * |
| * Notes: |
| * This function uses SDIO CMD53, which is the block transfer mode. |
| * --------------------------------------------------------------------------- |
| */ |
| CsrResult unifi_bulk_rw_noretry(card_t *card, u32 handle, void *pdata, |
| u32 len, s16 direction) |
| { |
| CsrResult csrResult; |
| |
| csrResult = csr_sdio_block_rw(card, card->function, handle, |
| (u8 *)pdata, (u16)len, direction); |
| if (csrResult != CSR_RESULT_SUCCESS) |
| { |
| unifi_error(card->ospriv, "Block %s failed\n", |
| (direction == UNIFI_SDIO_READ)?"read" : "write"); |
| return csrResult; |
| } |
| |
| return CSR_RESULT_SUCCESS; |
| } /* unifi_bulk_rw_noretry() */ |
| |
| |