/** @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 <linux/firmware.h>
|
|
#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;
|
}
|