/** @file moal_sdio_mmc.c | |
* | |
* @brief This file contains SDIO MMC IF (interface) module | |
* related functions. | |
* | |
* Copyright (C) 2008-2019, 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 <linux/firmware.h> | |
#include "moal_sdio.h" | |
/** define marvell vendor id */ | |
#define MARVELL_VENDOR_ID 0x02df | |
/******************************************************** | |
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 SD8777 */ | |
#define SD_DEVICE_ID_8777 (0x9131) | |
/** Device ID for SD8787 */ | |
#define SD_DEVICE_ID_8787 (0x9119) | |
/** Device ID for SD8887 */ | |
#define SD_DEVICE_ID_8887 (0x9135) | |
/** Device ID for SD8801 FN1 */ | |
#define SD_DEVICE_ID_8801 (0x9139) | |
/** Device ID for SD8897 */ | |
#define SD_DEVICE_ID_8897 (0x912d) | |
/** Device ID for SD8797 */ | |
#define SD_DEVICE_ID_8797 (0x9129) | |
/** Device ID for SD8977 */ | |
#define SD_DEVICE_ID_8977 (0x9145) | |
/** Device ID for SD8997 */ | |
#define SD_DEVICE_ID_8997 (0x9141) | |
/** Device ID for SD8987 */ | |
#define SD_DEVICE_ID_8987 (0x9149) | |
/** Device ID for SD8978 */ | |
#define SD_DEVICE_ID_8978 (0x9158) | |
/** WLAN IDs */ | |
static const struct sdio_device_id wlan_ids[] = { | |
{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8777)}, | |
{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8787)}, | |
{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8887)}, | |
{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8801)}, | |
{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8897)}, | |
{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8797)}, | |
{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8977)}, | |
{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8978)}, | |
{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8997)}, | |
{SDIO_DEVICE(MARVELL_VENDOR_ID, SD_DEVICE_ID_8987)}, | |
{}, | |
}; | |
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 | |
}; | |
#ifdef SDIO_OOB_IRQ | |
extern int mrvl_sdio_claim_irq(struct sdio_func *func, | |
sdio_irq_handler_t * handler); | |
extern int mrvl_sdio_release_irq(struct sdio_func *func); | |
extern int mrvl_sdio_suspend(struct sdio_func *func); | |
extern int mrvl_sdio_resume(struct sdio_func *func); | |
#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_8897[] = { 0x03, 0x04, 0x05, 0x06, 0x07, 0xC0, 0xC1 }; | |
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); | |
} | |
if (handle->card_type == CARD_TYPE_SD8897) { | |
fun1_reg = fun1_reg_8897; | |
array_size = sizeof(fun1_reg_8897) / sizeof(int); | |
} else { | |
fun1_reg = fun1_reg_other; | |
array_size = sizeof(fun1_reg_other) / sizeof(int); | |
} | |
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 updates the SDIO card types | |
* | |
* @param handle A Pointer to the moal_handle structure | |
* @param card A Pointer to card | |
* | |
* @return N/A | |
*/ | |
t_void | |
woal_sdio_update_card_type(moal_handle *handle, t_void *card) | |
{ | |
struct sdio_mmc_card *cardp = (struct sdio_mmc_card *)card; | |
/* Update card type */ | |
if (cardp->func->device == SD_DEVICE_ID_8777) | |
handle->card_type = CARD_TYPE_SD8777; | |
else if (cardp->func->device == SD_DEVICE_ID_8787) | |
handle->card_type = CARD_TYPE_SD8787; | |
else if (cardp->func->device == SD_DEVICE_ID_8887) | |
handle->card_type = CARD_TYPE_SD8887; | |
else if (cardp->func->device == SD_DEVICE_ID_8801) | |
handle->card_type = CARD_TYPE_SD8801; | |
else if (cardp->func->device == SD_DEVICE_ID_8897) | |
handle->card_type = CARD_TYPE_SD8897; | |
else if (cardp->func->device == SD_DEVICE_ID_8797) | |
handle->card_type = CARD_TYPE_SD8797; | |
else if (cardp->func->device == SD_DEVICE_ID_8977) | |
handle->card_type = CARD_TYPE_SD8977; | |
else if (cardp->func->device == SD_DEVICE_ID_8978) | |
handle->card_type = CARD_TYPE_SD8978; | |
else if (cardp->func->device == SD_DEVICE_ID_8997) | |
handle->card_type = CARD_TYPE_SD8997; | |
else if (cardp->func->device == SD_DEVICE_ID_8987) | |
handle->card_type = CARD_TYPE_SD8987; | |
} | |
/** | |
* @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) { | |
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)); | |
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; | |
#ifdef SDIO_OOB_IRQ | |
mrvl_sdio_suspend(func); | |
#endif | |
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; | |
#ifdef SDIO_OOB_IRQ | |
mrvl_sdio_resume(func); | |
#endif | |
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 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_sdio_writeb(moal_handle *handle, t_u32 reg, t_u8 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_sdio_readb(moal_handle *handle, t_u32 reg, t_u8 *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 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 | |
*/ | |
mlan_status | |
woal_sdio_f0_readb(moal_handle *handle, t_u32 reg, t_u8 *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_f0_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_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; | |
#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; | |
} | |
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); | |
#ifdef SDIO_OOB_IRQ | |
mrvl_sdio_release_irq(((struct sdio_mmc_card *)handle->card)-> | |
func); | |
#else | |
sdio_release_irq(((struct sdio_mmc_card *)handle->card)->func); | |
#endif | |
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); | |
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(); | |
func = card->func; | |
sdio_claim_host(func); | |
/* Request the SDIO IRQ */ | |
#ifdef SDIO_OOB_IRQ | |
ret = mrvl_sdio_claim_irq(func, woal_sdio_interrupt); | |
#else | |
ret = sdio_claim_irq(func, woal_sdio_interrupt); | |
#endif | |
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: | |
#ifdef SDIO_OOB_IRQ | |
mrvl_sdio_release_irq(func); | |
#else | |
sdio_release_irq(func); | |
#endif | |
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; | |
} |