| /* |
| * *************************************************************************** |
| * FILE: putest.c |
| * |
| * PURPOSE: putest related functions. |
| * |
| * Copyright (C) 2008-2009 by Cambridge Silicon Radio Ltd. |
| * |
| * Refer to LICENSE.txt included with this source code for details on |
| * the license terms. |
| * |
| * *************************************************************************** |
| */ |
| |
| #include <linux/vmalloc.h> |
| #include <linux/firmware.h> |
| |
| #include "unifi_priv.h" |
| #include "csr_wifi_hip_chiphelper.h" |
| |
| #define UNIFI_PROC_BOTH 3 |
| |
| |
| int unifi_putest_cmd52_read(unifi_priv_t *priv, unsigned char *arg) |
| { |
| struct unifi_putest_cmd52 cmd52_params; |
| u8 *arg_pos; |
| unsigned int cmd_param_size; |
| int r; |
| CsrResult csrResult; |
| unsigned char ret_buffer[32]; |
| u8 *ret_buffer_pos; |
| u8 retries; |
| |
| arg_pos = (u8*)(((unifi_putest_command_t*)arg) + 1); |
| if (get_user(cmd_param_size, (int*)arg_pos)) { |
| unifi_error(priv, |
| "unifi_putest_cmd52_read: Failed to get the argument\n"); |
| return -EFAULT; |
| } |
| |
| if (cmd_param_size != sizeof(struct unifi_putest_cmd52)) { |
| unifi_error(priv, |
| "unifi_putest_cmd52_read: cmd52 struct mismatch\n"); |
| return -EINVAL; |
| } |
| |
| arg_pos += sizeof(unsigned int); |
| if (copy_from_user(&cmd52_params, |
| (void*)arg_pos, |
| sizeof(struct unifi_putest_cmd52))) { |
| unifi_error(priv, |
| "unifi_putest_cmd52_read: Failed to get the cmd52 params\n"); |
| return -EFAULT; |
| } |
| |
| unifi_trace(priv, UDBG2, "cmd52r: func=%d addr=0x%x ", |
| cmd52_params.funcnum, cmd52_params.addr); |
| |
| retries = 3; |
| CsrSdioClaim(priv->sdio); |
| do { |
| if (cmd52_params.funcnum == 0) { |
| csrResult = CsrSdioF0Read8(priv->sdio, cmd52_params.addr, &cmd52_params.data); |
| } else { |
| csrResult = CsrSdioRead8(priv->sdio, cmd52_params.addr, &cmd52_params.data); |
| } |
| } while (--retries && ((csrResult == CSR_SDIO_RESULT_CRC_ERROR) || (csrResult == CSR_SDIO_RESULT_TIMEOUT))); |
| CsrSdioRelease(priv->sdio); |
| |
| if (csrResult != CSR_RESULT_SUCCESS) { |
| unifi_error(priv, |
| "\nunifi_putest_cmd52_read: Read8() failed (csrResult=0x%x)\n", csrResult); |
| return -EFAULT; |
| } |
| unifi_trace(priv, UDBG2, "data=%d\n", cmd52_params.data); |
| |
| /* Copy the info to the out buffer */ |
| *(unifi_putest_command_t*)ret_buffer = UNIFI_PUTEST_CMD52_READ; |
| ret_buffer_pos = (u8*)(((unifi_putest_command_t*)ret_buffer) + 1); |
| *(unsigned int*)ret_buffer_pos = sizeof(struct unifi_putest_cmd52); |
| ret_buffer_pos += sizeof(unsigned int); |
| memcpy(ret_buffer_pos, &cmd52_params, sizeof(struct unifi_putest_cmd52)); |
| ret_buffer_pos += sizeof(struct unifi_putest_cmd52); |
| |
| r = copy_to_user((void*)arg, |
| ret_buffer, |
| ret_buffer_pos - ret_buffer); |
| if (r) { |
| unifi_error(priv, |
| "unifi_putest_cmd52_read: Failed to return the data\n"); |
| return -EFAULT; |
| } |
| |
| return 0; |
| } |
| |
| |
| int unifi_putest_cmd52_write(unifi_priv_t *priv, unsigned char *arg) |
| { |
| struct unifi_putest_cmd52 cmd52_params; |
| u8 *arg_pos; |
| unsigned int cmd_param_size; |
| CsrResult csrResult; |
| u8 retries; |
| |
| arg_pos = (u8*)(((unifi_putest_command_t*)arg) + 1); |
| if (get_user(cmd_param_size, (int*)arg_pos)) { |
| unifi_error(priv, |
| "unifi_putest_cmd52_write: Failed to get the argument\n"); |
| return -EFAULT; |
| } |
| |
| if (cmd_param_size != sizeof(struct unifi_putest_cmd52)) { |
| unifi_error(priv, |
| "unifi_putest_cmd52_write: cmd52 struct mismatch\n"); |
| return -EINVAL; |
| } |
| |
| arg_pos += sizeof(unsigned int); |
| if (copy_from_user(&cmd52_params, |
| (void*)(arg_pos), |
| sizeof(struct unifi_putest_cmd52))) { |
| unifi_error(priv, |
| "unifi_putest_cmd52_write: Failed to get the cmd52 params\n"); |
| return -EFAULT; |
| } |
| |
| unifi_trace(priv, UDBG2, "cmd52w: func=%d addr=0x%x data=%d\n", |
| cmd52_params.funcnum, cmd52_params.addr, cmd52_params.data); |
| |
| retries = 3; |
| CsrSdioClaim(priv->sdio); |
| do { |
| if (cmd52_params.funcnum == 0) { |
| csrResult = CsrSdioF0Write8(priv->sdio, cmd52_params.addr, cmd52_params.data); |
| } else { |
| csrResult = CsrSdioWrite8(priv->sdio, cmd52_params.addr, cmd52_params.data); |
| } |
| } while (--retries && ((csrResult == CSR_SDIO_RESULT_CRC_ERROR) || (csrResult == CSR_SDIO_RESULT_TIMEOUT))); |
| CsrSdioRelease(priv->sdio); |
| |
| if (csrResult != CSR_RESULT_SUCCESS) { |
| unifi_error(priv, |
| "unifi_putest_cmd52_write: Write8() failed (csrResult=0x%x)\n", csrResult); |
| return -EFAULT; |
| } |
| |
| return 0; |
| } |
| |
| int unifi_putest_gp_read16(unifi_priv_t *priv, unsigned char *arg) |
| { |
| struct unifi_putest_gp_rw16 gp_r16_params; |
| u8 *arg_pos; |
| unsigned int cmd_param_size; |
| int r; |
| CsrResult csrResult; |
| unsigned char ret_buffer[32]; |
| u8 *ret_buffer_pos; |
| |
| arg_pos = (u8*)(((unifi_putest_command_t*)arg) + 1); |
| if (get_user(cmd_param_size, (int*)arg_pos)) { |
| unifi_error(priv, |
| "unifi_putest_gp_read16: Failed to get the argument\n"); |
| return -EFAULT; |
| } |
| |
| if (cmd_param_size != sizeof(struct unifi_putest_gp_rw16)) { |
| unifi_error(priv, |
| "unifi_putest_gp_read16: struct mismatch\n"); |
| return -EINVAL; |
| } |
| |
| arg_pos += sizeof(unsigned int); |
| if (copy_from_user(&gp_r16_params, |
| (void*)arg_pos, |
| sizeof(struct unifi_putest_gp_rw16))) { |
| unifi_error(priv, |
| "unifi_putest_gp_read16: Failed to get the params\n"); |
| return -EFAULT; |
| } |
| CsrSdioClaim(priv->sdio); |
| csrResult = unifi_card_read16(priv->card, gp_r16_params.addr, &gp_r16_params.data); |
| CsrSdioRelease(priv->sdio); |
| if (csrResult != CSR_RESULT_SUCCESS) { |
| unifi_error(priv, |
| "unifi_putest_gp_read16: unifi_card_read16() GP=0x%x failed (csrResult=0x%x)\n", gp_r16_params.addr, csrResult); |
| return -EFAULT; |
| } |
| |
| unifi_trace(priv, UDBG2, "gp_r16: GP=0x%08x, data=0x%04x\n", gp_r16_params.addr, gp_r16_params.data); |
| |
| /* Copy the info to the out buffer */ |
| *(unifi_putest_command_t*)ret_buffer = UNIFI_PUTEST_GP_READ16; |
| ret_buffer_pos = (u8*)(((unifi_putest_command_t*)ret_buffer) + 1); |
| *(unsigned int*)ret_buffer_pos = sizeof(struct unifi_putest_gp_rw16); |
| ret_buffer_pos += sizeof(unsigned int); |
| memcpy(ret_buffer_pos, &gp_r16_params, sizeof(struct unifi_putest_gp_rw16)); |
| ret_buffer_pos += sizeof(struct unifi_putest_gp_rw16); |
| |
| r = copy_to_user((void*)arg, |
| ret_buffer, |
| ret_buffer_pos - ret_buffer); |
| if (r) { |
| unifi_error(priv, |
| "unifi_putest_gp_read16: Failed to return the data\n"); |
| return -EFAULT; |
| } |
| |
| return 0; |
| } |
| |
| int unifi_putest_gp_write16(unifi_priv_t *priv, unsigned char *arg) |
| { |
| struct unifi_putest_gp_rw16 gp_w16_params; |
| u8 *arg_pos; |
| unsigned int cmd_param_size; |
| CsrResult csrResult; |
| |
| arg_pos = (u8*)(((unifi_putest_command_t*)arg) + 1); |
| if (get_user(cmd_param_size, (int*)arg_pos)) { |
| unifi_error(priv, |
| "unifi_putest_gp_write16: Failed to get the argument\n"); |
| return -EFAULT; |
| } |
| |
| if (cmd_param_size != sizeof(struct unifi_putest_gp_rw16)) { |
| unifi_error(priv, |
| "unifi_putest_gp_write16: struct mismatch\n"); |
| return -EINVAL; |
| } |
| |
| arg_pos += sizeof(unsigned int); |
| if (copy_from_user(&gp_w16_params, |
| (void*)(arg_pos), |
| sizeof(struct unifi_putest_gp_rw16))) { |
| unifi_error(priv, |
| "unifi_putest_gp_write16: Failed to get the params\n"); |
| return -EFAULT; |
| } |
| |
| unifi_trace(priv, UDBG2, "gp_w16: GP=0x%08x, data=0x%04x\n", gp_w16_params.addr, gp_w16_params.data); |
| CsrSdioClaim(priv->sdio); |
| csrResult = unifi_card_write16(priv->card, gp_w16_params.addr, gp_w16_params.data); |
| CsrSdioRelease(priv->sdio); |
| if (csrResult != CSR_RESULT_SUCCESS) { |
| unifi_error(priv, |
| "unifi_putest_gp_write16: unifi_card_write16() GP=%x failed (csrResult=0x%x)\n", gp_w16_params.addr, csrResult); |
| return -EFAULT; |
| } |
| |
| return 0; |
| } |
| |
| int unifi_putest_set_sdio_clock(unifi_priv_t *priv, unsigned char *arg) |
| { |
| int sdio_clock_speed; |
| CsrResult csrResult; |
| |
| if (get_user(sdio_clock_speed, (int*)(((unifi_putest_command_t*)arg) + 1))) { |
| unifi_error(priv, |
| "unifi_putest_set_sdio_clock: Failed to get the argument\n"); |
| return -EFAULT; |
| } |
| |
| unifi_trace(priv, UDBG2, "set sdio clock: %d KHz\n", sdio_clock_speed); |
| |
| CsrSdioClaim(priv->sdio); |
| csrResult = CsrSdioMaxBusClockFrequencySet(priv->sdio, sdio_clock_speed * 1000); |
| CsrSdioRelease(priv->sdio); |
| if (csrResult != CSR_RESULT_SUCCESS) { |
| unifi_error(priv, |
| "unifi_putest_set_sdio_clock: Set clock failed (csrResult=0x%x)\n", csrResult); |
| return -EFAULT; |
| } |
| |
| return 0; |
| } |
| |
| |
| int unifi_putest_start(unifi_priv_t *priv, unsigned char *arg) |
| { |
| int r; |
| CsrResult csrResult; |
| int already_in_test = priv->ptest_mode; |
| |
| /* Ensure that sme_sys_suspend() doesn't power down the chip because: |
| * 1) Power is needed anyway for ptest. |
| * 2) The app code uses the START ioctl as a reset, so it gets called |
| * multiple times. If the app stops the XAPs, but the power_down/up |
| * sequence doesn't actually power down the chip, there can be problems |
| * resetting, because part of the power_up sequence disables function 1 |
| */ |
| priv->ptest_mode = 1; |
| |
| /* Suspend the SME and UniFi */ |
| if (priv->sme_cli) { |
| r = sme_sys_suspend(priv); |
| if (r) { |
| unifi_error(priv, |
| "unifi_putest_start: failed to suspend UniFi\n"); |
| return r; |
| } |
| } |
| |
| /* Application may have stopped the XAPs, but they are needed for reset */ |
| if (already_in_test) { |
| CsrSdioClaim(priv->sdio); |
| csrResult = unifi_start_processors(priv->card); |
| CsrSdioRelease(priv->sdio); |
| if (csrResult != CSR_RESULT_SUCCESS) { |
| unifi_error(priv, "Failed to start XAPs. Hard reset required.\n"); |
| } |
| } else { |
| /* Ensure chip is powered for the case where there's no unifi_helper */ |
| CsrSdioClaim(priv->sdio); |
| csrResult = CsrSdioPowerOn(priv->sdio); |
| CsrSdioRelease(priv->sdio); |
| if (csrResult != CSR_RESULT_SUCCESS) { |
| unifi_error(priv, "CsrSdioPowerOn csrResult = %d\n", csrResult); |
| } |
| } |
| CsrSdioClaim(priv->sdio); |
| csrResult = unifi_init(priv->card); |
| CsrSdioRelease(priv->sdio); |
| if (csrResult != CSR_RESULT_SUCCESS) { |
| unifi_error(priv, |
| "unifi_putest_start: failed to init UniFi\n"); |
| return CsrHipResultToStatus(csrResult); |
| } |
| |
| return 0; |
| } |
| |
| |
| int unifi_putest_stop(unifi_priv_t *priv, unsigned char *arg) |
| { |
| int r = 0; |
| CsrResult csrResult; |
| |
| /* Application may have stopped the XAPs, but they are needed for reset */ |
| CsrSdioClaim(priv->sdio); |
| csrResult = unifi_start_processors(priv->card); |
| CsrSdioRelease(priv->sdio); |
| if (csrResult != CSR_RESULT_SUCCESS) { |
| unifi_error(priv, "Failed to start XAPs. Hard reset required.\n"); |
| } |
| |
| /* PUTEST_STOP is also used to resume the XAPs after SME coredump. |
| * Don't power off the chip, leave that to the normal wifi-off which is |
| * about to carry on. No need to resume the SME either, as it wasn't suspended. |
| */ |
| if (priv->coredump_mode) { |
| priv->coredump_mode = 0; |
| return 0; |
| } |
| |
| /* At this point function 1 is enabled and the XAPs are running, so it is |
| * safe to let the card power down. Power is restored later, asynchronously, |
| * during the wifi_on requested by the SME. |
| */ |
| CsrSdioClaim(priv->sdio); |
| CsrSdioPowerOff(priv->sdio); |
| CsrSdioRelease(priv->sdio); |
| |
| /* Resume the SME and UniFi */ |
| if (priv->sme_cli) { |
| r = sme_sys_resume(priv); |
| if (r) { |
| unifi_error(priv, |
| "unifi_putest_stop: failed to resume SME\n"); |
| } |
| } |
| priv->ptest_mode = 0; |
| |
| return r; |
| } |
| |
| |
| int unifi_putest_dl_fw(unifi_priv_t *priv, unsigned char *arg) |
| { |
| #define UF_PUTEST_MAX_FW_FILE_NAME 16 |
| #define UNIFI_MAX_FW_PATH_LEN 32 |
| unsigned int fw_name_length; |
| unsigned char fw_name[UF_PUTEST_MAX_FW_FILE_NAME+1]; |
| unsigned char *name_buffer; |
| int postfix; |
| char fw_path[UNIFI_MAX_FW_PATH_LEN]; |
| const struct firmware *fw_entry; |
| struct dlpriv temp_fw_sta; |
| int r; |
| CsrResult csrResult; |
| |
| /* Get the f/w file name length */ |
| if (get_user(fw_name_length, (unsigned int*)(((unifi_putest_command_t*)arg) + 1))) { |
| unifi_error(priv, |
| "unifi_putest_dl_fw: Failed to get the length argument\n"); |
| return -EFAULT; |
| } |
| |
| unifi_trace(priv, UDBG2, "unifi_putest_dl_fw: file name size = %d\n", fw_name_length); |
| |
| /* Sanity check for the f/w file name length */ |
| if (fw_name_length > UF_PUTEST_MAX_FW_FILE_NAME) { |
| unifi_error(priv, |
| "unifi_putest_dl_fw: F/W file name is too long\n"); |
| return -EINVAL; |
| } |
| |
| /* Get the f/w file name */ |
| name_buffer = ((unsigned char*)arg) + sizeof(unifi_putest_command_t) + sizeof(unsigned int); |
| if (copy_from_user(fw_name, (void*)name_buffer, fw_name_length)) { |
| unifi_error(priv, "unifi_putest_dl_fw: Failed to get the file name\n"); |
| return -EFAULT; |
| } |
| fw_name[fw_name_length] = '\0'; |
| unifi_trace(priv, UDBG2, "unifi_putest_dl_fw: file = %s\n", fw_name); |
| |
| /* Keep the existing f/w to a temp, we need to restore it later */ |
| temp_fw_sta = priv->fw_sta; |
| |
| /* Get the putest f/w */ |
| postfix = priv->instance; |
| scnprintf(fw_path, UNIFI_MAX_FW_PATH_LEN, "unifi-sdio-%d/%s", |
| postfix, fw_name); |
| r = request_firmware(&fw_entry, fw_path, priv->unifi_device); |
| if (r == 0) { |
| priv->fw_sta.fw_desc = (void *)fw_entry; |
| priv->fw_sta.dl_data = fw_entry->data; |
| priv->fw_sta.dl_len = fw_entry->size; |
| } else { |
| unifi_error(priv, "Firmware file not available\n"); |
| return -EINVAL; |
| } |
| |
| /* Application may have stopped the XAPs, but they are needed for reset */ |
| CsrSdioClaim(priv->sdio); |
| csrResult = unifi_start_processors(priv->card); |
| CsrSdioRelease(priv->sdio); |
| if (csrResult != CSR_RESULT_SUCCESS) { |
| unifi_error(priv, "Failed to start XAPs. Hard reset required.\n"); |
| } |
| |
| /* Download the f/w. On UF6xxx this will cause the f/w file to convert |
| * into patch format and download via the ROM boot loader |
| */ |
| CsrSdioClaim(priv->sdio); |
| csrResult = unifi_download(priv->card, 0x0c00); |
| CsrSdioRelease(priv->sdio); |
| if (csrResult != CSR_RESULT_SUCCESS) { |
| unifi_error(priv, |
| "unifi_putest_dl_fw: failed to download the f/w\n"); |
| goto free_fw; |
| } |
| |
| /* Free the putest f/w... */ |
| free_fw: |
| uf_release_firmware(priv, &priv->fw_sta); |
| /* ... and restore the original f/w */ |
| priv->fw_sta = temp_fw_sta; |
| |
| return CsrHipResultToStatus(csrResult); |
| } |
| |
| |
| int unifi_putest_dl_fw_buff(unifi_priv_t *priv, unsigned char *arg) |
| { |
| unsigned int fw_length; |
| unsigned char *fw_buf = NULL; |
| unsigned char *fw_user_ptr; |
| struct dlpriv temp_fw_sta; |
| CsrResult csrResult; |
| |
| /* Get the f/w buffer length */ |
| if (get_user(fw_length, (unsigned int*)(((unifi_putest_command_t*)arg) + 1))) { |
| unifi_error(priv, |
| "unifi_putest_dl_fw_buff: Failed to get the length arg\n"); |
| return -EFAULT; |
| } |
| |
| unifi_trace(priv, UDBG2, "unifi_putest_dl_fw_buff: size = %d\n", fw_length); |
| |
| /* Sanity check for the buffer length */ |
| if (fw_length == 0 || fw_length > 0xfffffff) { |
| unifi_error(priv, |
| "unifi_putest_dl_fw_buff: buffer length bad %u\n", fw_length); |
| return -EINVAL; |
| } |
| |
| /* Buffer for kernel copy of the f/w image */ |
| fw_buf = kmalloc(fw_length, GFP_KERNEL); |
| if (!fw_buf) { |
| unifi_error(priv, "unifi_putest_dl_fw_buff: malloc fail\n"); |
| return -ENOMEM; |
| } |
| |
| /* Get the f/w image */ |
| fw_user_ptr = ((unsigned char*)arg) + sizeof(unifi_putest_command_t) + sizeof(unsigned int); |
| if (copy_from_user(fw_buf, (void*)fw_user_ptr, fw_length)) { |
| unifi_error(priv, "unifi_putest_dl_fw_buff: Failed to get the buffer\n"); |
| kfree(fw_buf); |
| return -EFAULT; |
| } |
| |
| /* Save the existing f/w to a temp, we need to restore it later */ |
| temp_fw_sta = priv->fw_sta; |
| |
| /* Setting fw_desc NULL indicates to the core that no f/w file was loaded |
| * via the kernel request_firmware() mechanism. This indicates to the core |
| * that it shouldn't call release_firmware() after the download is done. |
| */ |
| priv->fw_sta.fw_desc = NULL; /* No OS f/w resource */ |
| priv->fw_sta.dl_data = fw_buf; |
| priv->fw_sta.dl_len = fw_length; |
| |
| /* Application may have stopped the XAPs, but they are needed for reset */ |
| CsrSdioClaim(priv->sdio); |
| csrResult = unifi_start_processors(priv->card); |
| CsrSdioRelease(priv->sdio); |
| if (csrResult != CSR_RESULT_SUCCESS) { |
| unifi_error(priv, "Failed to start XAPs. Hard reset required.\n"); |
| } |
| |
| /* Download the f/w. On UF6xxx this will cause the f/w file to convert |
| * into patch format and download via the ROM boot loader |
| */ |
| CsrSdioClaim(priv->sdio); |
| csrResult = unifi_download(priv->card, 0x0c00); |
| CsrSdioRelease(priv->sdio); |
| if (csrResult != CSR_RESULT_SUCCESS) { |
| unifi_error(priv, |
| "unifi_putest_dl_fw_buff: failed to download the f/w\n"); |
| goto free_fw; |
| } |
| |
| free_fw: |
| /* Finished with the putest f/w, so restore the station f/w */ |
| priv->fw_sta = temp_fw_sta; |
| kfree(fw_buf); |
| |
| return CsrHipResultToStatus(csrResult); |
| } |
| |
| |
| int unifi_putest_coredump_prepare(unifi_priv_t *priv, unsigned char *arg) |
| { |
| u16 data_u16; |
| s32 i; |
| CsrResult r; |
| |
| unifi_info(priv, "Preparing for SDIO coredump\n"); |
| #if defined (CSR_WIFI_HIP_DEBUG_OFFLINE) |
| unifi_debug_buf_dump(); |
| #endif |
| |
| /* Sanity check that userspace hasn't called a PUTEST_START, because that |
| * would have reset UniFi, potentially power cycling it and losing context |
| */ |
| if (priv->ptest_mode) { |
| unifi_error(priv, "PUTEST_START shouldn't be used before a coredump\n"); |
| } |
| |
| /* Flag that the userspace has requested coredump. Even if this preparation |
| * fails, the SME will call PUTEST_STOP to tidy up. |
| */ |
| priv->coredump_mode = 1; |
| |
| for (i = 0; i < 3; i++) { |
| CsrSdioClaim(priv->sdio); |
| r = CsrSdioRead16(priv->sdio, CHIP_HELPER_UNIFI_GBL_CHIP_VERSION*2, &data_u16); |
| CsrSdioRelease(priv->sdio); |
| if (r != CSR_RESULT_SUCCESS) { |
| unifi_info(priv, "Failed to read chip version! Try %d\n", i); |
| |
| /* First try, re-enable function which may have been disabled by f/w panic */ |
| if (i == 0) { |
| unifi_info(priv, "Try function enable\n"); |
| CsrSdioClaim(priv->sdio); |
| r = CsrSdioFunctionEnable(priv->sdio); |
| CsrSdioRelease(priv->sdio); |
| if (r != CSR_RESULT_SUCCESS) { |
| unifi_error(priv, "CsrSdioFunctionEnable failed %d\n", r); |
| } |
| continue; |
| } |
| |
| /* Subsequent tries, reset */ |
| |
| /* Set clock speed low */ |
| CsrSdioClaim(priv->sdio); |
| r = CsrSdioMaxBusClockFrequencySet(priv->sdio, UNIFI_SDIO_CLOCK_SAFE_HZ); |
| CsrSdioRelease(priv->sdio); |
| if (r != CSR_RESULT_SUCCESS) { |
| unifi_error(priv, "CsrSdioMaxBusClockFrequencySet() failed %d\n", r); |
| } |
| |
| /* Card software reset */ |
| CsrSdioClaim(priv->sdio); |
| r = unifi_card_hard_reset(priv->card); |
| CsrSdioRelease(priv->sdio); |
| if (r != CSR_RESULT_SUCCESS) { |
| unifi_error(priv, "unifi_card_hard_reset() failed %d\n", r); |
| } |
| } else { |
| unifi_info(priv, "Read chip version of 0x%04x\n", data_u16); |
| break; |
| } |
| } |
| |
| if (r != CSR_RESULT_SUCCESS) { |
| unifi_error(priv, "Failed to prepare chip\n"); |
| return -EIO; |
| } |
| |
| /* Stop the XAPs for coredump. The PUTEST_STOP must be called, e.g. at |
| * Raw SDIO deinit, to resume them. |
| */ |
| CsrSdioClaim(priv->sdio); |
| r = unifi_card_stop_processor(priv->card, UNIFI_PROC_BOTH); |
| CsrSdioRelease(priv->sdio); |
| if (r != CSR_RESULT_SUCCESS) { |
| unifi_error(priv, "Failed to stop processors\n"); |
| } |
| |
| return 0; |
| } |
| |
| int unifi_putest_cmd52_block_read(unifi_priv_t *priv, unsigned char *arg) |
| { |
| struct unifi_putest_block_cmd52_r block_cmd52; |
| u8 *arg_pos; |
| unsigned int cmd_param_size; |
| CsrResult r; |
| u8 *block_local_buffer; |
| |
| arg_pos = (u8*)(((unifi_putest_command_t*)arg) + 1); |
| if (get_user(cmd_param_size, (int*)arg_pos)) { |
| unifi_error(priv, |
| "cmd52r_block: Failed to get the argument\n"); |
| return -EFAULT; |
| } |
| |
| if (cmd_param_size != sizeof(struct unifi_putest_block_cmd52_r)) { |
| unifi_error(priv, |
| "cmd52r_block: cmd52 struct mismatch\n"); |
| return -EINVAL; |
| } |
| |
| arg_pos += sizeof(unsigned int); |
| if (copy_from_user(&block_cmd52, |
| (void*)arg_pos, |
| sizeof(struct unifi_putest_block_cmd52_r))) { |
| unifi_error(priv, |
| "cmd52r_block: Failed to get the cmd52 params\n"); |
| return -EFAULT; |
| } |
| |
| unifi_trace(priv, UDBG2, "cmd52r_block: func=%d addr=0x%x len=0x%x ", |
| block_cmd52.funcnum, block_cmd52.addr, block_cmd52.length); |
| |
| block_local_buffer = vmalloc(block_cmd52.length); |
| if (block_local_buffer == NULL) { |
| unifi_error(priv, "cmd52r_block: Failed to allocate buffer\n"); |
| return -ENOMEM; |
| } |
| |
| CsrSdioClaim(priv->sdio); |
| r = unifi_card_readn(priv->card, block_cmd52.addr, block_local_buffer, block_cmd52.length); |
| CsrSdioRelease(priv->sdio); |
| if (r != CSR_RESULT_SUCCESS) { |
| unifi_error(priv, "cmd52r_block: unifi_readn failed\n"); |
| return -EIO; |
| } |
| |
| if (copy_to_user((void*)block_cmd52.data, |
| block_local_buffer, |
| block_cmd52.length)) { |
| unifi_error(priv, |
| "cmd52r_block: Failed to return the data\n"); |
| return -EFAULT; |
| } |
| |
| return 0; |
| } |