/** @file moal_sdio.c * * @brief This file contains SDIO MMC IF (interface) module * related functions. * * * Copyright 2008-2022 NXP * * NXP CONFIDENTIAL * The source code contained or described herein and all documents related to * the source code (Materials) are owned by NXP, its * suppliers and/or its licensors. Title to the Materials remains with NXP, * its suppliers and/or its licensors. The Materials contain * trade secrets and proprietary and confidential information of NXP, its * suppliers and/or its licensors. The Materials are protected by worldwide * copyright and trade secret laws and treaty provisions. No part of the * Materials may be used, copied, reproduced, modified, published, uploaded, * posted, transmitted, distributed, or disclosed in any way without NXP's prior * express written permission. * * No license under any patent, copyright, trade secret or other intellectual * property right is granted to or conferred upon you by disclosure or delivery * of the Materials, either expressly, by implication, inducement, estoppel or * otherwise. Any license under such intellectual property rights must be * express and approved by NXP in writing. * * Alternatively, this software may be distributed under the terms of GPL v2. * SPDX-License-Identifier: GPL-2.0 * */ /**************************************************** Change log: 02/25/09: Initial creation - This file supports SDIO MMC only ****************************************************/ #include #include "moal_sdio.h" /** define nxp vendor id */ #define NXP_VENDOR_ID 0x0471 #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 #ifdef SD9177 /** Device ID for SD9177 */ #define SD_DEVICE_ID_9177 (0x0205) #endif #ifdef SDNW62X /** Device ID for SDNW62X */ #define SD_DEVICE_ID_NW62X (0x020C) #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 #ifdef SDNW62X {SDIO_DEVICE(NXP_VENDOR_ID, SD_DEVICE_ID_NW62X)}, #endif {}, }; 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 SDNW62X if (cardp_sd->func->device == SD_DEVICE_ID_NW62X) { card_type = CARD_TYPE_SDNW62X; moal_memcpy_ext(NULL, driver_version, CARD_SDNW62X, strlen(CARD_SDNW62X), 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 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(SDNW62X) || 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(SDNW62X) || 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 SDNW62X if (IS_SDNW62X(handle->card_type)) { if (magic == CHIP_MAGIC_VALUE) { if (strap == CARD_TYPE_SD_UART) strcpy(handle->card_info->fw_name, SDUARTNW62X_COMBO_FW_NAME); else strcpy(handle->card_info->fw_name, SDSDNW62X_COMBO_FW_NAME); } } #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; case SD9177_A1: if (magic == CHIP_MAGIC_VALUE) { if (strap == CARD_TYPE_SD_UART) strcpy(handle->card_info->fw_name, SDUART9177_DEFAULT_COMBO_V1_FW_NAME); else strcpy(handle->card_info->fw_name, SDSD9177_DEFAULT_COMBO_V1_FW_NAME); } strcpy(handle->card_info->fw_name_wlan, SD9177_DEFAULT_WLAN_V1_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, };