| /** @file moal_sdio_mmc.c |
| * |
| * @brief This file contains SDIO MMC IF (interface) module |
| * related functions. |
| * |
| * |
| * Copyright 2008-2021 NXP |
| * |
| * This software file (the File) is distributed by NXP |
| * under the terms of the GNU General Public License Version 2, June 1991 |
| * (the License). You may use, redistribute and/or modify the File in |
| * accordance with the terms and conditions of the License, a copy of which |
| * is available by writing to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the |
| * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. |
| * |
| * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE |
| * ARE EXPRESSLY DISCLAIMED. The License provides additional details about |
| * this warranty disclaimer. |
| * |
| */ |
| /**************************************************** |
| Change log: |
| 02/25/09: Initial creation - |
| This file supports SDIO MMC only |
| ****************************************************/ |
| |
| #include <linux/firmware.h> |
| |
| #include "moal_sdio.h" |
| |
| /** define nxp vendor id */ |
| #ifdef SD9177 |
| #define NXP_VENDOR_ID 0x0471 |
| #endif |
| #define MRVL_VENDOR_ID 0x02df |
| |
| /******************************************************** |
| Local Variables |
| ********************************************************/ |
| /* moal interface ops */ |
| static moal_if_ops sdiommc_ops; |
| /******************************************************** |
| Global Variables |
| ********************************************************/ |
| |
| #ifdef SD8887 |
| /** Device ID for SD8887 */ |
| #define SD_DEVICE_ID_8887 (0x9135) |
| #endif |
| #ifdef SD8801 |
| /** Device ID for SD8801 FN1 */ |
| #define SD_DEVICE_ID_8801 (0x9139) |
| #endif |
| #ifdef SD8897 |
| /** Device ID for SD8897 */ |
| #define SD_DEVICE_ID_8897 (0x912d) |
| #endif |
| #ifdef SD8977 |
| /** Device ID for SD8977 */ |
| #define SD_DEVICE_ID_8977 (0x9145) |
| #endif |
| #ifdef SD8978 |
| /** Device ID for SD8978 */ |
| #define SD_DEVICE_ID_8978 (0x9159) |
| #endif |
| #ifdef SD8997 |
| /** Device ID for SD8997 */ |
| #define SD_DEVICE_ID_8997 (0x9141) |
| #endif |
| #ifdef SD8987 |
| /** Device ID for SD8987 */ |
| #define SD_DEVICE_ID_8987 (0x9149) |
| #endif |
| #ifdef SD9098 |
| /** Device ID for SD9098 */ |
| #define SD_DEVICE_ID_9098_FN1 (0x914D) |
| /** Device ID for SD9098 */ |
| #define SD_DEVICE_ID_9098_FN2 (0x914E) |
| #endif |
| #ifdef SD9097 |
| /** Device ID for SD9097 */ |
| #define SD_DEVICE_ID_9097 (0x9155) |
| #endif |
| |
| /** WLAN IDs */ |
| static const struct sdio_device_id wlan_ids[] = { |
| #ifdef SD8887 |
| {SDIO_DEVICE(MRVL_VENDOR_ID, SD_DEVICE_ID_8887)}, |
| #endif |
| #ifdef SD8801 |
| {SDIO_DEVICE(MRVL_VENDOR_ID, SD_DEVICE_ID_8801)}, |
| #endif |
| #ifdef SD8897 |
| {SDIO_DEVICE(MRVL_VENDOR_ID, SD_DEVICE_ID_8897)}, |
| #endif |
| #ifdef SD8977 |
| {SDIO_DEVICE(MRVL_VENDOR_ID, SD_DEVICE_ID_8977)}, |
| #endif |
| #ifdef SD8978 |
| {SDIO_DEVICE(MRVL_VENDOR_ID, SD_DEVICE_ID_8978)}, |
| #endif |
| #ifdef SD8997 |
| {SDIO_DEVICE(MRVL_VENDOR_ID, SD_DEVICE_ID_8997)}, |
| #endif |
| #ifdef SD8987 |
| {SDIO_DEVICE(MRVL_VENDOR_ID, SD_DEVICE_ID_8987)}, |
| #endif |
| #ifdef SD9098 |
| {SDIO_DEVICE(MRVL_VENDOR_ID, SD_DEVICE_ID_9098_FN1)}, |
| {SDIO_DEVICE(MRVL_VENDOR_ID, SD_DEVICE_ID_9098_FN2)}, |
| #endif |
| #ifdef SD9097 |
| {SDIO_DEVICE(MRVL_VENDOR_ID, SD_DEVICE_ID_9097)}, |
| #endif |
| #ifdef SD9177 |
| {SDIO_DEVICE(NXP_VENDOR_ID, SD_DEVICE_ID_9177)}, |
| #endif |
| {}, |
| }; |
| |
| MODULE_DEVICE_TABLE(sdio, wlan_ids); |
| |
| int woal_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id); |
| void woal_sdio_remove(struct sdio_func *func); |
| #ifdef SDIO |
| static void woal_sdiommc_reg_dbg(pmoal_handle handle); |
| #endif |
| |
| #ifdef SDIO_SUSPEND_RESUME |
| #ifdef MMC_PM_KEEP_POWER |
| int woal_sdio_suspend(struct device *dev); |
| int woal_sdio_resume(struct device *dev); |
| |
| static struct dev_pm_ops wlan_sdio_pm_ops = { |
| .suspend = woal_sdio_suspend, |
| .resume = woal_sdio_resume, |
| }; |
| |
| void woal_sdio_shutdown(struct device *dev); |
| #endif |
| #endif |
| |
| // clang-format off |
| static struct sdio_driver REFDATA wlan_sdio = { |
| .name = "wlan_sdio", |
| .id_table = wlan_ids, |
| .probe = woal_sdio_probe, |
| .remove = woal_sdio_remove, |
| #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) |
| .drv = { |
| .owner = THIS_MODULE, |
| #ifdef SDIO_SUSPEND_RESUME |
| #ifdef MMC_PM_KEEP_POWER |
| .pm = &wlan_sdio_pm_ops, |
| .shutdown = woal_sdio_shutdown, |
| #endif |
| #endif |
| |
| } |
| #else |
| #ifdef SDIO_SUSPEND_RESUME |
| #ifdef MMC_PM_KEEP_POWER |
| .drv = { |
| .pm = &wlan_sdio_pm_ops, |
| .shutdown = woal_sdio_shutdown, |
| } |
| #endif |
| #endif |
| #endif |
| }; |
| |
| // clang-format on |
| |
| /******************************************************** |
| Local Functions |
| ********************************************************/ |
| static void woal_sdiommc_dump_fw_info(moal_handle *phandle); |
| #if 0 |
| /** @brief This function dump the sdio register |
| * |
| * @param handle A Pointer to the moal_handle structure |
| * @return N/A |
| */ |
| static void |
| woal_dump_sdio_reg(moal_handle *handle) |
| { |
| int ret = 0; |
| t_u8 data, i; |
| int fun0_reg[] = { 0x05, 0x04 }; |
| t_u8 array_size = 0; |
| #ifdef SD8897 |
| int fun1_reg_8897[] = { 0x03, 0x04, 0x05, 0x06, 0x07, 0xC0, 0xC1 }; |
| #endif |
| int fun1_reg_other[] = { 0x03, 0x04, 0x05, 0x60, 0x61 }; |
| int *fun1_reg = NULL; |
| |
| for (i = 0; i < ARRAY_SIZE(fun0_reg); i++) { |
| data = sdio_f0_readb(((struct sdio_mmc_card *)handle->card)-> |
| func, fun0_reg[i], &ret); |
| PRINTM(MMSG, "fun0: reg 0x%02x=0x%02x ret=%d\n", fun0_reg[i], |
| data, ret); |
| } |
| |
| #ifdef SD8897 |
| if (IS_SD8897(handle->card_type)) { |
| fun1_reg = fun1_reg_8897; |
| array_size = sizeof(fun1_reg_8897) / sizeof(int); |
| } else { |
| #endif |
| fun1_reg = fun1_reg_other; |
| array_size = sizeof(fun1_reg_other) / sizeof(int); |
| #ifdef SD8897 |
| } |
| #endif |
| for (i = 0; i < array_size; i++) { |
| data = sdio_readb(((struct sdio_mmc_card *)handle->card)->func, |
| fun1_reg[i], &ret); |
| PRINTM(MMSG, "fun1: reg 0x%02x=0x%02x ret=%d\n", fun1_reg[i], |
| data, ret); |
| } |
| return; |
| } |
| #endif |
| /******************************************************** |
| Global Functions |
| ********************************************************/ |
| /** |
| * @brief This function handles the interrupt. |
| * |
| * @param func A pointer to the sdio_func structure |
| * @return N/A |
| */ |
| static void |
| woal_sdio_interrupt(struct sdio_func *func) |
| { |
| moal_handle *handle; |
| struct sdio_mmc_card *card; |
| |
| ENTER(); |
| |
| card = sdio_get_drvdata(func); |
| if (!card || !card->handle) { |
| PRINTM(MINFO, |
| "sdio_mmc_interrupt(func = %p) card or handle is NULL, card=%p\n", |
| func, card); |
| LEAVE(); |
| return; |
| } |
| handle = card->handle; |
| if (handle->surprise_removed == MTRUE) { |
| LEAVE(); |
| return; |
| } |
| handle->main_state = MOAL_RECV_INT; |
| PRINTM(MINFO, "*** IN SDIO IRQ ***\n"); |
| PRINTM(MINTR, "*\n"); |
| |
| /* call mlan_interrupt to read int status */ |
| mlan_interrupt(0, handle->pmlan_adapter); |
| #ifdef SDIO_SUSPEND_RESUME |
| if (handle->is_suspended) { |
| PRINTM(MINTR, "Receive interrupt in hs_suspended\n"); |
| LEAVE(); |
| return; |
| } |
| #endif |
| handle->main_state = MOAL_START_MAIN_PROCESS; |
| /* Call MLAN main process */ |
| mlan_main_process(handle->pmlan_adapter); |
| handle->main_state = MOAL_END_MAIN_PROCESS; |
| LEAVE(); |
| } |
| |
| /** @brief This function updates the card types |
| * |
| * @param handle A Pointer to the moal_handle structure |
| * @param card A Pointer to card |
| * |
| * @return N/A |
| */ |
| static t_u16 |
| woal_update_card_type(t_void *card) |
| { |
| struct sdio_mmc_card *cardp_sd = (struct sdio_mmc_card *)card; |
| t_u16 card_type = 0; |
| |
| /* Update card type */ |
| #ifdef SD8887 |
| if (cardp_sd->func->device == SD_DEVICE_ID_8887) { |
| card_type = CARD_TYPE_SD8887; |
| moal_memcpy_ext(NULL, driver_version, CARD_SD8887, |
| strlen(CARD_SD8887), strlen(driver_version)); |
| moal_memcpy_ext(NULL, |
| driver_version + strlen(INTF_CARDTYPE) + |
| strlen(KERN_VERSION), |
| V15, strlen(V15), |
| strlen(driver_version) - |
| (strlen(INTF_CARDTYPE) + strlen(KERN_VERSION))); |
| } |
| #endif |
| #ifdef SD8801 |
| if (cardp_sd->func->device == SD_DEVICE_ID_8801) { |
| card_type = CARD_TYPE_SD8801; |
| moal_memcpy_ext(NULL, driver_version, CARD_SD8801, |
| strlen(CARD_SD8801), strlen(driver_version)); |
| moal_memcpy_ext(NULL, |
| driver_version + strlen(INTF_CARDTYPE) + |
| strlen(KERN_VERSION), |
| V14, strlen(V14), |
| strlen(driver_version) - |
| (strlen(INTF_CARDTYPE) + strlen(KERN_VERSION))); |
| } |
| #endif |
| |
| #ifdef SD8897 |
| if (cardp_sd->func->device == SD_DEVICE_ID_8897) { |
| card_type = CARD_TYPE_SD8897; |
| moal_memcpy_ext(NULL, driver_version, CARD_SD8897, |
| strlen(CARD_SD8897), strlen(driver_version)); |
| moal_memcpy_ext(NULL, |
| driver_version + strlen(INTF_CARDTYPE) + |
| strlen(KERN_VERSION), |
| V15, strlen(V15), |
| strlen(driver_version) - |
| (strlen(INTF_CARDTYPE) + strlen(KERN_VERSION))); |
| } |
| #endif |
| #ifdef SD8977 |
| if (cardp_sd->func->device == SD_DEVICE_ID_8977) { |
| card_type = CARD_TYPE_SD8977; |
| moal_memcpy_ext(NULL, driver_version, CARD_SD8977, |
| strlen(CARD_SD8977), strlen(driver_version)); |
| moal_memcpy_ext(NULL, |
| driver_version + strlen(INTF_CARDTYPE) + |
| strlen(KERN_VERSION), |
| V16, strlen(V16), |
| strlen(driver_version) - |
| (strlen(INTF_CARDTYPE) + strlen(KERN_VERSION))); |
| } |
| #endif |
| #ifdef SD8978 |
| if (cardp_sd->func->device == SD_DEVICE_ID_8978) { |
| card_type = CARD_TYPE_SD8978; |
| moal_memcpy_ext(NULL, driver_version, "SDIW416", |
| strlen("SDIW416"), strlen(driver_version)); |
| moal_memcpy_ext(NULL, |
| driver_version + strlen(INTF_CARDTYPE) + |
| strlen(KERN_VERSION), |
| V16, strlen(V16), |
| strlen(driver_version) - |
| (strlen(INTF_CARDTYPE) + strlen(KERN_VERSION))); |
| } |
| #endif |
| #ifdef SD8997 |
| if (cardp_sd->func->device == SD_DEVICE_ID_8997) { |
| card_type = CARD_TYPE_SD8997; |
| moal_memcpy_ext(NULL, driver_version, CARD_SD8997, |
| strlen(CARD_SD8997), strlen(driver_version)); |
| moal_memcpy_ext(NULL, |
| driver_version + strlen(INTF_CARDTYPE) + |
| strlen(KERN_VERSION), |
| V16, strlen(V16), |
| strlen(driver_version) - |
| (strlen(INTF_CARDTYPE) + strlen(KERN_VERSION))); |
| } |
| #endif |
| #ifdef SD8987 |
| if (cardp_sd->func->device == SD_DEVICE_ID_8987) { |
| card_type = CARD_TYPE_SD8987; |
| moal_memcpy_ext(NULL, driver_version, CARD_SD8987, |
| strlen(CARD_SD8987), strlen(driver_version)); |
| moal_memcpy_ext(NULL, |
| driver_version + strlen(INTF_CARDTYPE) + |
| strlen(KERN_VERSION), |
| V16, strlen(V16), |
| strlen(driver_version) - |
| (strlen(INTF_CARDTYPE) + strlen(KERN_VERSION))); |
| } |
| #endif |
| #ifdef SD9097 |
| if (cardp_sd->func->device == SD_DEVICE_ID_9097) { |
| card_type = CARD_TYPE_SD9097; |
| moal_memcpy_ext(NULL, driver_version, CARD_SD9097, |
| strlen(CARD_SD9097), strlen(driver_version)); |
| moal_memcpy_ext(NULL, |
| driver_version + strlen(INTF_CARDTYPE) + |
| strlen(KERN_VERSION), |
| V17, strlen(V17), |
| strlen(driver_version) - |
| (strlen(INTF_CARDTYPE) + strlen(KERN_VERSION))); |
| } |
| #endif |
| #ifdef SD9098 |
| if (cardp_sd->func->device == SD_DEVICE_ID_9098_FN1 || |
| cardp_sd->func->device == SD_DEVICE_ID_9098_FN2) { |
| card_type = CARD_TYPE_SD9098; |
| moal_memcpy_ext(NULL, driver_version, CARD_SD9098, |
| strlen(CARD_SD9098), strlen(driver_version)); |
| moal_memcpy_ext(NULL, |
| driver_version + strlen(INTF_CARDTYPE) + |
| strlen(KERN_VERSION), |
| V17, strlen(V17), |
| strlen(driver_version) - |
| (strlen(INTF_CARDTYPE) + strlen(KERN_VERSION))); |
| } |
| #endif |
| #ifdef SD9177 |
| if (cardp_sd->func->device == SD_DEVICE_ID_9177) { |
| card_type = CARD_TYPE_SD9177; |
| moal_memcpy_ext(NULL, driver_version, CARD_SD9177, |
| strlen(CARD_SD9177), strlen(driver_version)); |
| moal_memcpy_ext(NULL, |
| driver_version + strlen(INTF_CARDTYPE) + |
| strlen(KERN_VERSION), |
| V18, strlen(V18), |
| strlen(driver_version) - |
| (strlen(INTF_CARDTYPE) + strlen(KERN_VERSION))); |
| } |
| #endif |
| return card_type; |
| } |
| |
| /** @brief This function handles client driver probe. |
| * |
| * @param func A pointer to sdio_func structure. |
| * @param id A pointer to sdio_device_id structure. |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE/error code |
| */ |
| int |
| woal_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) |
| { |
| int ret = MLAN_STATUS_SUCCESS; |
| struct sdio_mmc_card *card = NULL; |
| t_u16 card_type = 0; |
| |
| ENTER(); |
| |
| PRINTM(MMSG, "vendor=0x%4.04X device=0x%4.04X class=%d function=%d\n", |
| func->vendor, func->device, func->class, func->num); |
| |
| card = kzalloc(sizeof(struct sdio_mmc_card), GFP_KERNEL); |
| if (!card) { |
| PRINTM(MFATAL, |
| "Failed to allocate memory in probe function!\n"); |
| LEAVE(); |
| return -ENOMEM; |
| } |
| |
| card->func = func; |
| |
| #ifdef MMC_QUIRK_BLKSZ_FOR_BYTE_MODE |
| /* The byte mode patch is available in kernel MMC driver |
| * which fixes one issue in MP-A transfer. |
| * bit1: use func->cur_blksize for byte mode |
| */ |
| func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE; |
| #endif |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) |
| func->card->quirks |= MMC_QUIRK_LENIENT_FN0; |
| #endif |
| |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) |
| /* wait for chip fully wake up */ |
| if (!func->enable_timeout) |
| func->enable_timeout = 200; |
| #endif |
| sdio_claim_host(func); |
| ret = sdio_enable_func(func); |
| if (ret) { |
| sdio_release_host(func); |
| PRINTM(MFATAL, "sdio_enable_func() failed: ret=%d\n", ret); |
| ret = -EIO; |
| goto err; |
| } |
| sdio_release_host(func); |
| |
| card_type = woal_update_card_type(card); |
| if (!card_type) { |
| PRINTM(MERROR, "sdmmc probe: woal_update_card_type() failed\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto err; |
| } |
| |
| if (NULL == |
| woal_add_card(card, &card->func->dev, &sdiommc_ops, card_type)) { |
| PRINTM(MERROR, "woal_add_card failed\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto err; |
| } |
| #ifdef IMX_SUPPORT |
| woal_regist_oob_wakeup_irq(card->handle); |
| #endif /* IMX_SUPPORT */ |
| |
| LEAVE(); |
| return ret; |
| err: |
| kfree(card); |
| sdio_claim_host(func); |
| sdio_disable_func(func); |
| sdio_release_host(func); |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| /** @brief This function handles client driver remove. |
| * |
| * @param func A pointer to sdio_func structure. |
| * @return N/A |
| */ |
| void |
| woal_sdio_remove(struct sdio_func *func) |
| { |
| struct sdio_mmc_card *card; |
| |
| ENTER(); |
| |
| if (func) { |
| PRINTM(MINFO, "SDIO func=%d\n", func->num); |
| card = sdio_get_drvdata(func); |
| if (card) { |
| #ifdef IMX_SUPPORT |
| woal_unregist_oob_wakeup_irq(card->handle); |
| #endif /* IMX_SUPPORT */ |
| woal_remove_card(card); |
| kfree(card); |
| } |
| } |
| |
| LEAVE(); |
| } |
| |
| #ifdef SDIO_SUSPEND_RESUME |
| #ifdef MMC_PM_KEEP_POWER |
| #ifdef MMC_PM_FUNC_SUSPENDED |
| /** @brief This function tells lower driver that WLAN is suspended |
| * |
| * @param handle A Pointer to the moal_handle structure |
| * @return N/A |
| */ |
| void |
| woal_wlan_is_suspended(moal_handle *handle) |
| { |
| ENTER(); |
| if (handle->suspend_notify_req == MTRUE) { |
| handle->is_suspended = MTRUE; |
| sdio_func_suspended(((struct sdio_mmc_card *)handle->card)-> |
| func); |
| } |
| LEAVE(); |
| } |
| #endif |
| |
| /** @brief This function handles client driver shutdown |
| * |
| * @param dev A pointer to device structure |
| * @return N/A |
| */ |
| void |
| woal_sdio_shutdown(struct device *dev) |
| { |
| struct sdio_func *func = dev_to_sdio_func(dev); |
| moal_handle *handle = NULL; |
| struct sdio_mmc_card *cardp; |
| mlan_ds_ps_info pm_info; |
| int i, retry_num = 8; |
| |
| ENTER(); |
| PRINTM(MCMND, "<--- Enter woal_sdio_shutdown --->\n"); |
| cardp = sdio_get_drvdata(func); |
| if (!cardp || !cardp->handle) { |
| PRINTM(MERROR, "Card or moal_handle structure is not valid\n"); |
| LEAVE(); |
| return; |
| } |
| handle = cardp->handle; |
| for (i = 0; i < handle->priv_num; i++) |
| netif_device_detach(handle->priv[i]->netdev); |
| |
| if (moal_extflg_isset(handle, EXT_SHUTDOWN_HS)) { |
| handle->shutdown_hs_in_process = MTRUE; |
| memset(&pm_info, 0, sizeof(pm_info)); |
| for (i = 0; i < retry_num; i++) { |
| if (MLAN_STATUS_SUCCESS == |
| woal_get_pm_info(woal_get_priv(handle, |
| MLAN_BSS_ROLE_ANY), |
| &pm_info)) { |
| if (pm_info.is_suspend_allowed == MTRUE) |
| break; |
| else |
| PRINTM(MMSG, |
| "Shutdown not allowed and retry again\n"); |
| } |
| woal_sched_timeout(100); |
| } |
| if (pm_info.is_suspend_allowed == MFALSE) { |
| PRINTM(MMSG, "Shutdown not allowed\n"); |
| goto done; |
| } |
| woal_enable_hs(woal_get_priv(handle, MLAN_BSS_ROLE_ANY)); |
| |
| wait_event_interruptible_timeout(handle->hs_activate_wait_q, |
| handle-> |
| hs_activate_wait_q_woken, |
| HS_ACTIVE_TIMEOUT); |
| if (handle->hs_activated == MTRUE) |
| PRINTM(MMSG, "HS actived in shutdown\n"); |
| else |
| PRINTM(MMSG, "Fail to enable HS in shutdown\n"); |
| } else { |
| for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { |
| if (handle->priv[i]) { |
| if (handle->priv[i]->media_connected == MTRUE |
| #ifdef UAP_SUPPORT |
| || (GET_BSS_ROLE(handle->priv[i]) == |
| MLAN_BSS_ROLE_UAP) |
| #endif |
| ) { |
| PRINTM(MIOCTL, |
| "disconnect on suspend\n"); |
| woal_disconnect(handle->priv[i], |
| MOAL_NO_WAIT, NULL, |
| DEF_DEAUTH_REASON_CODE); |
| } |
| } |
| } |
| } |
| |
| done: |
| PRINTM(MCMND, "<--- Leave woal_sdio_shutdown --->\n"); |
| LEAVE(); |
| return; |
| } |
| |
| /** @brief This function handles client driver suspend |
| * |
| * @param dev A pointer to device structure |
| * @return MLAN_STATUS_SUCCESS or error code |
| */ |
| int |
| woal_sdio_suspend(struct device *dev) |
| { |
| struct sdio_func *func = dev_to_sdio_func(dev); |
| mmc_pm_flag_t pm_flags = 0; |
| moal_handle *handle = NULL; |
| struct sdio_mmc_card *cardp; |
| int i, retry_num = 8; |
| int ret = MLAN_STATUS_SUCCESS; |
| int hs_actived = 0; |
| mlan_ds_ps_info pm_info; |
| |
| ENTER(); |
| PRINTM(MCMND, "<--- Enter woal_sdio_suspend --->\n"); |
| pm_flags = sdio_get_host_pm_caps(func); |
| PRINTM(MCMND, "%s: suspend: PM flags = 0x%x\n", sdio_func_id(func), |
| pm_flags); |
| cardp = sdio_get_drvdata(func); |
| if (!cardp || !cardp->handle) { |
| PRINTM(MERROR, "Card or moal_handle structure is not valid\n"); |
| LEAVE(); |
| return MLAN_STATUS_SUCCESS; |
| } |
| |
| handle = cardp->handle; |
| |
| if (moal_extflg_isset(handle, EXT_PM_KEEP_POWER) && |
| !(pm_flags & MMC_PM_KEEP_POWER)) { |
| PRINTM(MERROR, |
| "%s: cannot remain alive while host is suspended\n", |
| sdio_func_id(func)); |
| LEAVE(); |
| return -ENOSYS; |
| } |
| if (handle->is_suspended == MTRUE) { |
| PRINTM(MWARN, "Device already suspended\n"); |
| LEAVE(); |
| return MLAN_STATUS_SUCCESS; |
| } |
| if (handle->fw_dump) { |
| PRINTM(MMSG, "suspend not allowed while FW dump!"); |
| ret = -EBUSY; |
| goto done; |
| } |
| #ifdef STA_SUPPORT |
| for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { |
| if (handle->priv[i] && |
| (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA)) |
| woal_cancel_scan(handle->priv[i], MOAL_IOCTL_WAIT); |
| } |
| #endif |
| handle->suspend_fail = MFALSE; |
| memset(&pm_info, 0, sizeof(pm_info)); |
| for (i = 0; i < retry_num; i++) { |
| if (MLAN_STATUS_SUCCESS == |
| woal_get_pm_info(woal_get_priv(handle, MLAN_BSS_ROLE_ANY), |
| &pm_info)) { |
| if (pm_info.is_suspend_allowed == MTRUE) |
| break; |
| else |
| PRINTM(MMSG, |
| "Suspend not allowed and retry again\n"); |
| } |
| woal_sched_timeout(100); |
| } |
| if (pm_info.is_suspend_allowed == MFALSE) { |
| PRINTM(MMSG, "Suspend not allowed\n"); |
| ret = -EBUSY; |
| goto done; |
| } |
| |
| for (i = 0; i < handle->priv_num; i++) |
| netif_device_detach(handle->priv[i]->netdev); |
| |
| if (moal_extflg_isset(handle, EXT_PM_KEEP_POWER)) { |
| /* Enable the Host Sleep */ |
| #ifdef MMC_PM_FUNC_SUSPENDED |
| handle->suspend_notify_req = MTRUE; |
| #endif |
| hs_actived = |
| woal_enable_hs(woal_get_priv |
| (handle, MLAN_BSS_ROLE_ANY)); |
| #ifdef MMC_PM_FUNC_SUSPENDED |
| handle->suspend_notify_req = MFALSE; |
| #endif |
| if (hs_actived) { |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 4) |
| if (pm_flags & MMC_PM_WAKE_SDIO_IRQ) { |
| ret = sdio_set_host_pm_flags(func, |
| MMC_PM_WAKE_SDIO_IRQ); |
| PRINTM(MCMND, |
| "suspend with MMC_PM_WAKE_SDIO_IRQ ret=%d\n", |
| ret); |
| } |
| #endif |
| #ifdef MMC_PM_SKIP_RESUME_PROBE |
| PRINTM(MCMND, |
| "suspend with MMC_PM_KEEP_POWER and MMC_PM_SKIP_RESUME_PROBE\n"); |
| ret = sdio_set_host_pm_flags(func, |
| MMC_PM_KEEP_POWER | |
| MMC_PM_SKIP_RESUME_PROBE); |
| #else |
| PRINTM(MCMND, "suspend with MMC_PM_KEEP_POWER\n"); |
| ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); |
| #endif |
| } else { |
| PRINTM(MMSG, "HS not actived, suspend fail!"); |
| handle->suspend_fail = MTRUE; |
| for (i = 0; i < handle->priv_num; i++) |
| netif_device_attach(handle->priv[i]->netdev); |
| ret = -EBUSY; |
| goto done; |
| } |
| } |
| |
| /* Indicate device suspended */ |
| handle->is_suspended = MTRUE; |
| #ifdef IMX_SUPPORT |
| woal_enable_oob_wakeup_irq(handle); |
| #endif /* IMX_SUPPORT */ |
| done: |
| PRINTM(MCMND, "<--- Leave woal_sdio_suspend --->\n"); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** @brief This function handles client driver resume |
| * |
| * @param dev A pointer to device structure |
| * @return MLAN_STATUS_SUCCESS |
| */ |
| int |
| woal_sdio_resume(struct device *dev) |
| { |
| struct sdio_func *func = dev_to_sdio_func(dev); |
| mmc_pm_flag_t pm_flags = 0; |
| moal_handle *handle = NULL; |
| struct sdio_mmc_card *cardp; |
| int i; |
| |
| ENTER(); |
| PRINTM(MCMND, "<--- Enter woal_sdio_resume --->\n"); |
| pm_flags = sdio_get_host_pm_caps(func); |
| PRINTM(MCMND, "%s: resume: PM flags = 0x%x\n", sdio_func_id(func), |
| pm_flags); |
| cardp = sdio_get_drvdata(func); |
| if (!cardp || !cardp->handle) { |
| PRINTM(MERROR, "Card or moal_handle structure is not valid\n"); |
| LEAVE(); |
| return MLAN_STATUS_SUCCESS; |
| } |
| handle = cardp->handle; |
| |
| if (handle->is_suspended == MFALSE) { |
| PRINTM(MWARN, "Device already resumed\n"); |
| LEAVE(); |
| return MLAN_STATUS_SUCCESS; |
| } |
| handle->is_suspended = MFALSE; |
| if (woal_check_driver_status(handle)) { |
| PRINTM(MERROR, "Resuem, device is in hang state\n"); |
| LEAVE(); |
| return MLAN_STATUS_SUCCESS; |
| } |
| for (i = 0; i < handle->priv_num; i++) |
| netif_device_attach(handle->priv[i]->netdev); |
| |
| /* Disable Host Sleep */ |
| woal_cancel_hs(woal_get_priv(handle, MLAN_BSS_ROLE_ANY), MOAL_NO_WAIT); |
| #ifdef IMX_SUPPORT |
| woal_disable_oob_wakeup_irq(handle); |
| #endif /* IMX_SUPPORT */ |
| PRINTM(MCMND, "<--- Leave woal_sdio_resume --->\n"); |
| LEAVE(); |
| return MLAN_STATUS_SUCCESS; |
| } |
| #endif |
| #endif /* SDIO_SUSPEND_RESUME */ |
| |
| /** |
| * @brief This function writes data into card register |
| * |
| * @param handle A Pointer to the moal_handle structure |
| * @param reg Register offset |
| * @param data Value |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| static mlan_status |
| woal_sdiommc_write_reg(moal_handle *handle, t_u32 reg, t_u32 data) |
| { |
| mlan_status ret = MLAN_STATUS_FAILURE; |
| sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); |
| sdio_writeb(((struct sdio_mmc_card *)handle->card)->func, (t_u8)data, |
| reg, (int *)&ret); |
| sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); |
| return ret; |
| } |
| |
| /** |
| * @brief This function reads data from card register |
| * |
| * @param handle A Pointer to the moal_handle structure |
| * @param reg Register offset |
| * @param data Value |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| static mlan_status |
| woal_sdiommc_read_reg(moal_handle *handle, t_u32 reg, t_u32 *data) |
| { |
| mlan_status ret = MLAN_STATUS_FAILURE; |
| t_u8 val; |
| sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); |
| val = sdio_readb(((struct sdio_mmc_card *)handle->card)->func, reg, |
| (int *)&ret); |
| sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); |
| *data = val; |
| |
| return ret; |
| } |
| |
| /** |
| * @brief This function writes data into card register |
| * |
| * @param handle A Pointer to the moal_handle structure |
| * @param reg Register offset |
| * @param data Value |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| static mlan_status |
| woal_sdio_writeb(moal_handle *handle, t_u32 reg, t_u8 data) |
| { |
| mlan_status ret = MLAN_STATUS_FAILURE; |
| sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); |
| sdio_writeb(((struct sdio_mmc_card *)handle->card)->func, (t_u8)data, |
| reg, (int *)&ret); |
| sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); |
| return ret; |
| } |
| |
| /** |
| * @brief This function reads data from card register |
| * |
| * @param handle A Pointer to the moal_handle structure |
| * @param reg Register offset |
| * @param data Value |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| static mlan_status |
| woal_sdio_readb(moal_handle *handle, t_u32 reg, t_u8 *data) |
| { |
| mlan_status ret = MLAN_STATUS_FAILURE; |
| t_u8 val; |
| sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); |
| val = sdio_readb(((struct sdio_mmc_card *)handle->card)->func, reg, |
| (int *)&ret); |
| sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); |
| *data = val; |
| |
| return ret; |
| } |
| |
| /** |
| * @brief This function reads data from card register FN0 |
| * |
| * @param handle A Pointer to the moal_handle structure |
| * @param reg Register offset |
| * @param data Value |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| static mlan_status |
| woal_sdio_f0_readb(moal_handle *handle, t_u32 reg, t_u8 *data) |
| { |
| mlan_status ret = MLAN_STATUS_FAILURE; |
| t_u8 val; |
| sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); |
| val = sdio_f0_readb(((struct sdio_mmc_card *)handle->card)->func, reg, |
| (int *)&ret); |
| sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); |
| *data = val; |
| |
| return ret; |
| } |
| |
| /** |
| * @brief This function use SG mode to read/write data into card memory |
| * |
| * @param handle A Pointer to the moal_handle structure |
| * @param pmbuf_list Pointer to a linked list of mlan_buffer structure |
| * @param port Port |
| * @param write write flag |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| static mlan_status |
| woal_sdio_rw_mb(moal_handle *handle, pmlan_buffer pmbuf_list, |
| t_u32 port, t_u8 write) |
| { |
| struct scatterlist sg_list[SDIO_MP_AGGR_DEF_PKT_LIMIT_MAX]; |
| int num_sg = pmbuf_list->use_count; |
| int i = 0; |
| mlan_buffer *pmbuf = NULL; |
| struct mmc_request mmc_req; |
| struct mmc_command mmc_cmd; |
| struct mmc_data mmc_dat; |
| struct sdio_func *func = ((struct sdio_mmc_card *)handle->card)->func; |
| t_u32 ioport = (port & MLAN_SDIO_IO_PORT_MASK); |
| t_u32 blkcnt = pmbuf_list->data_len / MLAN_SDIO_BLOCK_SIZE; |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) |
| int status; |
| #endif |
| |
| if (num_sg > SDIO_MP_AGGR_DEF_PKT_LIMIT_MAX) { |
| PRINTM(MERROR, "ERROR: num_sg=%d", num_sg); |
| return MLAN_STATUS_FAILURE; |
| } |
| sg_init_table(sg_list, num_sg); |
| pmbuf = pmbuf_list->pnext; |
| for (i = 0; i < num_sg; i++) { |
| if (pmbuf == pmbuf_list) |
| break; |
| sg_set_buf(&sg_list[i], pmbuf->pbuf + pmbuf->data_offset, |
| pmbuf->data_len); |
| pmbuf = pmbuf->pnext; |
| } |
| memset(&mmc_req, 0, sizeof(struct mmc_request)); |
| memset(&mmc_cmd, 0, sizeof(struct mmc_command)); |
| memset(&mmc_dat, 0, sizeof(struct mmc_data)); |
| |
| mmc_dat.sg = sg_list; |
| mmc_dat.sg_len = num_sg; |
| mmc_dat.blksz = MLAN_SDIO_BLOCK_SIZE; |
| mmc_dat.blocks = blkcnt; |
| mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; |
| |
| mmc_cmd.opcode = SD_IO_RW_EXTENDED; |
| mmc_cmd.arg = write ? 1 << 31 : 0; |
| mmc_cmd.arg |= (func->num & 0x7) << 28; |
| mmc_cmd.arg |= 1 << 27; /* block basic */ |
| mmc_cmd.arg |= 0; /* fix address */ |
| mmc_cmd.arg |= (ioport & 0x1FFFF) << 9; |
| mmc_cmd.arg |= blkcnt & 0x1FF; |
| mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; |
| |
| mmc_req.cmd = &mmc_cmd; |
| mmc_req.data = &mmc_dat; |
| |
| sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); |
| mmc_set_data_timeout(&mmc_dat, |
| ((struct sdio_mmc_card *)handle->card)->func-> |
| card); |
| mmc_wait_for_req(((struct sdio_mmc_card *)handle->card)->func->card-> |
| host, &mmc_req); |
| |
| if (mmc_cmd.error || mmc_dat.error) { |
| PRINTM(MERROR, "CMD53 %s cmd_error = %d data_error=%d\n", |
| write ? "write" : "read", mmc_cmd.error, mmc_dat.error); |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) |
| /* issue abort cmd52 command through F0 */ |
| sdio_f0_writeb(((struct sdio_mmc_card *)handle->card)->func, |
| 0x01, SDIO_CCCR_ABORT, &status); |
| #endif |
| sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); |
| return MLAN_STATUS_FAILURE; |
| } |
| sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); |
| return MLAN_STATUS_SUCCESS; |
| } |
| |
| /** |
| * @brief This function writes multiple bytes into card memory |
| * |
| * @param handle A Pointer to the moal_handle structure |
| * @param pmbuf Pointer to mlan_buffer structure |
| * @param port Port |
| * @param timeout Time out value |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| static mlan_status |
| woal_sdiommc_write_data_sync(moal_handle *handle, |
| mlan_buffer *pmbuf, t_u32 port, t_u32 timeout) |
| { |
| mlan_status ret = MLAN_STATUS_FAILURE; |
| t_u8 *buffer = (t_u8 *)(pmbuf->pbuf + pmbuf->data_offset); |
| t_u8 blkmode = |
| (port & MLAN_SDIO_BYTE_MODE_MASK) ? BYTE_MODE : BLOCK_MODE; |
| t_u32 blksz = (blkmode == BLOCK_MODE) ? MLAN_SDIO_BLOCK_SIZE : 1; |
| t_u32 blkcnt = (blkmode == BLOCK_MODE) ? |
| (pmbuf->data_len / MLAN_SDIO_BLOCK_SIZE) : pmbuf->data_len; |
| t_u32 ioport = (port & MLAN_SDIO_IO_PORT_MASK); |
| int status = 0; |
| if (pmbuf->use_count > 1) |
| return woal_sdio_rw_mb(handle, pmbuf, port, MTRUE); |
| #ifdef SDIO_MMC_DEBUG |
| handle->cmd53w = 1; |
| #endif |
| sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); |
| status = sdio_writesb(((struct sdio_mmc_card *)handle->card)->func, |
| ioport, buffer, blkcnt * blksz); |
| if (!status) |
| ret = MLAN_STATUS_SUCCESS; |
| else { |
| PRINTM(MERROR, "cmd53 write error=%d\n", status); |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) |
| /* issue abort cmd52 command through F0 */ |
| sdio_f0_writeb(((struct sdio_mmc_card *)handle->card)->func, |
| 0x01, SDIO_CCCR_ABORT, &status); |
| #endif |
| } |
| sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); |
| #ifdef SDIO_MMC_DEBUG |
| handle->cmd53w = 2; |
| #endif |
| return ret; |
| } |
| |
| /** |
| * @brief This function reads multiple bytes from card memory |
| * |
| * @param handle A Pointer to the moal_handle structure |
| * @param pmbuf Pointer to mlan_buffer structure |
| * @param port Port |
| * @param timeout Time out value |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| static mlan_status |
| woal_sdiommc_read_data_sync(moal_handle *handle, |
| mlan_buffer *pmbuf, t_u32 port, t_u32 timeout) |
| { |
| mlan_status ret = MLAN_STATUS_FAILURE; |
| t_u8 *buffer = (t_u8 *)(pmbuf->pbuf + pmbuf->data_offset); |
| t_u8 blkmode = |
| (port & MLAN_SDIO_BYTE_MODE_MASK) ? BYTE_MODE : BLOCK_MODE; |
| t_u32 blksz = (blkmode == BLOCK_MODE) ? MLAN_SDIO_BLOCK_SIZE : 1; |
| t_u32 blkcnt = (blkmode == BLOCK_MODE) ? |
| (pmbuf->data_len / MLAN_SDIO_BLOCK_SIZE) : pmbuf->data_len; |
| t_u32 ioport = (port & MLAN_SDIO_IO_PORT_MASK); |
| int status = 0; |
| if (pmbuf->use_count > 1) |
| return woal_sdio_rw_mb(handle, pmbuf, port, MFALSE); |
| #ifdef SDIO_MMC_DEBUG |
| handle->cmd53r = 1; |
| #endif |
| sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); |
| status = sdio_readsb(((struct sdio_mmc_card *)handle->card)->func, |
| buffer, ioport, blkcnt * blksz); |
| if (!status) { |
| ret = MLAN_STATUS_SUCCESS; |
| } else { |
| PRINTM(MERROR, "cmd53 read error=%d\n", status); |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) |
| /* issue abort cmd52 command through F0 */ |
| sdio_f0_writeb(((struct sdio_mmc_card *)handle->card)->func, |
| 0x01, SDIO_CCCR_ABORT, &status); |
| #endif |
| } |
| sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); |
| #ifdef SDIO_MMC_DEBUG |
| handle->cmd53r = 2; |
| #endif |
| return ret; |
| } |
| |
| /** |
| * @brief This function registers the IF module in bus driver |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| mlan_status |
| woal_sdiommc_bus_register(void) |
| { |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| /* SDIO Driver Registration */ |
| if (sdio_register_driver(&wlan_sdio)) { |
| PRINTM(MFATAL, "SDIO Driver Registration Failed \n"); |
| LEAVE(); |
| return MLAN_STATUS_FAILURE; |
| } |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief This function de-registers the IF module in bus driver |
| * |
| * @return N/A |
| */ |
| void |
| woal_sdiommc_bus_unregister(void) |
| { |
| ENTER(); |
| |
| /* SDIO Driver Unregistration */ |
| sdio_unregister_driver(&wlan_sdio); |
| |
| LEAVE(); |
| } |
| |
| /** |
| * @brief This function de-registers the device |
| * |
| * @param handle A pointer to moal_handle structure |
| * @return N/A |
| */ |
| static void |
| woal_sdiommc_unregister_dev(moal_handle *handle) |
| { |
| ENTER(); |
| if (handle->card) { |
| struct sdio_mmc_card *card = handle->card; |
| struct sdio_func *func = card->func; |
| |
| /* Release the SDIO IRQ */ |
| sdio_claim_host(card->func); |
| sdio_release_irq(card->func); |
| sdio_disable_func(card->func); |
| if (handle->driver_status) |
| mmc_hw_reset(func->card->host); |
| sdio_release_host(card->func); |
| |
| sdio_set_drvdata(card->func, NULL); |
| |
| PRINTM(MWARN, "Making the sdio dev card as NULL\n"); |
| card->handle = NULL; |
| } |
| |
| LEAVE(); |
| } |
| |
| /** |
| * @brief This function registers the device |
| * |
| * @param handle A pointer to moal_handle structure |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| static mlan_status |
| woal_sdiommc_register_dev(moal_handle *handle) |
| { |
| int ret = MLAN_STATUS_SUCCESS; |
| struct sdio_mmc_card *card = handle->card; |
| struct sdio_func *func; |
| |
| ENTER(); |
| |
| /* save adapter pointer in card */ |
| card->handle = handle; |
| func = card->func; |
| sdio_claim_host(func); |
| /* Request the SDIO IRQ */ |
| ret = sdio_claim_irq(func, woal_sdio_interrupt); |
| if (ret) { |
| PRINTM(MFATAL, "sdio_claim_irq failed: ret=%d\n", ret); |
| goto release_host; |
| } |
| |
| /* Set block size */ |
| ret = sdio_set_block_size(card->func, MLAN_SDIO_BLOCK_SIZE); |
| if (ret) { |
| PRINTM(MERROR, |
| "sdio_set_block_seize(): cannot set SDIO block size\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto release_irq; |
| } |
| |
| sdio_release_host(func); |
| sdio_set_drvdata(func, card); |
| |
| LEAVE(); |
| return MLAN_STATUS_SUCCESS; |
| |
| release_irq: |
| sdio_release_irq(func); |
| release_host: |
| sdio_release_host(func); |
| handle->card = NULL; |
| |
| LEAVE(); |
| return MLAN_STATUS_FAILURE; |
| } |
| |
| /** |
| * @brief This function set bus clock on/off |
| * |
| * @param handle A pointer to moal_handle structure |
| * @param option TRUE--on , FALSE--off |
| * @return MLAN_STATUS_SUCCESS |
| */ |
| int |
| woal_sdio_set_bus_clock(moal_handle *handle, t_u8 option) |
| { |
| struct sdio_mmc_card *cardp = (struct sdio_mmc_card *)handle->card; |
| struct mmc_host *host = cardp->func->card->host; |
| |
| ENTER(); |
| if (option == MTRUE) { |
| /* restore value if non-zero */ |
| if (cardp->host_clock) |
| host->ios.clock = cardp->host_clock; |
| } else { |
| /* backup value if non-zero, then clear */ |
| if (host->ios.clock) |
| cardp->host_clock = host->ios.clock; |
| host->ios.clock = 0; |
| } |
| |
| host->ops->set_ios(host, &host->ios); |
| LEAVE(); |
| return MLAN_STATUS_SUCCESS; |
| } |
| |
| /** |
| * @brief This function updates card reg based on the Cmd52 value in dev |
| * structure |
| * |
| * @param handle A pointer to moal_handle structure |
| * @param func A pointer to store func variable |
| * @param reg A pointer to store reg variable |
| * @param val A pointer to store val variable |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| int |
| woal_sdio_read_write_cmd52(moal_handle *handle, int func, int reg, int val) |
| { |
| int ret = MLAN_STATUS_SUCCESS; |
| struct sdio_mmc_card *card = (struct sdio_mmc_card *)handle->card; |
| |
| ENTER(); |
| /* Save current func and reg for read */ |
| handle->cmd52_func = func; |
| handle->cmd52_reg = reg; |
| sdio_claim_host(card->func); |
| if (val >= 0) { |
| /* Perform actual write only if val is provided */ |
| if (func) |
| sdio_writeb(card->func, val, reg, &ret); |
| else |
| sdio_f0_writeb(card->func, val, reg, &ret); |
| if (ret) { |
| PRINTM(MERROR, |
| "Cannot write value (0x%x) to func %d reg 0x%x\n", |
| val, func, reg); |
| } else { |
| PRINTM(MMSG, "write value (0x%x) to func %d reg 0x%x\n", |
| (u8)val, func, reg); |
| handle->cmd52_val = val; |
| } |
| } else { |
| if (func) |
| val = sdio_readb(card->func, reg, &ret); |
| else |
| val = sdio_f0_readb(card->func, reg, &ret); |
| if (ret) { |
| PRINTM(MERROR, |
| "Cannot read value from func %d reg 0x%x\n", |
| func, reg); |
| } else { |
| PRINTM(MMSG, |
| "read value (0x%x) from func %d reg 0x%x\n", |
| (u8)val, func, reg); |
| handle->cmd52_val = val; |
| } |
| } |
| sdio_release_host(card->func); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief This function check if this is second mac |
| * |
| * @param handle A pointer to moal_handle structure |
| * @return MTRUE/MFALSE |
| * |
| */ |
| static t_u8 |
| woal_sdiommc_is_second_mac(moal_handle *handle) |
| { |
| #ifdef SD9098 |
| struct sdio_mmc_card *card = (struct sdio_mmc_card *)handle->card; |
| if (card->func->device == SD_DEVICE_ID_9098_FN2) |
| return MTRUE; |
| #endif |
| return MFALSE; |
| } |
| |
| static mlan_status |
| woal_sdiommc_get_fw_name(moal_handle *handle) |
| { |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| #ifdef SD9098 |
| struct sdio_mmc_card *card = (struct sdio_mmc_card *)handle->card; |
| #endif |
| t_u32 revision_id = 0; |
| t_u32 rev_id_reg = handle->card_info->rev_id_reg; |
| |
| #if defined(SD8987) || defined(SD8997) || defined(SD9098) || defined(SD9097) || defined(SD8978) || defined(SD9177) |
| t_u32 magic_reg = handle->card_info->magic_reg; |
| t_u32 magic = 0; |
| t_u32 host_strap_reg = handle->card_info->host_strap_reg; |
| t_u32 strap = 0; |
| #endif |
| |
| ENTER(); |
| |
| if (handle->params.fw_name) |
| goto done; |
| #ifdef SD8801 |
| if (IS_SD8801(handle->card_type)) |
| goto done; |
| #endif |
| /** Revision ID register */ |
| woal_sdiommc_read_reg(handle, rev_id_reg, &revision_id); |
| PRINTM(MCMND, "revision_id=0x%x\n", revision_id); |
| |
| #if defined(SD8987) || defined(SD8997) || defined(SD9098) || defined(SD9097) || defined(SD8978) || defined(SD9177) |
| /** Revision ID register */ |
| woal_sdiommc_read_reg(handle, magic_reg, &magic); |
| /** Revision ID register */ |
| woal_sdiommc_read_reg(handle, host_strap_reg, &strap); |
| strap &= 0x1; |
| magic &= 0xFF; |
| /* 1 = SDSD, 0 --SD UART */ |
| PRINTM(MCMND, "magic=0x%x strap=0x%x\n", magic, strap); |
| #endif |
| #if defined(SD8977) |
| if (IS_SD8977(handle->card_type)) { |
| switch (revision_id) { |
| case SD8977_V0: |
| strcpy(handle->card_info->fw_name, SD8977_V0_FW_NAME); |
| strcpy(handle->card_info->fw_name_wlan, |
| SD8977_WLAN_V0_FW_NAME); |
| break; |
| case SD8977_V1: |
| strcpy(handle->card_info->fw_name, SD8977_V1_FW_NAME); |
| strcpy(handle->card_info->fw_name_wlan, |
| SD8977_WLAN_V1_FW_NAME); |
| break; |
| case SD8977_V2: |
| strcpy(handle->card_info->fw_name, SD8977_V2_FW_NAME); |
| strcpy(handle->card_info->fw_name_wlan, |
| SD8977_WLAN_V2_FW_NAME); |
| break; |
| default: |
| break; |
| } |
| } |
| #endif |
| #if defined(SD8887) |
| if (IS_SD8887(handle->card_type)) { |
| /* Check revision ID */ |
| switch (revision_id) { |
| case SD8887_A0: |
| strcpy(handle->card_info->fw_name, SD8887_A0_FW_NAME); |
| strcpy(handle->card_info->fw_name_wlan, |
| SD8887_WLAN_A0_FW_NAME); |
| break; |
| case SD8887_A2: |
| strcpy(handle->card_info->fw_name, SD8887_A2_FW_NAME); |
| strcpy(handle->card_info->fw_name_wlan, |
| SD8887_WLAN_A2_FW_NAME); |
| break; |
| default: |
| break; |
| } |
| } |
| #endif |
| |
| #ifdef SD8997 |
| if (IS_SD8997(handle->card_type)) { |
| if (magic == CHIP_MAGIC_VALUE) { |
| if (strap == CARD_TYPE_SD_UART) |
| strcpy(handle->card_info->fw_name, |
| SDUART8997_DEFAULT_COMBO_FW_NAME); |
| else |
| strcpy(handle->card_info->fw_name, |
| SDSD8997_DEFAULT_COMBO_FW_NAME); |
| } |
| } |
| #endif |
| |
| #ifdef SD8987 |
| if (IS_SD8987(handle->card_type)) { |
| if (magic == CHIP_MAGIC_VALUE) { |
| if (strap == CARD_TYPE_SD_UART) |
| strcpy(handle->card_info->fw_name, |
| SDUART8987_DEFAULT_COMBO_FW_NAME); |
| else |
| strcpy(handle->card_info->fw_name, |
| SDSD8987_DEFAULT_COMBO_FW_NAME); |
| } |
| } |
| #endif |
| |
| #ifdef SD8978 |
| if (IS_SD8978(handle->card_type)) { |
| if (magic == CHIP_MAGIC_VALUE) { |
| if (strap == CARD_TYPE_SD_UART) |
| strcpy(handle->card_info->fw_name, |
| SDUART8978_DEFAULT_COMBO_FW_NAME); |
| else |
| strcpy(handle->card_info->fw_name, |
| SDSD8978_DEFAULT_COMBO_FW_NAME); |
| } |
| } |
| #endif |
| |
| #ifdef SD9098 |
| if (IS_SD9098(handle->card_type) && |
| (card->func->device == SD_DEVICE_ID_9098_FN1)) { |
| switch (revision_id) { |
| case SD9098_Z1Z2: |
| if (magic == CHIP_MAGIC_VALUE) { |
| if (strap == CARD_TYPE_SD_UART) |
| strcpy(handle->card_info->fw_name, |
| SDUART9098_DEFAULT_COMBO_FW_NAME); |
| else |
| strcpy(handle->card_info->fw_name, |
| SDSD9098_DEFAULT_COMBO_FW_NAME); |
| } |
| strcpy(handle->card_info->fw_name_wlan, |
| SD9098_DEFAULT_WLAN_FW_NAME); |
| break; |
| case SD9098_A0: |
| case SD9098_A1: |
| case SD9098_A2: |
| if (magic == CHIP_MAGIC_VALUE) { |
| if (strap == CARD_TYPE_SD_UART) |
| strcpy(handle->card_info->fw_name, |
| SDUART9098_COMBO_V1_FW_NAME); |
| else |
| strcpy(handle->card_info->fw_name, |
| SDSD9098_COMBO_V1_FW_NAME); |
| } |
| strcpy(handle->card_info->fw_name_wlan, |
| SD9098_WLAN_V1_FW_NAME); |
| break; |
| default: |
| break; |
| } |
| } |
| #endif |
| #ifdef SD9097 |
| if (IS_SD9097(handle->card_type)) { |
| switch (revision_id) { |
| case SD9097_B0: |
| case SD9097_B1: |
| if (magic == CHIP_MAGIC_VALUE) { |
| if (strap == CARD_TYPE_SD_UART) |
| strcpy(handle->card_info->fw_name, |
| SDUART9097_COMBO_V1_FW_NAME); |
| else |
| strcpy(handle->card_info->fw_name, |
| SDSD9097_COMBO_V1_FW_NAME); |
| } |
| strcpy(handle->card_info->fw_name_wlan, |
| SD9097_WLAN_V1_FW_NAME); |
| break; |
| default: |
| break; |
| } |
| } |
| #endif |
| #ifdef SD9177 |
| if (IS_SD9177(handle->card_type)) { |
| switch (revision_id) { |
| case SD9177_A0: |
| if (magic == CHIP_MAGIC_VALUE) { |
| if (strap == CARD_TYPE_SD_UART) |
| strcpy(handle->card_info->fw_name, |
| SDUART9177_DEFAULT_COMBO_FW_NAME); |
| else |
| strcpy(handle->card_info->fw_name, |
| SDSD9177_DEFAULT_COMBO_FW_NAME); |
| } |
| strcpy(handle->card_info->fw_name_wlan, |
| SD9177_DEFAULT_WLAN_FW_NAME); |
| break; |
| default: |
| break; |
| } |
| } |
| #endif |
| done: |
| PRINTM(MCMND, "combo fw:%s wlan fw:%s \n", handle->card_info->fw_name, |
| handle->card_info->fw_name_wlan); |
| LEAVE(); |
| return ret; |
| } |
| |
| #define DEBUG_FW_DONE 0xFF |
| #define DEBUG_MEMDUMP_FINISH 0xFE |
| #define MAX_POLL_TRIES 100 |
| |
| typedef enum { |
| DUMP_TYPE_ITCM = 0, |
| DUMP_TYPE_DTCM = 1, |
| DUMP_TYPE_SQRAM = 2, |
| DUMP_TYPE_APU_REGS = 3, |
| DUMP_TYPE_CIU_REGS = 4, |
| DUMP_TYPE_ICU_REGS = 5, |
| DUMP_TYPE_MAC_REGS = 6, |
| DUMP_TYPE_EXTEND_7 = 7, |
| DUMP_TYPE_EXTEND_8 = 8, |
| DUMP_TYPE_EXTEND_9 = 9, |
| DUMP_TYPE_EXTEND_10 = 10, |
| DUMP_TYPE_EXTEND_11 = 11, |
| DUMP_TYPE_EXTEND_12 = 12, |
| DUMP_TYPE_EXTEND_13 = 13, |
| DUMP_TYPE_EXTEND_LAST = 14 |
| } dumped_mem_type; |
| |
| #define MAX_NAME_LEN 8 |
| |
| typedef struct { |
| t_u8 mem_name[MAX_NAME_LEN]; |
| t_u8 *mem_Ptr; |
| struct file *pfile_mem; |
| t_u8 done_flag; |
| t_u8 type; |
| } memory_type_mapping; |
| |
| static memory_type_mapping mem_type_mapping_tbl[] = { |
| {"ITCM", NULL, NULL, 0xF0, FW_DUMP_TYPE_MEM_ITCM}, |
| {"DTCM", NULL, NULL, 0xF1, FW_DUMP_TYPE_MEM_DTCM}, |
| {"SQRAM", NULL, NULL, 0xF2, FW_DUMP_TYPE_MEM_SQRAM}, |
| {"APU", NULL, NULL, 0xF3, FW_DUMP_TYPE_REG_APU}, |
| {"CIU", NULL, NULL, 0xF4, FW_DUMP_TYPE_REG_CIU}, |
| {"ICU", NULL, NULL, 0xF5, FW_DUMP_TYPE_REG_ICU}, |
| {"MAC", NULL, NULL, 0xF6, FW_DUMP_TYPE_REG_MAC}, |
| {"EXT7", NULL, NULL, 0xF7, 0}, |
| {"EXT8", NULL, NULL, 0xF8, 0}, |
| {"EXT9", NULL, NULL, 0xF9, 0}, |
| {"EXT10", NULL, NULL, 0xFA, 0}, |
| {"EXT11", NULL, NULL, 0xFB, 0}, |
| {"EXT12", NULL, NULL, 0xFC, 0}, |
| {"EXT13", NULL, NULL, 0xFD, 0}, |
| {"EXTLAST", NULL, NULL, 0xFE, 0}, |
| }; |
| |
| static memory_type_mapping mem_type_mapping_tbl_8977_8997 = { "DUMP", NULL, |
| NULL, 0xDD, 0 |
| }; |
| |
| /** |
| * @brief This function read/write firmware via cmd52 |
| * |
| * @param phandle A pointer to moal_handle |
| * @param doneflag A flag |
| * |
| * @return MLAN_STATUS_SUCCESS |
| */ |
| static rdwr_status |
| woal_cmd52_rdwr_firmware(moal_handle *phandle, t_u8 doneflag) |
| { |
| int ret = 0; |
| int tries = 0; |
| t_u8 ctrl_data = 0; |
| t_u8 dbg_dump_ctrl_reg = phandle->card_info->dump_fw_ctrl_reg; |
| t_u8 debug_host_ready = phandle->card_info->dump_fw_host_ready; |
| |
| #ifdef SD9177 |
| if (IS_SD9177(phandle->card_type)) { |
| if (phandle->event_fw_dump) |
| debug_host_ready = 0xAA; |
| } |
| #endif |
| ret = woal_sdio_writeb(phandle, dbg_dump_ctrl_reg, debug_host_ready); |
| if (ret) { |
| PRINTM(MERROR, "SDIO Write ERR\n"); |
| return RDWR_STATUS_FAILURE; |
| } |
| #ifdef SD9177 |
| if (IS_SD9177(phandle->card_type)) { |
| if (phandle->event_fw_dump) |
| return RDWR_STATUS_SUCCESS; |
| } |
| #endif |
| for (tries = 0; tries < MAX_POLL_TRIES; tries++) { |
| ret = woal_sdio_readb(phandle, dbg_dump_ctrl_reg, &ctrl_data); |
| if (ret) { |
| PRINTM(MERROR, "SDIO READ ERR\n"); |
| return RDWR_STATUS_FAILURE; |
| } |
| if (ctrl_data == DEBUG_FW_DONE) |
| break; |
| if (doneflag && ctrl_data == doneflag) |
| return RDWR_STATUS_DONE; |
| if (ctrl_data != debug_host_ready) { |
| PRINTM(MMSG, |
| "The ctrl reg was changed, re-try again!\n"); |
| ret = woal_sdio_writeb(phandle, dbg_dump_ctrl_reg, |
| debug_host_ready); |
| if (ret) { |
| PRINTM(MERROR, "SDIO Write ERR\n"); |
| return RDWR_STATUS_FAILURE; |
| } |
| } |
| udelay(100); |
| } |
| if (ctrl_data == debug_host_ready || tries == MAX_POLL_TRIES) { |
| PRINTM(MERROR, "Fail to pull ctrl_data\n"); |
| return RDWR_STATUS_FAILURE; |
| } |
| return RDWR_STATUS_SUCCESS; |
| } |
| |
| #ifdef SD8801 |
| #define DEBUG_HOST_READY 0xEE |
| #define DEBUG_FW_DONE 0xFF |
| #define DEBUG_MEMDUMP_FINISH 0xFE |
| #define MAX_POLL_TRIES 100 |
| #define DEBUG_ITCM_DONE 0xaa |
| #define DEBUG_DTCM_DONE 0xbb |
| #define DEBUG_SQRAM_DONE 0xcc |
| |
| #define DEBUG_DUMP_CTRL_REG 0x63 |
| #define DEBUG_DUMP_FIRST_REG 0x62 |
| #define DEBUG_DUMP_START_REG 0x64 |
| #define DEBUG_DUMP_END_REG 0x6a |
| #define ITCM_SIZE 0x60000 |
| #define SQRAM_SIZE 0x33500 |
| #define DTCM_SIZE 0x14000 |
| |
| /** |
| * @brief This function dump firmware memory to file |
| * |
| * @param phandle A pointer to moal_handle |
| * |
| * @return N/A |
| */ |
| void |
| woal_dump_firmware_info(moal_handle *phandle) |
| { |
| |
| int ret = 0; |
| unsigned int reg, reg_start, reg_end; |
| t_u8 *ITCM_Ptr = NULL; |
| t_u8 *DTCM_Ptr = NULL; |
| t_u8 *SQRAM_Ptr = NULL; |
| t_u8 *dbg_ptr = NULL; |
| t_u32 sec, usec; |
| t_u8 ctrl_data = 0; |
| t_u32 dtcm_size = DTCM_SIZE; |
| t_u32 sqram_size = SQRAM_SIZE; |
| t_u8 *end_ptr = NULL; |
| int tries; |
| |
| if (!phandle) { |
| PRINTM(MERROR, "Could not dump firmwware info\n"); |
| return; |
| } |
| if (!phandle->fw_dump_buf) { |
| ret = moal_vmalloc(phandle, FW_DUMP_INFO_LEN, |
| &(phandle->fw_dump_buf)); |
| if (ret != MLAN_STATUS_SUCCESS || !phandle->fw_dump_buf) { |
| PRINTM(MERROR, "Failed to vmalloc fw dump bufffer\n"); |
| return; |
| } |
| } else { |
| memset(phandle->fw_dump_buf, 0x00, FW_DUMP_INFO_LEN); |
| } |
| phandle->fw_dump_len = 0; |
| |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) |
| sdio_claim_host(((struct sdio_mmc_card *)phandle->card)->func); |
| #endif |
| /* start dump fw memory */ |
| moal_get_system_time(phandle, &sec, &usec); |
| PRINTM(MMSG, "==== DEBUG MODE OUTPUT START: %u.%06u ====\n", sec, usec); |
| ret = moal_vmalloc(phandle, ITCM_SIZE + 1, (t_u8 **)&ITCM_Ptr); |
| if ((ret != MLAN_STATUS_SUCCESS) || !ITCM_Ptr) { |
| PRINTM(MERROR, "Error: vmalloc ITCM buffer failed!!!\n"); |
| goto done; |
| } |
| |
| PRINTM(MMSG, "DTCM_SIZE=0x%x\n", dtcm_size); |
| ret = moal_vmalloc(phandle, dtcm_size + 1, (t_u8 **)&DTCM_Ptr); |
| if ((ret != MLAN_STATUS_SUCCESS) || !DTCM_Ptr) { |
| PRINTM(MERROR, "Error: vmalloc DTCM buffer failed!!!\n"); |
| goto done; |
| } |
| ret = moal_vmalloc(phandle, sqram_size + 1, (t_u8 **)&SQRAM_Ptr); |
| if ((ret != MLAN_STATUS_SUCCESS) || !SQRAM_Ptr) { |
| PRINTM(MERROR, "Error: vmalloc SQRAM buffer failed!!!\n"); |
| goto done; |
| } |
| dbg_ptr = ITCM_Ptr; |
| end_ptr = ITCM_Ptr + ITCM_SIZE; |
| moal_get_system_time(phandle, &sec, &usec); |
| PRINTM(MMSG, "Start ITCM output %u.%06u, please wait...\n", sec, usec); |
| reg_start = DEBUG_DUMP_START_REG; |
| reg_end = DEBUG_DUMP_END_REG; |
| do { |
| ret = woal_sdio_writeb(phandle, DEBUG_DUMP_CTRL_REG, |
| DEBUG_HOST_READY); |
| if (ret) { |
| PRINTM(MERROR, "SDIO Write ERR\n"); |
| goto done; |
| } |
| for (tries = 0; tries < MAX_POLL_TRIES; tries++) { |
| ret = woal_sdio_readb(phandle, DEBUG_DUMP_CTRL_REG, |
| &ctrl_data); |
| if (ret) { |
| PRINTM(MERROR, "SDIO READ ERR\n"); |
| goto done; |
| } |
| if ((ctrl_data == DEBUG_FW_DONE) || |
| (ctrl_data == DEBUG_ITCM_DONE) |
| || (ctrl_data == DEBUG_DTCM_DONE) || |
| (ctrl_data == DEBUG_SQRAM_DONE)) |
| break; |
| if (ctrl_data != DEBUG_HOST_READY) { |
| ret = woal_sdio_writeb(phandle, |
| DEBUG_DUMP_CTRL_REG, |
| DEBUG_HOST_READY); |
| if (ret) { |
| PRINTM(MERROR, "SDIO Write ERR\n"); |
| goto done; |
| } |
| } |
| udelay(100); |
| } |
| if (ctrl_data == DEBUG_HOST_READY) { |
| PRINTM(MERROR, "Fail to pull ctrl_data\n"); |
| goto done; |
| } |
| reg = DEBUG_DUMP_FIRST_REG; |
| ret = woal_sdio_readb(phandle, reg, dbg_ptr); |
| if (ret) { |
| PRINTM(MMSG, "SDIO READ ERR\n"); |
| goto done; |
| } |
| if (dbg_ptr < end_ptr) |
| dbg_ptr++; |
| else { |
| PRINTM(MERROR, "pre-allocced buf is not enough\n"); |
| goto done; |
| } |
| for (reg = reg_start; reg <= reg_end; reg++) { |
| ret = woal_sdio_readb(phandle, reg, dbg_ptr); |
| if (ret) { |
| PRINTM(MMSG, "SDIO READ ERR\n"); |
| goto done; |
| } |
| if (dbg_ptr < end_ptr) |
| dbg_ptr++; |
| else |
| PRINTM(MMSG, |
| "pre-allocced buf is not enough\n"); |
| } |
| switch (ctrl_data) { |
| case DEBUG_ITCM_DONE: |
| #ifdef MLAN_64BIT |
| PRINTM(MMSG, "ITCM done: size=0x%lx\n", |
| dbg_ptr - ITCM_Ptr); |
| #else |
| PRINTM(MMSG, "ITCM done: size=0x%x\n", |
| dbg_ptr - ITCM_Ptr); |
| #endif |
| woal_save_dump_info_to_buf(phandle, ITCM_Ptr, ITCM_SIZE, |
| FW_DUMP_TYPE_MEM_ITCM); |
| dbg_ptr = DTCM_Ptr; |
| end_ptr = DTCM_Ptr + dtcm_size; |
| moal_get_system_time(phandle, &sec, &usec); |
| PRINTM(MMSG, |
| "Start DTCM output %u.%06u, please wait...\n", |
| sec, usec); |
| break; |
| case DEBUG_DTCM_DONE: |
| #ifdef MLAN_64BIT |
| PRINTM(MMSG, "DTCM done: size=0x%lx\n", |
| dbg_ptr - DTCM_Ptr); |
| #else |
| PRINTM(MMSG, "DTCM done: size=0x%x\n", |
| dbg_ptr - DTCM_Ptr); |
| #endif |
| woal_save_dump_info_to_buf(phandle, ITCM_Ptr, dtcm_size, |
| FW_DUMP_TYPE_MEM_DTCM); |
| dbg_ptr = SQRAM_Ptr; |
| end_ptr = SQRAM_Ptr + sqram_size; |
| moal_get_system_time(phandle, &sec, &usec); |
| PRINTM(MMSG, |
| "Start SQRAM output %u.%06u, please wait...\n", |
| sec, usec); |
| break; |
| case DEBUG_SQRAM_DONE: |
| #ifdef MLAN_64BIT |
| PRINTM(MMSG, "SQRAM done: size=0x%lx\n", |
| dbg_ptr - SQRAM_Ptr); |
| #else |
| PRINTM(MMSG, "SQRAM done: size=0x%x\n", |
| dbg_ptr - SQRAM_Ptr); |
| #endif |
| woal_save_dump_info_to_buf(phandle, SQRAM_Ptr, |
| sqram_size, |
| FW_DUMP_TYPE_MEM_SQRAM); |
| PRINTM(MMSG, "End output!\n"); |
| break; |
| default: |
| break; |
| } |
| } while (ctrl_data != DEBUG_SQRAM_DONE); |
| |
| woal_append_end_block(phandle); |
| PRINTM(MMSG, |
| "The output ITCM/DTCM/SQRAM have been saved to files successfully!\n"); |
| moal_get_system_time(phandle, &sec, &usec); |
| PRINTM(MMSG, "==== DEBUG MODE OUTPUT END: %u.%06u ====\n", sec, usec); |
| /* end dump fw memory */ |
| done: |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) |
| sdio_release_host(((struct sdio_mmc_card *)phandle->card)->func); |
| #endif |
| if (ITCM_Ptr) |
| moal_vfree(phandle, ITCM_Ptr); |
| if (DTCM_Ptr) |
| moal_vfree(phandle, DTCM_Ptr); |
| if (SQRAM_Ptr) |
| moal_vfree(phandle, SQRAM_Ptr); |
| PRINTM(MMSG, "==== DEBUG MODE END ====\n"); |
| return; |
| } |
| #endif |
| |
| /** |
| * @brief This function dump firmware memory to file |
| * |
| * @param phandle A pointer to moal_handle |
| * |
| * @return N/A |
| */ |
| void |
| woal_dump_firmware_info_v2(moal_handle *phandle) |
| { |
| int ret = 0; |
| unsigned int reg, reg_start, reg_end; |
| t_u8 *dbg_ptr = NULL; |
| t_u32 sec, usec; |
| t_u8 dump_num = 0; |
| t_u8 idx = 0; |
| t_u8 doneflag = 0; |
| rdwr_status stat; |
| t_u8 i = 0; |
| t_u8 read_reg = 0; |
| t_u32 memory_size = 0; |
| t_u8 *end_ptr = NULL; |
| t_u8 dbg_dump_start_reg = 0; |
| t_u8 dbg_dump_end_reg = 0; |
| t_u8 dbg_dump_ctrl_reg = 0; |
| |
| if (!phandle) { |
| PRINTM(MERROR, "Could not dump firmwware info\n"); |
| return; |
| } |
| |
| dbg_dump_start_reg = phandle->card_info->dump_fw_start_reg; |
| dbg_dump_end_reg = phandle->card_info->dump_fw_end_reg; |
| dbg_dump_ctrl_reg = phandle->card_info->dump_fw_ctrl_reg; |
| |
| if (!phandle->fw_dump_buf) { |
| ret = moal_vmalloc(phandle, FW_DUMP_INFO_LEN, |
| &(phandle->fw_dump_buf)); |
| if (ret != MLAN_STATUS_SUCCESS || !phandle->fw_dump_buf) { |
| PRINTM(MERROR, "Failed to vmalloc fw dump bufffer\n"); |
| return; |
| } |
| } else { |
| memset(phandle->fw_dump_buf, 0x00, FW_DUMP_INFO_LEN); |
| } |
| phandle->fw_dump_len = 0; |
| |
| /* start dump fw memory */ |
| moal_get_system_time(phandle, &sec, &usec); |
| PRINTM(MMSG, "==== DEBUG MODE OUTPUT START: %u.%06u ====\n", sec, usec); |
| /* read the number of the memories which will dump */ |
| if (RDWR_STATUS_FAILURE == woal_cmd52_rdwr_firmware(phandle, doneflag)) |
| goto done; |
| reg = dbg_dump_start_reg; |
| ret = woal_sdio_readb(phandle, reg, &dump_num); |
| if (ret) { |
| PRINTM(MMSG, "SDIO READ MEM NUM ERR\n"); |
| goto done; |
| } |
| |
| /* read the length of every memory which will dump */ |
| for (idx = 0; idx < dump_num; idx++) { |
| if (RDWR_STATUS_FAILURE == |
| woal_cmd52_rdwr_firmware(phandle, doneflag)) |
| goto done; |
| memory_size = 0; |
| reg = dbg_dump_start_reg; |
| for (i = 0; i < 4; i++) { |
| ret = woal_sdio_readb(phandle, reg, &read_reg); |
| if (ret) { |
| PRINTM(MMSG, "SDIO READ ERR\n"); |
| goto done; |
| } |
| memory_size |= (read_reg << i * 8); |
| reg++; |
| } |
| if (memory_size == 0) { |
| PRINTM(MMSG, "Firmware Dump Finished!\n"); |
| ret = woal_sdiommc_write_reg(phandle, dbg_dump_ctrl_reg, |
| DEBUG_MEMDUMP_FINISH); |
| if (ret) { |
| PRINTM(MERROR, |
| "SDIO Write MEMDUMP_FINISH ERR\n"); |
| goto done; |
| } |
| break; |
| } else { |
| PRINTM(MMSG, "%s_SIZE=0x%x\n", |
| mem_type_mapping_tbl[idx].mem_name, memory_size); |
| ret = moal_vmalloc(phandle, memory_size + 1, |
| (t_u8 **)&mem_type_mapping_tbl[idx]. |
| mem_Ptr); |
| if ((ret != MLAN_STATUS_SUCCESS) || |
| !mem_type_mapping_tbl[idx].mem_Ptr) { |
| PRINTM(MERROR, |
| "Error: vmalloc %s buffer failed!!!\n", |
| mem_type_mapping_tbl[idx].mem_name); |
| goto done; |
| } |
| dbg_ptr = mem_type_mapping_tbl[idx].mem_Ptr; |
| end_ptr = dbg_ptr + memory_size; |
| } |
| doneflag = mem_type_mapping_tbl[idx].done_flag; |
| moal_get_system_time(phandle, &sec, &usec); |
| PRINTM(MMSG, "Start %s output %u.%06u, please wait...\n", |
| mem_type_mapping_tbl[idx].mem_name, sec, usec); |
| do { |
| stat = woal_cmd52_rdwr_firmware(phandle, doneflag); |
| if (RDWR_STATUS_FAILURE == stat) |
| goto done; |
| reg_start = dbg_dump_start_reg; |
| reg_end = dbg_dump_end_reg; |
| for (reg = reg_start; reg <= reg_end; reg++) { |
| ret = woal_sdio_readb(phandle, reg, dbg_ptr); |
| if (ret) { |
| PRINTM(MMSG, "SDIO READ ERR\n"); |
| goto done; |
| } |
| if (dbg_ptr < end_ptr) |
| dbg_ptr++; |
| else |
| PRINTM(MMSG, |
| "pre-allocced buf is not enough\n"); |
| } |
| if (RDWR_STATUS_DONE == stat) { |
| #ifdef MLAN_64BIT |
| PRINTM(MMSG, |
| "%s done:" |
| "size = 0x%lx\n", |
| mem_type_mapping_tbl[idx].mem_name, |
| dbg_ptr - mem_type_mapping_tbl[idx] |
| .mem_Ptr); |
| #else |
| PRINTM(MMSG, |
| "%s done:" |
| "size = 0x%x\n", |
| mem_type_mapping_tbl[idx].mem_name, |
| dbg_ptr - mem_type_mapping_tbl[idx] |
| .mem_Ptr); |
| #endif |
| woal_save_dump_info_to_buf(phandle, |
| mem_type_mapping_tbl |
| [idx].mem_Ptr, |
| memory_size, |
| mem_type_mapping_tbl |
| [idx].type); |
| moal_vfree(phandle, |
| mem_type_mapping_tbl[idx].mem_Ptr); |
| mem_type_mapping_tbl[idx].mem_Ptr = NULL; |
| break; |
| } |
| } while (1); |
| } |
| woal_append_end_block(phandle); |
| moal_get_system_time(phandle, &sec, &usec); |
| PRINTM(MMSG, "==== DEBUG MODE OUTPUT END: %u.%06u ====\n", sec, usec); |
| /* end dump fw memory */ |
| done: |
| for (idx = 0; idx < dump_num; idx++) { |
| if (mem_type_mapping_tbl[idx].mem_Ptr) { |
| moal_vfree(phandle, mem_type_mapping_tbl[idx].mem_Ptr); |
| mem_type_mapping_tbl[idx].mem_Ptr = NULL; |
| } |
| } |
| PRINTM(MMSG, "==== DEBUG MODE END ====\n"); |
| return; |
| } |
| |
| /** |
| * @brief This function dump firmware memory to file |
| * |
| * @param phandle A pointer to moal_handle |
| * |
| * @return N/A |
| */ |
| void |
| woal_dump_firmware_info_v3(moal_handle *phandle) |
| { |
| int ret = 0; |
| int tries = 0; |
| unsigned int reg, reg_start, reg_end; |
| t_u8 *dbg_ptr = NULL; |
| t_u8 *temp_Ptr = NULL; |
| t_u32 sec, usec; |
| t_u8 start_flag = 0; |
| t_u8 doneflag = 0; |
| rdwr_status stat; |
| t_u32 memory_size = 0; |
| t_u8 *end_ptr = NULL; |
| t_u8 dbg_dump_start_reg = 0; |
| t_u8 dbg_dump_end_reg = 0; |
| memory_type_mapping *pmem_type_mapping_tbl = |
| &mem_type_mapping_tbl_8977_8997; |
| |
| if (!phandle) { |
| PRINTM(MERROR, "Could not dump firmwware info\n"); |
| return; |
| } |
| #ifdef SD9177 |
| if (IS_SD9177(phandle->card_type)) { |
| if (phandle->event_fw_dump) { |
| if (RDWR_STATUS_FAILURE != |
| woal_cmd52_rdwr_firmware(phandle, doneflag)) { |
| PRINTM(MMSG, |
| "====SDIO FW DUMP EVENT MODE START ====\n"); |
| return; |
| } |
| } |
| } |
| #endif |
| |
| dbg_dump_start_reg = phandle->card_info->dump_fw_start_reg; |
| dbg_dump_end_reg = phandle->card_info->dump_fw_end_reg; |
| |
| /* start dump fw memory */ |
| moal_get_system_time(phandle, &sec, &usec); |
| PRINTM(MMSG, "==== DEBUG MODE OUTPUT START: %u.%06u ====\n", sec, usec); |
| /* read the number of the memories which will dump */ |
| if (RDWR_STATUS_FAILURE == woal_cmd52_rdwr_firmware(phandle, doneflag)) |
| goto done; |
| |
| /** check the reg which indicate dump starting */ |
| for (reg = dbg_dump_start_reg; reg <= dbg_dump_end_reg; reg++) { |
| for (tries = 0; tries < MAX_POLL_TRIES; tries++) { |
| ret = woal_sdio_readb(phandle, reg, &start_flag); |
| if (ret) { |
| PRINTM(MMSG, "SDIO READ ERR\n"); |
| goto done; |
| } |
| /** 0 means dump starting*/ |
| if (start_flag == 0) |
| break; |
| udelay(100); |
| } |
| if (tries == MAX_POLL_TRIES) { |
| PRINTM(MMSG, "FW not ready to dump\n"); |
| goto done; |
| } |
| } |
| memory_size = 0xF0000; |
| PRINTM(MMSG, "%s_SIZE=0x%x\n", pmem_type_mapping_tbl->mem_name, |
| memory_size); |
| ret = moal_vmalloc(phandle, memory_size + 1, |
| (t_u8 **)&pmem_type_mapping_tbl->mem_Ptr); |
| if ((ret != MLAN_STATUS_SUCCESS) || !pmem_type_mapping_tbl->mem_Ptr) { |
| PRINTM(MERROR, "Error: vmalloc %s buffer failed!!!\n", |
| pmem_type_mapping_tbl->mem_name); |
| goto done; |
| } |
| dbg_ptr = pmem_type_mapping_tbl->mem_Ptr; |
| end_ptr = dbg_ptr + memory_size; |
| doneflag = pmem_type_mapping_tbl->done_flag; |
| moal_get_system_time(phandle, &sec, &usec); |
| PRINTM(MMSG, "Start %s output %u.%06u, please wait...\n", |
| pmem_type_mapping_tbl->mem_name, sec, usec); |
| do { |
| stat = woal_cmd52_rdwr_firmware(phandle, doneflag); |
| if (RDWR_STATUS_FAILURE == stat) |
| goto done; |
| reg_start = dbg_dump_start_reg; |
| reg_end = dbg_dump_end_reg; |
| for (reg = reg_start; reg <= reg_end; reg++) { |
| ret = woal_sdio_readb(phandle, reg, dbg_ptr); |
| if (ret) { |
| PRINTM(MMSG, "SDIO READ ERR\n"); |
| goto done; |
| } |
| dbg_ptr++; |
| if (dbg_ptr >= end_ptr) { |
| PRINTM(MMSG, |
| "pre-allocced buf is not enough\n"); |
| ret = moal_vmalloc(phandle, |
| memory_size + 0x4000 + 1, |
| (t_u8 **)&temp_Ptr); |
| if ((ret != MLAN_STATUS_SUCCESS) || !temp_Ptr) { |
| PRINTM(MERROR, |
| "Error: vmalloc buffer failed!!!\n"); |
| goto done; |
| } |
| moal_memcpy_ext(phandle, temp_Ptr, |
| pmem_type_mapping_tbl->mem_Ptr, |
| memory_size, |
| memory_size + 0x4000); |
| moal_vfree(phandle, |
| pmem_type_mapping_tbl->mem_Ptr); |
| pmem_type_mapping_tbl->mem_Ptr = temp_Ptr; |
| temp_Ptr = NULL; |
| dbg_ptr = pmem_type_mapping_tbl->mem_Ptr + |
| memory_size; |
| memory_size += 0x4000; |
| end_ptr = pmem_type_mapping_tbl->mem_Ptr + |
| memory_size; |
| } |
| } |
| if (RDWR_STATUS_DONE == stat) { |
| #ifdef MLAN_64BIT |
| PRINTM(MMSG, |
| "%s done:" |
| "size = 0x%lx\n", |
| pmem_type_mapping_tbl->mem_name, |
| dbg_ptr - pmem_type_mapping_tbl->mem_Ptr); |
| #else |
| PRINTM(MMSG, |
| "%s done:" |
| "size = 0x%x\n", |
| pmem_type_mapping_tbl->mem_name, |
| dbg_ptr - pmem_type_mapping_tbl->mem_Ptr); |
| |
| #endif |
| if (phandle->fw_dump_buf) { |
| moal_vfree(phandle, phandle->fw_dump_buf); |
| phandle->fw_dump_buf = NULL; |
| phandle->fw_dump_len = 0; |
| } |
| phandle->fw_dump_buf = pmem_type_mapping_tbl->mem_Ptr; |
| phandle->fw_dump_len = |
| dbg_ptr - pmem_type_mapping_tbl->mem_Ptr; |
| pmem_type_mapping_tbl->mem_Ptr = NULL; |
| break; |
| } |
| } while (1); |
| moal_get_system_time(phandle, &sec, &usec); |
| PRINTM(MMSG, "==== DEBUG MODE OUTPUT END: %u.%06u ====\n", sec, usec); |
| /* end dump fw memory */ |
| done: |
| if (pmem_type_mapping_tbl->mem_Ptr) { |
| moal_vfree(phandle, pmem_type_mapping_tbl->mem_Ptr); |
| pmem_type_mapping_tbl->mem_Ptr = NULL; |
| } |
| PRINTM(MMSG, "==== DEBUG MODE END ====\n"); |
| return; |
| } |
| |
| /** |
| * @brief This function reads and displays SDIO registers for debugging |
| * |
| * @param phandle A pointer to moal_handle |
| * |
| * @return N/A |
| */ |
| static void |
| woal_sdiommc_reg_dbg(moal_handle *phandle) |
| { |
| int ret = 0; |
| t_u8 loop, index = 0, func, data; |
| unsigned int reg, reg_start, reg_end; |
| unsigned int scratch_reg = phandle->card_info->scratch_reg; |
| t_u8 *reg_table = phandle->card_info->dump_reg.reg_table; |
| t_u8 reg_table_size = phandle->card_info->dump_reg.reg_table_size; |
| char buf[256], *ptr; |
| |
| mlan_pm_wakeup_card(phandle->pmlan_adapter, MTRUE); |
| for (loop = 0; loop < 5; loop++) { |
| memset(buf, 0, sizeof(buf)); |
| ptr = buf; |
| if (loop == 0) { |
| /* Read the registers of SDIO function0 */ |
| func = loop; |
| reg_start = 0; |
| reg_end = 9; |
| } else if (loop == 1) { |
| /* Read the registers of SDIO function1 */ |
| func = loop; |
| reg_start = phandle->card_info->func1_reg_start; |
| reg_end = phandle->card_info->func1_reg_end; |
| } else if (loop == 2) { |
| /* Read specific registers of SDIO function1 */ |
| index = 0; |
| func = 1; |
| reg_start = reg_table[index++]; |
| reg_end = reg_table[reg_table_size - 1]; |
| } else { |
| /* Read the scratch registers of SDIO function1 */ |
| if (loop == 4) |
| mdelay(100); |
| func = 1; |
| reg_start = scratch_reg; |
| reg_end = scratch_reg + 10; |
| } |
| if (loop != 2) |
| ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", func, |
| reg_start, reg_end); |
| else |
| ptr += sprintf(ptr, "SDIO Func%d: ", func); |
| for (reg = reg_start; reg <= reg_end;) { |
| if (func == 0) |
| ret = woal_sdio_f0_readb(phandle, reg, &data); |
| else |
| ret = woal_sdio_readb(phandle, reg, &data); |
| if (loop == 2) |
| ptr += sprintf(ptr, "(%#x) ", reg); |
| if (!ret) |
| ptr += sprintf(ptr, "%02x ", data); |
| else { |
| ptr += sprintf(ptr, "ERR"); |
| break; |
| } |
| if (loop == 2 && reg < reg_end) |
| reg = reg_table[index++]; |
| else |
| reg++; |
| } |
| PRINTM(MMSG, "%s\n", buf); |
| } |
| mlan_pm_wakeup_card(phandle->pmlan_adapter, MFALSE); |
| } |
| |
| /** |
| * @brief This function dump firmware memory to file |
| * |
| * @param phandle A pointer to moal_handle |
| * |
| * @return N/A |
| */ |
| static void |
| woal_sdiommc_dump_fw_info(moal_handle *phandle) |
| { |
| if (!phandle) { |
| PRINTM(MERROR, "Could not dump firmwware info\n"); |
| return; |
| } |
| mlan_pm_wakeup_card(phandle->pmlan_adapter, MTRUE); |
| phandle->fw_dump = MTRUE; |
| if (phandle->card_info->dump_fw_info == DUMP_FW_SDIO_V2) { |
| woal_dump_firmware_info_v2(phandle); |
| } else if (phandle->card_info->dump_fw_info == DUMP_FW_SDIO_V3) { |
| woal_dump_firmware_info_v3(phandle); |
| if (phandle->event_fw_dump) { |
| phandle->event_fw_dump = MFALSE; |
| queue_work(phandle->workqueue, &phandle->main_work); |
| phandle->is_fw_dump_timer_set = MTRUE; |
| woal_mod_timer(&phandle->fw_dump_timer, MOAL_TIMER_5S); |
| return; |
| } |
| } |
| #ifdef SD8801 |
| else { |
| woal_dump_firmware_info(phandle); |
| } |
| #endif |
| woal_send_fw_dump_complete_event(woal_get_priv |
| (phandle, MLAN_BSS_ROLE_ANY)); |
| phandle->fw_dump = MFALSE; |
| mlan_pm_wakeup_card(phandle->pmlan_adapter, MFALSE); |
| queue_work(phandle->workqueue, &phandle->main_work); |
| woal_process_hang(phandle); |
| return; |
| } |
| |
| /** |
| * @brief This function save sdio reg info |
| * |
| * @param phandle A pointer to moal_handle |
| * @param buf A pointer buffer saving log |
| * |
| * @return The length of this log |
| */ |
| static int |
| woal_sdiommc_dump_reg_info(moal_handle *phandle, t_u8 *drv_buf) |
| { |
| char *drv_ptr = (char *)drv_buf; |
| int ret = 0; |
| t_u8 loop, index = 0, func, data; |
| unsigned int reg, reg_start, reg_end; |
| unsigned int scratch_reg = 0; |
| t_u8 *reg_table = NULL; |
| t_u8 reg_table_size = 0; |
| char buf[256], *ptr; |
| |
| ENTER(); |
| |
| if (!phandle || !drv_buf) { |
| PRINTM(MMSG, "%s: can't retreive info\n", __func__); |
| LEAVE(); |
| return 0; |
| } |
| |
| scratch_reg = phandle->card_info->scratch_reg; |
| reg_table = phandle->card_info->dump_reg.reg_table; |
| reg_table_size = phandle->card_info->dump_reg.reg_table_size; |
| |
| mlan_pm_wakeup_card(phandle->pmlan_adapter, MTRUE); |
| |
| drv_ptr += sprintf(drv_ptr, "--------sdio_reg_debug_info---------\n"); |
| for (loop = 0; loop < 5; loop++) { |
| memset(buf, 0, sizeof(buf)); |
| ptr = buf; |
| if (loop == 0) { |
| /* Read the registers of SDIO function0 */ |
| func = loop; |
| reg_start = 0; |
| reg_end = 9; |
| |
| } else if (loop == 1) { |
| /* Read the registers of SDIO function1 */ |
| func = loop; |
| reg_start = phandle->card_info->func1_reg_start; |
| reg_end = phandle->card_info->func1_reg_end; |
| } else if (loop == 2) { |
| /* Read specific registers of SDIO function1 */ |
| index = 0; |
| func = 1; |
| reg_start = reg_table[index++]; |
| reg_end = reg_table[reg_table_size - 1]; |
| } else { |
| /* Read the scratch registers of SDIO function1 */ |
| if (loop == 4) |
| mdelay(100); |
| func = 1; |
| reg_start = scratch_reg; |
| reg_end = scratch_reg + 10; |
| } |
| if (loop != 2) |
| ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", func, |
| reg_start, reg_end); |
| else |
| ptr += sprintf(ptr, "SDIO Func%d: ", func); |
| for (reg = reg_start; reg <= reg_end;) { |
| if (func == 0) |
| ret = woal_sdio_f0_readb(phandle, reg, &data); |
| else |
| ret = woal_sdio_readb(phandle, reg, &data); |
| |
| if (loop == 2) |
| ptr += sprintf(ptr, "(%#x) ", reg); |
| if (!ret) |
| ptr += sprintf(ptr, "%02x ", data); |
| else { |
| ptr += sprintf(ptr, "ERR"); |
| break; |
| } |
| if (loop == 2 && reg < reg_end) |
| reg = reg_table[index++]; |
| else |
| reg++; |
| } |
| drv_ptr += sprintf(drv_ptr, "%s\n", buf); |
| } |
| |
| drv_ptr += |
| sprintf(drv_ptr, "--------sdio_reg_debug_info End---------\n"); |
| mlan_pm_wakeup_card(phandle->pmlan_adapter, MFALSE); |
| |
| LEAVE(); |
| return drv_ptr - (char *)drv_buf; |
| } |
| |
| /** |
| * @brief This function reset sdio through sdio bus driver |
| * |
| * @param phandle A pointer to moal_handle |
| * |
| * @return N/A |
| */ |
| void |
| woal_sdio_reset_hw(moal_handle *handle) |
| { |
| struct sdio_mmc_card *card = handle->card; |
| struct sdio_func *func = card->func; |
| ENTER(); |
| sdio_claim_host(func); |
| sdio_release_irq(card->func); |
| sdio_disable_func(card->func); |
| mmc_hw_reset(func->card->host); |
| #ifdef MMC_QUIRK_BLKSZ_FOR_BYTE_MODE |
| /* The byte mode patch is available in kernel MMC driver |
| * which fixes one issue in MP-A transfer. |
| * bit1: use func->cur_blksize for byte mode |
| */ |
| func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE; |
| #endif |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) |
| func->card->quirks |= MMC_QUIRK_LENIENT_FN0; |
| #endif |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) |
| /* wait for chip fully wake up */ |
| if (!func->enable_timeout) |
| func->enable_timeout = 200; |
| #endif |
| sdio_enable_func(func); |
| sdio_claim_irq(func, woal_sdio_interrupt); |
| sdio_set_block_size(card->func, MLAN_SDIO_BLOCK_SIZE); |
| sdio_release_host(func); |
| LEAVE(); |
| return; |
| } |
| |
| static moal_if_ops sdiommc_ops = { |
| .register_dev = woal_sdiommc_register_dev, |
| .unregister_dev = woal_sdiommc_unregister_dev, |
| .read_reg = woal_sdiommc_read_reg, |
| .write_reg = woal_sdiommc_write_reg, |
| .read_data_sync = woal_sdiommc_read_data_sync, |
| .write_data_sync = woal_sdiommc_write_data_sync, |
| .get_fw_name = woal_sdiommc_get_fw_name, |
| .dump_fw_info = woal_sdiommc_dump_fw_info, |
| .dump_reg_info = woal_sdiommc_dump_reg_info, |
| .reg_dbg = woal_sdiommc_reg_dbg, |
| .is_second_mac = woal_sdiommc_is_second_mac, |
| }; |