/** @file moal_sdio_mmc.c * * @brief This file contains SDIO MMC IF (interface) module * related functions. * * Copyright (C) 2008-2017, Marvell International Ltd. * * This software file (the "File") is distributed by Marvell International * Ltd. under the terms of the GNU General Public License Version 2, June 1991 * (the "License"). You may use, redistribute and/or modify this 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 #include "moal_sdio.h" /** define marvell vendor id */ #define MARVELL_VENDOR_ID 0x02df /* The macros below are hardware platform dependent. The definition should match the actual platform */ /** Initialize GPIO port */ #define GPIO_PORT_INIT() /** Set GPIO port to high */ #define GPIO_PORT_TO_HIGH() /** Set GPIO port to low */ #define GPIO_PORT_TO_LOW() /******************************************************** Local Variables ********************************************************/ /******************************************************** Global Variables ********************************************************/ #ifdef SDIO_SUSPEND_RESUME /** PM keep power */ extern int pm_keep_power; extern int shutdown_hs; #endif extern int disconnect_on_suspend; /** Device ID for SD8977 */ #define SD_DEVICE_ID_8977 (0x9145) /** WLAN IDs */ static const struct sdio_device_id wlan_ids[] = { {SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8977)}, {}, }; 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_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 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 }; /******************************************************** Local Functions ********************************************************/ /** @brief This function dump the sdio register * * @param handle A Pointer to the moal_handle structure * @return N/A */ 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; int fun1_reg[] = { 0x03, 0x04, 0x05, 0x60, 0x61 }; 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); } array_size = ARRAY_SIZE(fun1_reg); 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; } /******************************************************** 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; PRINTM(MINFO, "*** IN SDIO IRQ ***\n"); woal_interrupt(handle); LEAVE(); } /** @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; 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_disable_func(func); sdio_release_host(func); kfree(card); PRINTM(MFATAL, "sdio_enable_func() failed: ret=%d\n", ret); LEAVE(); return -EIO; } sdio_release_host(func); if (NULL == woal_add_card(card)) { PRINTM(MERROR, "woal_add_card failed\n"); kfree(card); sdio_claim_host(func); sdio_disable_func(func); sdio_release_host(func); ret = MLAN_STATUS_FAILURE; } 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) { 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 #define SHUTDOWN_HOST_SLEEP_DEF_GAP 100 #define SHUTDOWN_HOST_SLEEP_DEF_GPIO 0x3 #define SHUTDOWN_HOST_SLEEP_DEF_COND 0x0 /** @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 timeout = 0; 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 (shutdown_hs) { 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)); timeout = 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); if (!(pm_flags & MMC_PM_KEEP_POWER)) { PRINTM(MERROR, "%s: cannot remain alive while host is suspended\n", sdio_func_id(func)); LEAVE(); return -ENOSYS; } 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 == 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 (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) { #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; 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); 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 */ mlan_status woal_write_reg(moal_handle *handle, t_u32 reg, t_u32 data) { mlan_status ret = MLAN_STATUS_FAILURE; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); #endif sdio_writeb(((struct sdio_mmc_card *)handle->card)->func, (t_u8)data, reg, (int *)&ret); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); #endif 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 */ mlan_status woal_read_reg(moal_handle *handle, t_u32 reg, t_u32 *data) { mlan_status ret = MLAN_STATUS_FAILURE; t_u8 val; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); #endif val = sdio_readb(((struct sdio_mmc_card *)handle->card)->func, reg, (int *)&ret); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); #endif *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 */ 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]; 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) { 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; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); #endif 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 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); #endif return MLAN_STATUS_FAILURE; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); #endif 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 */ mlan_status woal_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 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); #endif 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 } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); #endif #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 */ mlan_status woal_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 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); #endif 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 } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); #endif #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_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; } /* init GPIO PORT for wakeup purpose */ GPIO_PORT_INIT(); /* set default value */ GPIO_PORT_TO_HIGH(); LEAVE(); return ret; } /** * @brief This function de-registers the IF module in bus driver * * @return N/A */ void woal_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 */ void woal_unregister_dev(moal_handle *handle) { ENTER(); if (handle->card) { /* Release the SDIO IRQ */ sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); sdio_release_irq(((struct sdio_mmc_card *)handle->card)->func); sdio_disable_func(((struct sdio_mmc_card *)handle->card)->func); sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); sdio_set_drvdata(((struct sdio_mmc_card *)handle->card)->func, NULL); GPIO_PORT_TO_LOW(); PRINTM(MWARN, "Making the sdio dev card as NULL\n"); } LEAVE(); } /** * @brief This function registers the device * * @param handle A pointer to moal_handle structure * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE */ mlan_status woal_register_dev(moal_handle *handle) { int ret = MLAN_STATUS_SUCCESS; struct sdio_mmc_card *card = handle->card; struct sdio_func *func; ENTER(); GPIO_PORT_INIT(); GPIO_PORT_TO_HIGH(); 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); handle->hotplug_device = &func->dev; 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; }