summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/rda/rda_wlan/wlan_sdio.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/rda/rda_wlan/wlan_sdio.c')
-rwxr-xr-xdrivers/net/wireless/rda/rda_wlan/wlan_sdio.c584
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
+