diff options
Diffstat (limited to 'drivers/net/wireless/rda/rda_wlan/wlan_sdio.c')
-rwxr-xr-x | drivers/net/wireless/rda/rda_wlan/wlan_sdio.c | 584 |
1 files changed, 584 insertions, 0 deletions
diff --git a/drivers/net/wireless/rda/rda_wlan/wlan_sdio.c b/drivers/net/wireless/rda/rda_wlan/wlan_sdio.c new file mode 100755 index 00000000..ff456053 --- /dev/null +++ b/drivers/net/wireless/rda/rda_wlan/wlan_sdio.c @@ -0,0 +1,584 @@ +#include "wlan_includes.h" + +extern void rda_mci_enable_sdio_irq(struct mmc_host *mmc, int enable); +void check_sdio_status(wlan_private * priv, int result) +{ +#ifdef CHECK_SDIO_STAUTS + if (result) { + priv->SdioErrorCount++; + if ( !rda_combo_wifi_in_test_mode() && priv->SdioErrorCount > WLAN_SDIO_MAX_ERR) { + wlan_push_event(priv, WLAN_EVENT_CHECK_SDIO, priv, FALSE); + priv->SdioErrorCount = 0; + priv->sdio_need_reset = 1; + } + }else{ + priv->SdioErrorCount = 0; + } +#endif +} + +int wlan_card_check_sdio(wlan_private * priv) +{ + int ret = 0; +#ifdef WLAN_SDIO_RESET_DEBUG + priv->debug_count = 0; +#endif + WLAN_ERRP("###################################################"); + WLAN_ERRP("###################################################"); + WLAN_ERRP("###################################################"); + + wlan_indicate_disconnected(priv); + + ret = wlan_reset_card(priv); + if(ret < 0) + WLAN_ERRP("wlan sdio reset failed"); + priv->sdio_need_reset = 0; + return 0; +} +int wlan_read_byte(wlan_private * priv, u32 addr, u8* data) +{ + wlan_sdio_card *card; + int ret = 0; + + card = (wlan_sdio_card*)priv->card; + + if (!card || !card->func){ + WLAN_ERRP("wlan_read_byte(): card or function is NULL!\n"); + return WLAN_STATUS_FAILED; + } + + *data = sdio_readb(card->func, addr, &ret); + if (ret){ + WLAN_ERRP("wlan_read_byte(): sdio_readb failed! ret=%d\n", ret); + //kevin add ,fix restore factory default. sdio fail too many time! + if(ret == -110){ + WLAN_ERRP("sleep 100 msec\n"); + msleep(100); + } + check_sdio_status(priv, ret); + } + + return ret; +} + +int wlan_write_byte(wlan_private * priv, u32 addr, u8 data) +{ + wlan_sdio_card *card; + int ret = 0; + + card = (wlan_sdio_card*)priv->card; + + if (!card || !card->func){ + WLAN_ERRP("wlan_write_byte(): card or function is NULL!\n"); + return WLAN_STATUS_FAILED; + } + + sdio_writeb(card->func, data, addr, &ret); + if (ret){ + WLAN_ERRP("wlan_write_byte(): sdio_writeb failed! ret=%d\n", ret); + check_sdio_status(priv, ret); + } + + return ret; +} + +int wlan_read_bytes(wlan_private * priv, u32 addr, u8* buf, u32 count) +{ + wlan_sdio_card *card; + int ret = 0; + + card = (wlan_sdio_card*)priv->card; + + if (!card || !card->func){ + WLAN_ERRP("wlan_read_bytes(): card or function is NULL!\n"); + return WLAN_STATUS_FAILED; + } + + ret = sdio_readsb(card->func, buf, addr, count); + if (ret){ + WLAN_ERRP("wlan_read_bytes(): sdio_readsb failed! ret=%d\n", ret); + check_sdio_status(priv, ret); + }else{ + WLAN_DBGLAP(WLAN_DA_SDIO, WLAN_DL_DEBUG, "wlan_read_bytes :len %d\n", count); + } + + return ret; +} + +int wlan_write_sdio_2_ahb(wlan_private * priv, u32 addr, u8* buf, u32 count) +{ + wlan_sdio_card *card; + int ret = 0; + u16 size = 0; + u8 size_l = 0, size_h = 0; + u16 bytes_left = 0, offset = 0, batch = 0; +#ifdef RDA_ANDROID_PLATFORM + u32 blockSize = 4, nb = 0; + u8 *packet_to_send = NULL; + struct page *pg = NULL; +#endif + card = (wlan_sdio_card*)priv->card; + + if (!card || !card->func ){ + WLAN_ERRP("wlan_read_byte(): card or function is NULL!\n"); + return WLAN_STATUS_FAILED; + } + + if(rda_combo_wifi_in_test_mode() && is_sdio_init_complete()){ + return WLAN_STATUS_SUCCESS; + } + + if (priv->version == WLAN_VERSION_90_D + || priv->version == WLAN_VERSION_90_E) { + ret = wlan_sdio_flow_ctrl_90(priv); + if(ret){ + WLAN_ERRP("wlan_sdio_flow_ctrl 5990 failed! \n"); + return ret; + } + } else if(priv->version == WLAN_VERSION_91){ + ret = wlan_sdio_flow_ctrl_91(priv); + if(ret){ + WLAN_ERRP("wlan_sdio_flow_ctrl 5991 failed! \n"); + return ret; + } + }else if (priv->version == WLAN_VERSION_91_E) { + ret = wlan_sdio_flow_ctrl_91e(priv); + if (ret) { + WLAN_ERRP("wlan_sdio_flow_ctrl 5991e failed! \n"); + return ret; + } + }else if (priv->version == WLAN_VERSION_91_F) { + ret = wlan_sdio_flow_ctrl_91e(priv); + if (ret) { + WLAN_ERRP("wlan_sdio_flow_ctrl 5991f failed! \n"); + return ret; + } + } else { + WLAN_ERRP("wlan_sdio_flow_ctrl unkown version:%d\n",priv->version); + return ret; + } + +// IN rda android platform sdio should 2^n alligen +#ifdef RDA_ANDROID_PLATFORM + if(count > card->func->cur_blksize){//the left bytes should be 2^n alligen + nb = count%(card->func->cur_blksize); + count -= nb; + while (blockSize < nb ){ + blockSize = blockSize << 1; + } + count += blockSize; + blockSize = count; + } else { + while (blockSize < count){ + blockSize = blockSize << 1; + } + } + size = blockSize/4; + count = blockSize; + WLAN_DBGLAP(WLAN_DA_SDIO, WLAN_DL_DEBUG,"wlan_write_sdio_2_ahb size:%d %d cur_blk:%d\n",count, blockSize, card->func->cur_blksize); +#else + size = count/4; +#endif + size_l = size & 0xff; + size_h = ((size >> 8) & 0x7f) | 0x80; //0x80 flags means lenght higer bytes + + sdio_claim_host(card->func); + ret = wlan_write_byte(priv, IF_SDIO_SDIO2AHB_PKTLEN_L, size_l); + if(ret){ + WLAN_ERRP("SDIO write size_l failed! \n"); + goto out; + } + + ret = wlan_write_byte(priv, IF_SDIO_SDIO2AHB_PKTLEN_H, size_h); + if(ret){ + WLAN_ERRP("SDIO write size_h failed! \n"); + goto out; + } + + bytes_left = count; + while(bytes_left){ + batch = bytes_left > card->func->cur_blksize?card->func->cur_blksize:bytes_left; +#ifdef RDA_ANDROID_PLATFORM + { + packet_to_send = buf + offset; + + if (((u32)packet_to_send >> PAGE_SHIFT) != + (((u32)packet_to_send + batch - 1) >> PAGE_SHIFT)){ + + pg = alloc_page(GFP_KERNEL); + if(!pg){ + ret = -1; + break; + } + memcpy(page_address(pg), packet_to_send, batch); + packet_to_send = page_address(pg); + + ret = sdio_writesb(card->func, addr, packet_to_send, batch); + __free_page(pg); + WLAN_ERRP("wlan data cross page boundary addr:%x size:%x \n", (u32)(buf + offset), batch); + }else + ret = sdio_writesb(card->func, addr, packet_to_send, batch); + } +#else + ret = sdio_writesb(card->func, addr, buf + offset, batch); +#endif + if (ret){ + WLAN_ERRP("SDIO sdio_writesb failed! ret=%d len:%d \n", ret, size*4); + break; + } + offset += batch; + bytes_left -= batch; + } +#ifdef WLAN_SDIO_RESET_DEBUG + priv->debug_count++; +#endif +out: + sdio_release_host(card->func); +#ifdef WLAN_SDIO_RESET_DEBUG + WLAN_DBGLAP(WLAN_DA_SDIO, WLAN_DL_DEBUG, "########### len %d debug_count:%d #######********\n", size * 4, priv->debug_count); + if(priv->debug_count > 300) + ret = -5; +#endif + if (ret) + check_sdio_status(priv, ret); + return ret; +} + +int wlan_wake_up_card(wlan_private * priv) +{ + int ret = 0; +#ifdef WLAN_POWER_MANAGER + wlan_sdio_card *card = (wlan_sdio_card*)priv->card; +#endif + + ENTER(); +#ifdef WLAN_POWER_MANAGER + if (!rda_combo_wifi_in_test_mode()){ + atomic_set(&priv->CardNeedSleep, FALSE); + if(priv->CardInSleep) { + WLAN_DBGLAP(WLAN_DA_PM, WLAN_DL_DEBUG, "wake up card \n"); + sdio_claim_host(card->func); + ret = wlan_write_byte(priv, IF_SDIO_FUN1_INT_TO_DEV, 1); + sdio_release_host(card->func); + if(ret){ + WLAN_ERRP("wakeup card failed \n"); + }else{ + priv->CardInSleep = FALSE; + WLAN_DBGLAP(WLAN_DA_PM, WLAN_DL_DEBUG, + "CardInSleep = FALSE \n"); + wlan_sched_timeout(10); + } + + if (!priv->CardSleepWakeLockOn) { + priv->CardSleepWakeLockOn = TRUE; + wake_lock(&priv->CardSleepTimerLock); + } + } + } +#endif + LEAVE(); + return ret; +} + +void handle_card_to_sleep_cmd(wlan_private * priv) +{ + atomic_set(&priv->CardNeedSleep, TRUE); + complete(&priv->TxThread.comp); +} +int wlan_card_enter_sleep(wlan_private * priv) +{ + int ret = 0; +#ifdef WLAN_POWER_MANAGER + wlan_sdio_card *card = (wlan_sdio_card *) priv->card; +#endif + + ENTER(); + +#ifdef WLAN_POWER_MANAGER + if (!rda_combo_wifi_in_test_mode() + && priv->wlan_pm_enable + && (priv->scan_running != WLAN_SCAN_RUNNING) + && (!priv->assoc_ongoing)) { + if(atomic_read(&priv->CardNeedSleep)) { + sdio_claim_host(card->func); + ret = wlan_write_byte(priv, IF_SDIO_FUN1_INT_PEND, IF_SDIO_HOST_TX_FLAG); + sdio_release_host(card->func); + if (ret) { + WLAN_ERRP("enter sleep failed \n"); + wlan_mod_timer(&priv->CardToSleepTimer, CARD_ENTER_SLEEP_TIMER); + return ret; + } else { + priv->CardInSleep = TRUE; + WLAN_DBGLAP(WLAN_DA_PM, WLAN_DL_DEBUG, "CardInSleep = TRUE \n"); + atomic_set(&priv->CardNeedSleep, FALSE); + } + if (priv->CardSleepWakeLockOn) { + wake_unlock(&priv->CardSleepTimerLock); + priv->CardSleepWakeLockOn = FALSE; + } + if(!(priv->CardToSleepTimer).timer_is_canceled) + wlan_cancel_timer(&priv->CardToSleepTimer); + + WLAN_DBGLAP(WLAN_DA_SDIO, WLAN_DL_DEBUG, "card enter sleep \n"); + } else { + wlan_mod_timer(&priv->CardToSleepTimer, CARD_ENTER_SLEEP_TIMER); + } + } + +#endif + LEAVE(); + return ret; +} +#ifdef WLAN_FLOW_CTRL_90E +int wlan_sdio_flow_ctrl_90(wlan_private * priv) +{ + int ret = 0; + u8 status = 0; + s32 int_sleep_count = 0; + wlan_sdio_card *card = (wlan_sdio_card *) priv->card; + + ENTER(); + wlan_wake_up_card(priv); + while ((!priv->CardRemoved) && is_sdio_patch_complete()) { + sdio_claim_host(card->func); + ret = wlan_read_byte(priv, IF_SDIO_FUN1_INT_PEND, &status); + sdio_release_host(card->func); + if (ret) { + WLAN_ERRP("wlan read IF_SDIO_FUN1_INT_PEND failed \n"); + schedule(); + continue; + } + if ((status & IF_SDIO_INT_SLEEP) == 0) { // If SDIO can't write + if (int_sleep_count >= FLOW_CTRL_INT_SLEEP_RETRY_COUNT_90){ + WLAN_ERRP("Flow_ctrl:max int_sleep_count:%d\n",int_sleep_count); + complete(&priv->RxThread.comp); + msleep(100); + break; + } else { + int_sleep_count++; + } + } else { + if (int_sleep_count > 10) + WLAN_ERRP("Flow_ctrl:int_sleep_count:%d\n",int_sleep_count); + sdio_claim_host(card->func); + ret = wlan_write_byte(priv, IF_SDIO_FUN1_INT_PEND, IF_SDIO_INT_SLEEP); + sdio_release_host(card->func); + break; + } + schedule(); + } + LEAVE(); + return ret; +} +#else +//return 0 success +int wlan_sdio_flow_ctrl_90(wlan_private * priv) +{ + int ret = -1; + u8 status = 0; + s32 count = 0; + wlan_sdio_card * card = (wlan_sdio_card*)priv->card; + + //first wake up card if needed + wlan_wake_up_card(priv); + + + ENTER(); + + while(!priv->CardRemoved){ + sdio_claim_host(card->func); + ret = wlan_read_byte(priv,IF_SDIO_FUN1_INT_PEND, &status); + sdio_release_host(card->func); + if(status & IF_SDIO_INT_RXCMPL) + WLAN_DBGLAP(WLAN_DA_SDIO, WLAN_DL_DEBUG, "IF_SDIO_FUN1_INT_PEND:count count %d : %x \n",count, status); + if(!ret){ + if((status & IF_SDIO_INT_RXCMPL) == 0){ //flows ctrl failed + if(count > FLOW_CTRL_RXCMPL_RETRY_COUNT_90){ + sdio_claim_host(card->func); + wlan_write_byte( priv, IF_SDIO_FUN1_INT_PEND, 0x40); + sdio_release_host(card->func); + complete(&priv->RxThread.comp); + msleep(100); + ret = 0; + WLAN_DBGLAP(WLAN_DA_SDIO, WLAN_DL_CRIT, + "flows ctrl RXCMPL failed, count:%d over,return back \n", + FLOW_CTRL_RXCMPL_RETRY_COUNT_90); + break; + } else { + count ++ ; + } + }else{ + sdio_claim_host(card->func); + ret = wlan_write_byte( priv, IF_SDIO_FUN1_INT_PEND, 0x40); + sdio_release_host(card->func); + if(!ret){ + break; + } + } + } + schedule(); + } + + LEAVE(); + return ret; +} +#endif + +//return 0 success +int wlan_sdio_flow_ctrl_91(wlan_private * priv) +{ + int ret = 0; + u8 status = 0; + s32 int_sleep_count = 0; + wlan_sdio_card * card = (wlan_sdio_card*)priv->card; + + ENTER(); + + //first wake up card if needed + wlan_wake_up_card(priv); + + + + while ((!priv->CardRemoved) && is_sdio_patch_complete()) { + sdio_claim_host(card->func); + ret = wlan_read_byte(priv,IF_SDIO_FUN1_INT_PEND, &status); + sdio_release_host(card->func); + if(ret){ + WLAN_ERRP("wlan read IF_SDIO_FUN1_INT_PEND failed \n"); + schedule(); + continue; + } + + if ((status & IF_SDIO_INT_SLEEP) == 0) { // If SDIO can't write + if (int_sleep_count >= FLOW_CTRL_INT_SLEEP_RETRY_COUNT_91){ + break; + } else { + int_sleep_count++; + } + }else{ + sdio_claim_host(card->func); + ret = wlan_write_byte(priv, IF_SDIO_FUN1_INT_PEND, IF_SDIO_INT_SLEEP); + sdio_release_host(card->func); + break; + + } + if(int_sleep_count < 20) + udelay(10); + else + msleep(1); + } + LEAVE(); + return ret; +} +#ifndef FLOWCTRL_91E +//return 0 success +//int seq_num =0; +int wlan_sdio_flow_ctrl_91e(wlan_private * priv) +{ + int ret = -1; + u8 status = 0; + s32 count = 0; + wlan_sdio_card *card = (wlan_sdio_card *) priv->card; + + wlan_wake_up_card(priv); + + ENTER(); + + while (!priv->CardRemoved) { + sdio_claim_host(card->func); + ret = wlan_read_byte(priv, IF_SDIO_FUN1_INT_PEND, &status); + sdio_release_host(card->func); + if (status & IF_SDIO_INT_RXCMPL) + WLAN_DBGLAP(WLAN_DA_SDIO, WLAN_DL_DEBUG, "IF_SDIO_FUN1_INT_PEND:count count %d : %x \n", count, status); + if (!ret) { + if ((status & IF_SDIO_INT_RXCMPL) == 0) { //flows ctrl failed + if (count > FLOW_CTRL_RXCMPL_RETRY_COUNT_91) { + sdio_claim_host(card->func); + wlan_write_byte(priv, IF_SDIO_FUN1_INT_PEND, 0x40); + sdio_release_host(card->func); + complete(&priv->RxThread.comp); + //msleep(100); + ret = 0; + //seq_num++; + //if(count>4) + //printk("count is %d,seq is %d,sleep_st is %d\n",count,seq_num,priv->CardInSleep); + break; + } else { + count++; + } + //schedule(); + if(count < 20) + udelay(2); + else + msleep(1); + } else { + sdio_claim_host(card->func); + ret = wlan_write_byte(priv, IF_SDIO_FUN1_INT_PEND,0x40); + sdio_release_host(card->func); + //seq_num++; + //if(count>5) + //printk("count is %d,seq is %d,sleep_st is %d\n",count,seq_num,priv->CardInSleep); + if (!ret) { + break; + } + } + } + //schedule(); + + } + + LEAVE(); + return ret; +} +#else +//int seq_num =0; +//return 0 success +int wlan_sdio_flow_ctrl_91e(wlan_private * priv) +{ + int ret = 0; + u8 status = 0; + s32 int_sleep_count = 0; + wlan_sdio_card *card = (wlan_sdio_card *) priv->card; + + ENTER(); + + wlan_wake_up_card(priv); + + while ((!priv->CardRemoved) && is_sdio_patch_complete()) { + sdio_claim_host(card->func); + ret = wlan_read_byte(priv, IF_SDIO_FUN1_INT_PEND, &status); + sdio_release_host(card->func); + if (ret) { + WLAN_ERRP("wlan read IF_SDIO_FUN1_INT_PEND failed \n"); + schedule(); + continue; + } + + if ((status & IF_SDIO_INT_SLEEP) == 0) { // If SDIO can't write + if (int_sleep_count >= FLOW_CTRL_INT_SLEEP_RETRY_COUNT_91){ + //seq_num++; + //printk("count is %d,seq is %d,sleep_st is %d\n",int_sleep_count,seq_num,priv->CardInSleep); + break; + } else { + int_sleep_count++; + } + } else { + + //seq_num++; + //if(int_sleep_count > 10) + // printk("count is %d,seq is %d,sleep_st is %d\n",int_sleep_count,seq_num,priv->CardInSleep); + sdio_claim_host(card->func); + ret = wlan_write_byte(priv, IF_SDIO_FUN1_INT_PEND, IF_SDIO_INT_SLEEP); + sdio_release_host(card->func); + break; + } + if(int_sleep_count < 20) + udelay(10); + else + msleep(1); + } + LEAVE(); + return ret; +} +#endif + |