/* * Copyright (C) 2013 Spreadtrum Communications Inc. * * Filename : sdio_dev.c * Abstract : This file is a implementation for itm sipc command/event function * * Authors : gaole.zhang * * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "sdio_dev.h" #if defined(CONFIG_SDIODEV_TEST) #include "sdio_dev_test.h" #endif static struct mutex g_sdio_func_lock; static struct timeval ack_irq_time = {0}; static int ack_gpio_status = 0; static int marlin_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id); static void marlin_sdio_remove(struct sdio_func *func); static int marlin_sdio_suspend(struct device *dev); static int marlin_sdio_resume(struct device *dev); extern void set_bt_pm_func(void *wakeup,void *sleep); struct sdio_func *sprd_sdio_func[SDIODEV_MAX_FUNCS]; struct sdio_func sdio_func_0; SDIO_CHN_HANDLER sdio_tran_handle[17] = {0}; static struct sdio_data *sdio_data=NULL; static int sdio_irq_num; static int marlinwake_irq_num; static int marlin_sdio_ready_irq_num; static struct wake_lock marlin_wakelock; static struct wake_lock BT_AP_wakelock; static struct wake_lock marlinpub_wakelock; static struct work_struct marlin_wq; static struct work_struct marlinwake_wq; static bool ap_sdio_ready = 0; static bool have_card = 0; static MARLIN_SDIO_READY_T marlin_sdio_ready = {0}; static const struct sdio_device_id marlin_sdio_ids[] = { {SDIO_DEVICE(MARLIN_VENDOR_ID, MARLIN_DEVICE_ID)}, {}, }; static const struct dev_pm_ops marlin_sdio_pm_ops = { .suspend = marlin_sdio_suspend, .resume = marlin_sdio_resume, }; static struct sdio_driver marlin_sdio_driver = { .probe = marlin_sdio_probe, .remove = marlin_sdio_remove, .name = "marlin_sdio", .id_table = marlin_sdio_ids, .drv = { .pm = &marlin_sdio_pm_ops, }, }; static struct mmc_host *sdio_dev_host = NULL; char *sync_data_ptr = NULL; static int gpio_marlin_req_tag = 0; static int gpio_marlinwake_tag = 0; static bool sdio_w_flag = 0; //static bool req_marlin_wait_tag = 0; //0: don't wait 1: wait //atomic_t req_marlin_wait_tag; atomic_t gpioreq_need_pulldown; static bool bt_wake_flag = 0; static bool marlin_bt_wake_flag = 0; static struct completion marlin_ack = {0}; static SLEEP_POLICY_T sleep_para = {0}; spinlock_t sleep_spinlock ; MARLIN_PM_RESUME_WAIT_INIT(marlin_sdio_wait); /************************************************************************/ typedef struct { struct mutex func_lock; struct early_suspend early_suspend; atomic_t in_early_suspend; atomic_t refcnt; }mgr_suspend_t; #define SDIO_OS_LOCK() do{\ if(1 == atomic_read(&g_mgr_suspend.in_early_suspend)) \ {\ mutex_lock(&(g_mgr_suspend.func_lock));\ lock_flag = 1;\ }else{\ lock_flag = 0;\ atomic_inc(&(g_mgr_suspend.refcnt));\ }\ }while(0) #define SDIO_OS_UNLOCK() do{\ if(lock_flag){\ mutex_unlock(&(g_mgr_suspend.func_lock));\ }else{\ atomic_dec(&(g_mgr_suspend.refcnt));}\ }while(0) mgr_suspend_t g_mgr_suspend = {0}; static void sdio_early_suspend(struct early_suspend *es) { unsigned long timeout; SDIOTRAN_ERR("[%s]enter\n", __func__); timeout = jiffies + msecs_to_jiffies(3000); atomic_set(&g_mgr_suspend.in_early_suspend, 1); while(0 != atomic_read(&(g_mgr_suspend.refcnt)) ) { usleep_range(50, 100); if (time_after(jiffies, timeout)) { SDIOTRAN_ERR("[%s][timeout 3000ms][EEROR]\n", __func__); return; } } SDIOTRAN_ERR("[%s]ok\n", __func__); return; } static void sdio_late_resume(struct early_suspend *es) { atomic_set(&g_mgr_suspend.in_early_suspend, 0); SDIOTRAN_ERR("[%s]ok\n", __func__); return; } int mgr_suspend_init(void ) { int ret; atomic_set(&g_mgr_suspend.refcnt, 0); atomic_set(&g_mgr_suspend.in_early_suspend, 0); mutex_init(&g_mgr_suspend.func_lock); g_mgr_suspend.early_suspend.suspend = sdio_early_suspend; g_mgr_suspend.early_suspend.resume = sdio_late_resume; register_early_suspend(&g_mgr_suspend.early_suspend); return 0; } void mgr_suspend_defint(void ) { unregister_early_suspend(&g_mgr_suspend.early_suspend); return; } /************************************************************************/ static int get_sdio_dev_func(struct sdio_func *func) { if(SDIODEV_MAX_FUNCS <= func->num) { SDIOTRAN_ERR("func num err!!! func num is %d!!!",func->num); return -1; } SDIOTRAN_DEBUG("func num is %d!!!",func->num); if (func->num == 1) { sdio_func_0.num = 0; sdio_func_0.card = func->card; sprd_sdio_func[0] = &sdio_func_0; } sprd_sdio_func[func->num] = func; return 0; } static int free_sdio_dev_func(struct sdio_func *func) { if(SDIODEV_MAX_FUNCS <= func->num) { SDIOTRAN_ERR("func num err!!! func num is %d!!!",func->num); return -1; } sprd_sdio_func[0] = NULL; sprd_sdio_func[func->num] = NULL; sdio_func_0.num = 0; sdio_func_0.card = NULL; return 0; } static int wakeup_slave_pin_init(void) { int ret; ret = gpio_request(sdio_data->wake_out,"marlin_wakeup"); if (ret<0) { SDIOTRAN_ERR("req gpio sdio_data->wake_out = %d fail!!!",\ sdio_data->wake_out); return ret; } ret = gpio_direction_output(sdio_data->wake_out,0); if (ret) { SDIOTRAN_ERR("sdio_data->wake_out = %d input set fail!!!",\ sdio_data->wake_out); return ret; } SDIOTRAN_ERR("req sdio_data->wake_out = %d succ!!!",\ sdio_data->wake_out); return ret; } void gpio_timer_handler(unsigned long data) { if(gpio_get_value(sdio_data->wake_ack)) { sleep_para.gpio_opt_tag = 1; mod_timer(&(sleep_para.gpio_timer),\ jiffies + msecs_to_jiffies(sleep_para.ack_high_time));// 250<300<2000-1500 SDIOTRAN_ERR("ack high"); } else { sleep_para.gpio_opt_tag = 0; SDIOTRAN_ERR("gpio_opt_tag=0\n" ); } } int fwdownload_fin = 0; int get_sprd_download_fin(void) { return fwdownload_fin; } EXPORT_SYMBOL_GPL(get_sprd_download_fin); void set_sprd_download_fin(int dl_tag) { marlin_sdio_ready.marlin_sdio_init_end_tag = 1; return fwdownload_fin = dl_tag; } EXPORT_SYMBOL_GPL(set_sprd_download_fin); unsigned int irq_count_change = 0; unsigned int irq_count_change_last = 0; bool get_sprd_marlin_status(void) { printk(KERN_INFO "irq_count_change:%d\n",irq_count_change); printk(KERN_INFO "irq_count_change_last:%d\n",irq_count_change_last); if((irq_count_change == 0)&&(irq_count_change_last == 0)) return 1; else if(irq_count_change != irq_count_change_last) { irq_count_change_last = irq_count_change; return 1; } else return 0; } EXPORT_SYMBOL_GPL(get_sprd_marlin_status); atomic_t is_wlan_open; //0: wlan close, 1: wlan open atomic_t set_marlin_cmplete; //0: not complete, 1: complete int set_wlan_status(int status) { atomic_set(&(is_wlan_open), status); return 0; } EXPORT_SYMBOL_GPL(set_wlan_status); /*add start by sam.sun, 20150108, for get the screen status (on or off) return: 1 screen on 0 screen off */ #define BRIGHTNESS_PATH "/sys/class/backlight/sprd_backlight/brightness" int get_screen_status(void) { struct file *file = NULL; int is_screen_on = 1; char * buf; //0~255 mm_segment_t old_fs; int size =0; loff_t pos = 0; file=filp_open(BRIGHTNESS_PATH, O_RDONLY, 0); if (IS_ERR(file)) SDIOTRAN_ERR("Open file %s failed /n",BRIGHTNESS_PATH); old_fs = get_fs(); set_fs(KERNEL_DS); buf = (int *)kzalloc(4,GFP_KERNEL); vfs_read(file, buf, 3, &pos); size = strlen(buf); if( strncmp(buf,"0",(size-1))==0 ) is_screen_on = 0; //screen off 224 else is_screen_on = 1; //SDIOTRAN_ERR("*brightness is %s, is_screen_on is %d\r",buf, is_screen_on); kfree(buf); filp_close(file, NULL); set_fs(old_fs); return is_screen_on; } /*add end by sam.sun, for get the screen status (on or off)*/ extern bool get_bt_state(void); // 1: bt open , 0: bt close int set_marlin_wakeup(uint32 chn,uint32 user_id) { //user_id: wifi=0x1;bt=0x2 #if !defined(CONFIG_MARLIN_NO_SLEEP) int ret; int flags; int screen_status = 1; int bt_state ; SDIOTRAN_DEBUG("entry"); if(get_sprd_download_fin() != 1) { SDIOTRAN_ERR("marlin unready"); return -1; } /* when bt only, marlin will don't stay 2s wakeup; so add for avoid bt ack 250ms high, then in below cases ap don't req cp and send data directly; case1: first open wlan, then user_id = 1; case2: send AT cmd or loop check, then user_id = 3; mdbg module has done re-try action */ if((1 == atomic_read(&(is_wlan_open))) && (1 == user_id) ) { if((gpio_get_value(sdio_data->wake_ack)) && (0 == sleep_para.gpio_opt_tag) ) msleep(sleep_para.ack_high_time); if(gpio_get_value(sdio_data->wake_ack)) SDIOTRAN_ERR("err:bt req delay 300ms\n"); atomic_set(&is_wlan_open, 0); // avoid repeat into this func } //screen_status = get_screen_status(); // for wifi screen download //bt_state = get_bt_state(); spin_lock_irqsave(&sleep_spinlock, flags); //if((0 == sleep_para.gpio_opt_tag) ||((!screen_status) && (1 == user_id) && (!bt_state)) ) if(0 == sleep_para.gpio_opt_tag) { if(gpio_get_value(sdio_data->wake_ack)) //bt ack 250ms { spin_unlock_irqrestore(&sleep_spinlock, flags); SDIOTRAN_ERR("%d-high,user_id-%d \n", sdio_data->wake_ack,user_id); return -2; } if(0 != ack_gpio_status) //bt ack low,then lose the marlinwake irq { spin_unlock_irqrestore(&sleep_spinlock, flags); SDIOTRAN_ERR("ack_gpio_status-%d\n", ack_gpio_status); return -2; } if(0 == atomic_read(&(set_marlin_cmplete))) //last wakeup don't complete { spin_unlock_irqrestore(&sleep_spinlock, flags); SDIOTRAN_ERR("set_marlin_complete -%d\n", set_marlin_cmplete); return -3; } atomic_set(&(set_marlin_cmplete), 0); //invoid re-entry this func. if((0x1 == user_id) ||(0x3 == user_id)) { sdio_w_flag = 1; } if(0x2 == user_id) { bt_wake_flag = 1; } atomic_set(&gpioreq_need_pulldown, 1);//sleep_para.gpioreq_need_pulldown = 1; gpio_direction_output(sdio_data->wake_out,1); spin_unlock_irqrestore(&sleep_spinlock, flags); SDIOTRAN_ERR("%d-1\n", sdio_data->wake_out ); if((0x1 == user_id) ||(0x3 == user_id)) { ret = wait_for_completion_timeout( &marlin_ack, msecs_to_jiffies(100) ); if (ret == 0){ atomic_set(&(set_marlin_cmplete), 1); SDIOTRAN_ERR("marlin chn %d ack timeout, user_id-%d!!!",chn, user_id); return -ETIMEDOUT;//timeout } } } else { spin_unlock_irqrestore(&sleep_spinlock, flags); } atomic_set(&(set_marlin_cmplete), 1); #endif return 0; } EXPORT_SYMBOL_GPL(set_marlin_wakeup); int set_marlin_sleep(uint32 chn,uint32 user_id) { //user_id: wifi=0x1;bt=0x2 #if !defined(CONFIG_MARLIN_NO_SLEEP) SDIOTRAN_DEBUG("entry"); if( atomic_read(&gpioreq_need_pulldown) )//if(sleep_para.gpioreq_need_pulldown) { gpio_direction_output(sdio_data->wake_out,0); if((user_id == 0x1) ||(user_id == 0x3)) { sdio_w_flag = 0; } if(user_id == 0x2) { bt_wake_flag = 0; } atomic_set(&gpioreq_need_pulldown, 0);//sleep_para.gpioreq_need_pulldown = 0; SDIOTRAN_ERR("gpio%d-0,user_id-%d\n", sdio_data->wake_out, user_id); } #endif return 0; } EXPORT_SYMBOL_GPL(set_marlin_sleep); /*only idle chn can be used for ap write*/ int sdio_dev_chn_idle(uint32 chn) { uint32 active_status; uint8 status0,status1; int err_ret; MARLIN_PM_RESUME_WAIT(marlin_sdio_wait); MARLIN_PM_RESUME_RETURN_ERROR(-1); if(NULL == sprd_sdio_func[SDIODEV_FUNC_0]) { SDIOTRAN_ERR("func uninit!!!"); return -1; } sdio_claim_host(sprd_sdio_func[SDIODEV_FUNC_0]); status0 = sdio_readb(sprd_sdio_func[SDIODEV_FUNC_0],0x840,&err_ret); status1 = sdio_readb(sprd_sdio_func[SDIODEV_FUNC_0],0x841,&err_ret); sdio_release_host(sprd_sdio_func[SDIODEV_FUNC_0]); active_status = (uint32)(status0) + ((uint32)(status1) << 8); if( BIT_0 & ( active_status >> chn )){ SDIOTRAN_DEBUG("SDIO channel num = %x is active!!",chn); return 0; //active } else{ SDIOTRAN_DEBUG("SDIO channel num = %x is idle!!",chn); return 1; //idle } } EXPORT_SYMBOL_GPL(sdio_dev_chn_idle); #if defined(CONFIG_SDIODEV_TEST) /*only active chn can be readed*/ int sdio_dev_get_read_chn(void) { uint8 rw_flag0,rw_flag1; uint8 status0,status1; uint32 chn_status; uint32 chn_rw_flag; uint32 tmp; int err_ret,read_chn; MARLIN_PM_RESUME_WAIT(marlin_sdio_wait); MARLIN_PM_RESUME_RETURN_ERROR(-1); sdio_claim_host(sprd_sdio_func[SDIODEV_FUNC_0]); status0 = sdio_readb(sprd_sdio_func[SDIODEV_FUNC_0],0x840,&err_ret); status1 = sdio_readb(sprd_sdio_func[SDIODEV_FUNC_0],0x841,&err_ret); rw_flag0 = sdio_readb(sprd_sdio_func[SDIODEV_FUNC_0],0x842,&err_ret); rw_flag1 = sdio_readb(sprd_sdio_func[SDIODEV_FUNC_0],0x843,&err_ret); sdio_release_host(sprd_sdio_func[SDIODEV_FUNC_0]); chn_status = (uint32)(status0) + ((uint32)(status1) << 8); chn_rw_flag = (uint32)(rw_flag0) + ((uint32)(rw_flag1) << 8); SDIOTRAN_DEBUG("ap recv: chn_status is 0x%x!!!",chn_status); SDIOTRAN_DEBUG("ap read: chn_rw_flag is 0x%x!!!",chn_rw_flag); tmp = (chn_status&(~chn_rw_flag))&(0x0000ffff); for(read_chn=0;read_chn<16;read_chn++) { if(BIT_0 & (tmp>>read_chn)) { return read_chn; } } return -1; } #else int sdio_dev_get_read_chn(void) { uint8 chn_status; int err_ret; int lock_flag; MARLIN_PM_RESUME_WAIT(marlin_sdio_wait); MARLIN_PM_RESUME_RETURN_ERROR(-1); SDIO_OS_LOCK();//mutex_lock(&g_sdio_func_lock); sdio_claim_host(sprd_sdio_func[SDIODEV_FUNC_0]); chn_status = sdio_readb(sprd_sdio_func[SDIODEV_FUNC_0],0x841,&err_ret); while( chn_status == 0 ){ chn_status = sdio_readb(sprd_sdio_func[SDIODEV_FUNC_0],0x841,&err_ret); } sdio_release_host(sprd_sdio_func[SDIODEV_FUNC_0]); SDIO_OS_UNLOCK();//mutex_unlock(&g_sdio_func_lock); SDIOTRAN_DEBUG("ap read: chn_status is 0x%x!!!",chn_status); if(chn_status == 0xff) { SDIOTRAN_ERR("Sdio Err!!!chn status 0x%x!!!",chn_status); return -1; } else if(chn_status & SDIO_CHN_8) return 8; else if(chn_status & SDIO_CHN_9) return 9; else if(chn_status & SDIO_CHN_11) return 11; else if(chn_status & SDIO_CHN_12) return 12; else if(chn_status & SDIO_CHN_13) return 13; else if(chn_status & SDIO_CHN_14) return 14; else if(chn_status & SDIO_CHN_15) return 15; else { SDIOTRAN_ERR("Invalid sdio read chn!!!chn status 0x%x!!!",chn_status); return -1; } } #endif EXPORT_SYMBOL_GPL(sdio_dev_get_read_chn); /*ONLY WIFI WILL USE THIS FUNC*/ int sdio_chn_status(unsigned short chn, unsigned short *status) { unsigned char status0 = 0; unsigned char status1 = 0; int err_ret; int ret = 0; int lock_flag; SDIOTRAN_DEBUG("ENTRY"); MARLIN_PM_RESUME_WAIT(marlin_sdio_wait); MARLIN_PM_RESUME_RETURN_ERROR(-1); if(NULL == sprd_sdio_func[SDIODEV_FUNC_0]) { SDIOTRAN_ERR("func err"); return -1; } SDIO_OS_LOCK();//mutex_lock(&g_sdio_func_lock); sdio_claim_host(sprd_sdio_func[SDIODEV_FUNC_0]); if (0x00FF & chn) { status0 = sdio_readb(sprd_sdio_func[SDIODEV_FUNC_0],0x840,&err_ret); if (status0 == 0xFF){ SDIOTRAN_ERR("sdio chn reg0 err"); ret = -1; } } if (0xFF00 & chn) { status1 = sdio_readb(sprd_sdio_func[SDIODEV_FUNC_0],0x841,&err_ret); if (status1 == 0xFF){ SDIOTRAN_ERR("sdio chn reg1 err"); ret = -1; } } sdio_release_host(sprd_sdio_func[SDIODEV_FUNC_0]); SDIO_OS_UNLOCK();//mutex_unlock(&g_sdio_func_lock); *status = ( (status0+(status1 << 8)) & chn ); return ret; } EXPORT_SYMBOL_GPL(sdio_chn_status); int sdio_dev_get_chn_datalen(uint32 chn) { uint32 usedata; uint8 status0,status1,status2; int err_ret; int lock_flag; MARLIN_PM_RESUME_WAIT(marlin_sdio_wait); MARLIN_PM_RESUME_RETURN_ERROR(-1); if(NULL == sprd_sdio_func[SDIODEV_FUNC_0]) { SDIOTRAN_ERR("func uninit!!!"); return -1; } SDIO_OS_LOCK();//mutex_lock(&g_sdio_func_lock); sdio_claim_host(sprd_sdio_func[SDIODEV_FUNC_0]); status0 = sdio_readb(sprd_sdio_func[SDIODEV_FUNC_0],(0x800+ 4*chn),&err_ret); status1 = sdio_readb(sprd_sdio_func[SDIODEV_FUNC_0],(0x801+ 4*chn),&err_ret); status2 = sdio_readb(sprd_sdio_func[SDIODEV_FUNC_0],(0x802+ 4*chn),&err_ret); sdio_release_host(sprd_sdio_func[SDIODEV_FUNC_0]); SDIO_OS_UNLOCK();//mutex_unlock(&g_sdio_func_lock); usedata = ((uint32)(status0) ) + ((uint32)(status1) << 8) + ((uint32)(status2) << 16); if(status0 == 0xff || status1 == 0xff || status2 == 0xff) { SDIOTRAN_ERR("read err!!!"); return -1; } return usedata; } EXPORT_SYMBOL_GPL(sdio_dev_get_chn_datalen); int sdio_dev_write(uint32 chn,void* data_buf,uint32 count) { int ret,data_len; int lock_flag; MARLIN_PM_RESUME_WAIT(marlin_sdio_wait); MARLIN_PM_RESUME_RETURN_ERROR(-1); if(NULL == sprd_sdio_func[SDIODEV_FUNC_1]) { SDIOTRAN_ERR("func uninit!!!"); return -1; } if(chn != 0 && chn != 1) { data_len = sdio_dev_get_chn_datalen(chn); if(data_len < count || data_len <= 0) { SDIOTRAN_ERR("chn %d, len %d, cnt %d, err!!!",chn,data_len,count); return -1; } } SDIO_OS_LOCK();//mutex_lock(&g_sdio_func_lock); sdio_claim_host(sprd_sdio_func[SDIODEV_FUNC_1]); ret = sdio_memcpy_toio(sprd_sdio_func[SDIODEV_FUNC_1],chn,data_buf,count); SDIOTRAN_DEBUG("chn %d send ok!!!",chn); sdio_release_host(sprd_sdio_func[SDIODEV_FUNC_1]); SDIO_OS_UNLOCK();//mutex_unlock(&g_sdio_func_lock); return ret; } EXPORT_SYMBOL_GPL(sdio_dev_write); int sdio_dev_read(uint32 chn,void* read_buf,uint32 *count) { int err_ret,data_len; int lock_flag; MARLIN_PM_RESUME_WAIT(marlin_sdio_wait); MARLIN_PM_RESUME_RETURN_ERROR(-1); if(NULL == sprd_sdio_func[SDIODEV_FUNC_1]) { SDIOTRAN_ERR("func uninit!!!"); return -1; } data_len = sdio_dev_get_chn_datalen(chn); if(data_len <= 0) { SDIOTRAN_ERR("chn %d,datelen %d err!!!",chn,data_len); return -1; } SDIOTRAN_DEBUG("ap recv chn %d: read_datalen is 0x%x!!!",chn,data_len); SDIO_OS_LOCK();//mutex_lock(&g_sdio_func_lock); sdio_claim_host(sprd_sdio_func[SDIODEV_FUNC_1]); err_ret = sdio_memcpy_fromio(sprd_sdio_func[SDIODEV_FUNC_1],read_buf,chn,data_len); sdio_release_host(sprd_sdio_func[SDIODEV_FUNC_1]); SDIO_OS_UNLOCK();//mutex_unlock(&g_sdio_func_lock); if(NULL != count) *count = data_len; return err_ret; } EXPORT_SYMBOL_GPL(sdio_dev_read); int sdio_read_wlan(uint32 chn,void* read_buf,uint32 *count) { int err_ret,data_len; data_len = 16384; MARLIN_PM_RESUME_WAIT(marlin_sdio_wait); MARLIN_PM_RESUME_RETURN_ERROR(-1); if(NULL == sprd_sdio_func[SDIODEV_FUNC_1]) { SDIOTRAN_ERR("func uninit!!!"); return -1; } sdio_claim_host(sprd_sdio_func[SDIODEV_FUNC_1]); err_ret = sdio_memcpy_fromio(sprd_sdio_func[SDIODEV_FUNC_1],read_buf,chn,data_len); sdio_release_host(sprd_sdio_func[SDIODEV_FUNC_1]); if(NULL != count) *count = data_len; return err_ret; } EXPORT_SYMBOL_GPL(sdio_read_wlan); int sdiodev_readchn_init(uint32 chn,void* callback,bool with_para) { SDIOTRAN_DEBUG("Param, chn = %d, callback = %p",chn,callback); if(chn >= INVALID_SDIO_CHN) { SDIOTRAN_ERR("err input chn %d",chn); return -1; } sdio_tran_handle[chn].chn = chn; if(with_para == 1) { sdio_tran_handle[chn].tran_callback_para = callback; SDIOTRAN_DEBUG("sdio_tran_handle, chn = %d, callback = %p",sdio_tran_handle[chn].chn,sdio_tran_handle[chn].tran_callback_para); } else { sdio_tran_handle[chn].tran_callback = callback; SDIOTRAN_DEBUG("sdio_tran_handle, chn = %d, callback = %p",sdio_tran_handle[chn].chn,sdio_tran_handle[chn].tran_callback); } return 0; } EXPORT_SYMBOL_GPL(sdiodev_readchn_init); int sdiodev_readchn_uninit(uint32 chn) { if(chn >= INVALID_SDIO_CHN) { SDIOTRAN_ERR("err input chn %d",chn); return -1; } sdio_tran_handle[chn].chn = INVALID_SDIO_CHN; sdio_tran_handle[chn].tran_callback = NULL; sdio_tran_handle[chn].tran_callback_para = NULL; return 0; } EXPORT_SYMBOL_GPL(sdiodev_readchn_uninit); void invalid_recv_flush(uint32 chn) { int len ; char* pbuff = NULL; if(chn < 0 || chn > 15) { SDIOTRAN_ERR("err chn"); return; } len = sdio_dev_get_chn_datalen(chn); if(len <= 0){ SDIOTRAN_ERR("chn %d, len err %d!!",chn,len); return; } SDIOTRAN_ERR("NO VALID DATA! CHN %d FLUSH! DATALEN %d!",chn,len); pbuff = kmalloc(len,GFP_KERNEL); if(pbuff){ SDIOTRAN_DEBUG("Read to Flush,chn=%d,pbuff=%p,len=%d",chn, pbuff, len); sdio_dev_read(chn, pbuff, NULL); kfree(pbuff); }else{ SDIOTRAN_ERR("Kmalloc %d failed!",len); } } EXPORT_SYMBOL_GPL(invalid_recv_flush); void flush_blkchn(void) { uint8 rw_flag0,rw_flag1; uint8 status0,status1; uint32 chn_status; uint32 chn_rw_flag; uint32 tmp; int err_ret,read_chn; MARLIN_PM_RESUME_WAIT(marlin_sdio_wait); MARLIN_PM_RESUME_RETURN_ERROR(-1); sdio_claim_host(sprd_sdio_func[SDIODEV_FUNC_0]); status0 = sdio_readb(sprd_sdio_func[SDIODEV_FUNC_0],0x840,&err_ret); if (status0 == 0xFF){ SDIOTRAN_ERR("sdio chn reg0 err"); return; } status1 = sdio_readb(sprd_sdio_func[SDIODEV_FUNC_0],0x841,&err_ret); if (status1 == 0xFF){ SDIOTRAN_ERR("sdio chn reg1 err"); return; } rw_flag0 = sdio_readb(sprd_sdio_func[SDIODEV_FUNC_0],0x842,&err_ret); if (rw_flag0 == 0xFF){ SDIOTRAN_ERR("sdio RW reg0 err"); return; } rw_flag1 = sdio_readb(sprd_sdio_func[SDIODEV_FUNC_0],0x843,&err_ret); if (rw_flag1 == 0xFF){ SDIOTRAN_ERR("sdio RW reg1 err"); return; } sdio_release_host(sprd_sdio_func[SDIODEV_FUNC_0]); chn_status = (uint32)(status0) + ((uint32)(status1) << 8); chn_rw_flag = (uint32)(rw_flag0) + ((uint32)(rw_flag1) << 8); SDIOTRAN_ERR("chn_status is 0x%x!!!",chn_status); SDIOTRAN_ERR("chn_rw_flag is 0x%x!!!",chn_rw_flag); tmp = (chn_status&(~chn_rw_flag))&(0x0000ffff); for(read_chn=0;read_chn<16;read_chn++) { if(BIT_0 & (tmp>>read_chn)) { SDIOTRAN_ERR("BLK CHN is 0x%x!!!",read_chn); invalid_recv_flush(read_chn); return; } } SDIOTRAN_ERR("NO BLK CHN!!!"); return; } EXPORT_SYMBOL_GPL(flush_blkchn); int sdiolog_handler(void) { if(NULL != sdio_tran_handle[SDIOLOG_CHN].tran_callback) { SDIOTRAN_DEBUG("SDIOLOG_CHN tran_callback=%p",sdio_tran_handle[SDIOLOG_CHN].tran_callback); sdio_tran_handle[SDIOLOG_CHN].tran_callback(); return 0; } else { return -1; } } int sdio_download_handler(void) { if(NULL != sdio_tran_handle[DOWNLOAD_CHANNEL_READ].tran_callback) { SDIOTRAN_DEBUG("DOWNLOAD_CHN tran_callback=%p",sdio_tran_handle[DOWNLOAD_CHANNEL_READ].tran_callback); sdio_tran_handle[DOWNLOAD_CHANNEL_READ].tran_callback(); return 0; } else { return -1; } } int sdio_pseudo_atc_handler(void) { SDIOTRAN_ERR("ENTRY"); if(NULL != sdio_tran_handle[PSEUDO_ATC_CHANNEL_READ].tran_callback) { SDIOTRAN_ERR("tran_callback=%p",sdio_tran_handle[PSEUDO_ATC_CHANNEL_READ].tran_callback); sdio_tran_handle[PSEUDO_ATC_CHANNEL_READ].tran_callback(); return 0; } else { return -1; } } int sdio_pseudo_loopcheck_handler(void) { SDIOTRAN_ERR("ENTRY"); if(NULL != sdio_tran_handle[PSEUDO_ATC_CHANNEL_LOOPCHECK].tran_callback) { SDIOTRAN_ERR("tran_callback=%p",sdio_tran_handle[PSEUDO_ATC_CHANNEL_LOOPCHECK].tran_callback); sdio_tran_handle[PSEUDO_ATC_CHANNEL_LOOPCHECK].tran_callback(); return 0; } else { return -1; } } int sdio_wifi_handler(uint32 chn) { if(NULL != sdio_tran_handle[chn].tran_callback_para) { SDIOTRAN_DEBUG("wifi_handle_chn %d tran_callback=%p",chn,sdio_tran_handle[chn].tran_callback_para); sdio_tran_handle[chn].tran_callback_para(chn); return 0; } else { return -1; } } int sdio_assertinfo_handler(void) { SDIOTRAN_ERR("ENTRY"); if(NULL != sdio_tran_handle[MARLIN_ASSERTINFO_CHN].tran_callback) { SDIOTRAN_ERR("tran_callback=%p",sdio_tran_handle[MARLIN_ASSERTINFO_CHN].tran_callback); sdio_tran_handle[MARLIN_ASSERTINFO_CHN].tran_callback(); return 0; } else { return -1; } } static void marlin_workq(void) { int read_chn; int ret = 0; SDIOTRAN_DEBUG("ENTRY"); #if !defined(CONFIG_SDIODEV_TEST) read_chn = sdio_dev_get_read_chn(); switch(read_chn) { case SDIOLOG_CHN: ret = sdiolog_handler(); break; case DOWNLOAD_CHANNEL_READ: ret = sdio_download_handler(); break; case PSEUDO_ATC_CHANNEL_READ: ret = sdio_pseudo_atc_handler(); break; case PSEUDO_ATC_CHANNEL_LOOPCHECK: ret = sdio_pseudo_loopcheck_handler(); break; case MARLIN_ASSERTINFO_CHN: ret = sdio_assertinfo_handler(); break; case WIFI_CHN_8: case WIFI_CHN_9: ret = sdio_wifi_handler(read_chn); break; default: SDIOTRAN_ERR("no handler for this chn",read_chn); invalid_recv_flush(read_chn); } if(-1 == ret) invalid_recv_flush(read_chn); #else #if defined(SDIO_TEST_CASE4) case4_workq_handler(); #endif #if defined(SDIO_TEST_CASE5) case5_workq_handler(); #endif #if defined(SDIO_TEST_CASE6) case6_workq_handler(); #endif #if defined(SDIO_TEST_CASE9) case9_workq_handler(); #endif #endif//CONFIG_SDIODEV_TEST wake_unlock(&marlin_wakelock); } static irqreturn_t marlin_irq_handler(int irq, void * para) { wake_lock(&marlin_wakelock); irq_set_irq_type(irq,IRQF_TRIGGER_RISING); if(get_sprd_download_fin() != 1) { schedule_work(&marlin_wq); } else if(NULL != sdio_tran_handle[WIFI_CHN_8].tran_callback_para) { sdio_tran_handle[WIFI_CHN_8].tran_callback_para(WIFI_CHN_8); wake_unlock(&marlin_wakelock); } else schedule_work(&marlin_wq); return IRQ_HANDLED; } static int sdio_dev_intr_init(void) { int ret; wake_lock_init(&marlin_wakelock, WAKE_LOCK_SUSPEND, "marlin_wakelock"); INIT_WORK(&marlin_wq, marlin_workq); ret = gpio_request(sdio_data->data_irq,"marlin_irq"); if (ret<0) { SDIOTRAN_ERR("req gpio sdio_data->data_irq = %d fail!!!",\ sdio_data->data_irq); return ret; } ret = gpio_direction_input(sdio_data->data_irq); if (ret<0) { SDIOTRAN_ERR("sdio_data->data_irq = %d input set fail!!!",\ sdio_data->data_irq); return ret; } sdio_irq_num = gpio_to_irq(sdio_data->data_irq); ret = request_irq(sdio_irq_num, marlin_irq_handler,IRQF_TRIGGER_RISING |IRQF_NO_SUSPEND, "sdio_dev_intr", NULL ); //ret = request_irq(sdio_irq_num, marlin_irq_handler,IRQF_TRIGGER_HIGH | IRQF_ONESHOT |IRQF_NO_SUSPEND, "sdio_dev_intr", NULL ); if (ret != 0) { SDIOTRAN_ERR("request irq err!!!gpio is %d!!!",sdio_data->data_irq); return ret; } SDIOTRAN_ERR("ok!!!irq is %d!!!",sdio_irq_num); return 0; } static void sdio_dev_intr_uninit(void) { disable_irq_nosync(sdio_irq_num); free_irq(sdio_irq_num,NULL); gpio_free(sdio_data->data_irq); gpio_free(sdio_data->wake_out); cancel_work_sync(&marlin_wq); //flush_workqueue(&marlin_wq); //destroy_workqueue(&marlin_wq); wake_lock_destroy(&marlin_wakelock); SDIOTRAN_ERR("ok!!!"); } static void set_apsdiohal_ready(void) { ap_sdio_ready = 1; } static void set_apsdiohal_unready(void) { ap_sdio_ready = 0; } //return 1 means ap sdiohal ready bool get_apsdiohal_status(void) { return ap_sdio_ready; } EXPORT_SYMBOL_GPL(get_apsdiohal_status); //return 1 means marlin sdiohal ready bool get_sdiohal_status(void) { return marlin_sdio_ready.marlin_sdio_init_end_tag; } EXPORT_SYMBOL_GPL(get_sdiohal_status); static void clear_sdiohal_status(void) { marlin_sdio_ready.marlin_sdio_init_start_tag = 0; marlin_sdio_ready.marlin_sdio_init_end_tag = 0; } static irqreturn_t marlinsdio_ready_irq_handler(int irq, void * para) { disable_irq_nosync(irq); SDIOTRAN_ERR("entry"); if(!marlin_sdio_ready.marlin_sdio_init_start_tag){ SDIOTRAN_ERR("start"); irq_set_irq_type(irq,IRQF_TRIGGER_LOW); marlin_sdio_ready.marlin_sdio_init_start_tag = 1;} else{ SDIOTRAN_ERR("end"); irq_set_irq_type(irq,IRQF_TRIGGER_HIGH); marlin_sdio_ready.marlin_sdio_init_end_tag = 1;} if(!marlin_sdio_ready.marlin_sdio_init_end_tag) enable_irq(irq); return IRQ_HANDLED; } static int marlin_sdio_sync_init(void) { #if !(defined CONFIG_MACH_SP8730SEEA || defined CONFIG_MACH_SP8730SEEA_QHD) int ret; SDIOTRAN_ERR("entry"); sci_glb_clr(SPRD_PIN_BASE + sdio_data->rfctl_off,(BIT(3)|BIT(7)|0)); sci_glb_set(SPRD_PIN_BASE + sdio_data->rfctl_off,(BIT(4)|BIT(5)|BIT(2)|BIT(6))); ret = gpio_request(sdio_data->io_ready,"marlin_sdio_ready_irq"); if (ret<0) { SDIOTRAN_ERR("req gpio sdio_data->io_ready = %d fail!!!",\ sdio_data->io_ready); return ret; } ret = gpio_direction_input(sdio_data->io_ready); if (ret<0) { SDIOTRAN_ERR("sdio_data->io_ready = %d input set fail!!!",\ sdio_data->io_ready); return ret; } marlin_sdio_ready_irq_num = gpio_to_irq(sdio_data->io_ready); SDIOTRAN_ERR("req irq"); ret = request_irq(marlin_sdio_ready_irq_num, marlinsdio_ready_irq_handler,IRQF_TRIGGER_RISING |IRQF_NO_SUSPEND, "m_sdio_ready_intr", NULL ); if (ret != 0) { SDIOTRAN_ERR("request irq err!!!gpio is %d!!!",sdio_data->io_ready); return ret; } SDIOTRAN_ERR("ok!!!irq is %d!!!",marlin_sdio_ready_irq_num); #endif return 0; } void marlin_sdio_sync_uninit(void) { free_irq(marlin_sdio_ready_irq_num,NULL); gpio_free(sdio_data->io_ready); #if !(defined CONFIG_MACH_SP8730SEEA || defined CONFIG_MACH_SP8730SEEA_QHD) sci_glb_clr(SPRD_PIN_BASE + sdio_data->rfctl_off,(BIT(4)|BIT(5)|0)); sci_glb_set(SPRD_PIN_BASE + sdio_data->rfctl_off,(BIT(4))); #endif SDIOTRAN_ERR("ok!!!"); } EXPORT_SYMBOL_GPL(marlin_sdio_sync_uninit); static void marlinack_workq(void) { int read_chn; int ret = 0; SDIOTRAN_ERR("ENTRY"); if(sdio_w_flag == 1) complete(&marlin_ack); } int time_d_value(struct timeval *start, struct timeval *end) { return (end->tv_sec - start->tv_sec)*1000000 + (end->tv_usec - start->tv_usec); } static irqreturn_t marlinwake_irq_handler(int irq, void * para) { struct timeval cur_time; uint32 gpio_wake_status = 0, usec; //irq_set_irq_type(irq,IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING); gpio_wake_status = gpio_get_value(sdio_data->wake_ack); if(get_sprd_download_fin() != 1) { if(!marlin_sdio_ready.marlin_sdio_init_start_tag && gpio_wake_status){ SDIOTRAN_ERR("start,g_val=%d",gpio_wake_status); irq_set_irq_type(irq,IRQF_TRIGGER_LOW); marlin_sdio_ready.marlin_sdio_init_start_tag = 1; } else if(!marlin_sdio_ready.marlin_sdio_init_end_tag && (!gpio_wake_status)){ SDIOTRAN_ERR("end,g_val=%d",gpio_wake_status); irq_set_irq_type(irq,IRQF_TRIGGER_RISING); marlin_sdio_ready.marlin_sdio_init_end_tag = 1; } else { SDIOTRAN_ERR("marlin gpio0 err interupt,g_val=%d",gpio_wake_status); } } else { /*add count irq for loopcheck*/ irq_count_change++; /* avoid gpio jump , so need check the last and cur gpio value.*/ do_gettimeofday(&cur_time); if(ack_gpio_status == gpio_wake_status) { // usec = time_d_value(&ack_irq_time, &cur_time); // if(usec < 200) //means invalid gpio value, so discard { SDIOTRAN_ERR("discard gpio%d irq\n", sdio_data->wake_ack); return IRQ_HANDLED; } // SDIOTRAN_ERR("gpio%d %d-->%d\n",sdio_data->wake_ack, gpio_wake_status, 1 - gpio_wake_status ); // gpio_wake_status = 1 - gpio_wake_status; } ack_irq_time = cur_time; ack_gpio_status = gpio_wake_status; SDIOTRAN_ERR("%d-%d\n",sdio_data->wake_ack, gpio_wake_status ); if(gpio_wake_status) irq_set_irq_type(irq, IRQ_TYPE_EDGE_FALLING); else irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING); if(gpio_wake_status) { wake_lock(&marlinpub_wakelock); sleep_para.gpio_up_time = jiffies; } else { wake_unlock(&marlinpub_wakelock); sleep_para.gpio_down_time = jiffies; } if((!gpio_wake_status) && time_after(sleep_para.gpio_down_time,sleep_para.gpio_up_time)) { if(jiffies_to_msecs(sleep_para.gpio_down_time -\ sleep_para.gpio_up_time)> sleep_para.bt_req_time) { SDIOTRAN_ERR("BT REQ!!!"); marlin_bt_wake_flag = 1; wake_lock_timeout(&BT_AP_wakelock, sleep_para.wake_lock_time); //wsh } } //schedule_work(&marlinack_wq); if(gpio_wake_status) { if( atomic_read(&gpioreq_need_pulldown) )//if(sleep_para.gpioreq_need_pulldown) { sleep_para.gpio_opt_tag = 1; SDIOTRAN_ERR("gpio_opt_tag-1\n"); mod_timer(&(sleep_para.gpio_timer), jiffies + msecs_to_jiffies(sleep_para.marlin_waketime) ); if(sdio_w_flag == 1) { complete(&marlin_ack); SDIOTRAN_ERR("ack-sem\n"); set_marlin_sleep(0xff,0x1); } else { SDIOTRAN_ERR("sdio_w_flag:%d\n", sdio_w_flag); set_marlin_sleep(0xff,0x2); } } } } return IRQ_HANDLED; } static int marlin_wake_intr_init(void) { int ret; sdio_w_flag = 0; bt_wake_flag = 0; INIT_WORK(&marlinwake_wq, marlinack_workq); init_completion (&marlin_ack); wake_lock_init(&BT_AP_wakelock, WAKE_LOCK_SUSPEND, "marlinbtup_wakelock"); wake_lock_init(&marlinpub_wakelock, WAKE_LOCK_SUSPEND, "marlinpub_wakelock"); ret = gpio_request(sdio_data->wake_ack,"marlinwake_irq"); if (ret<0) { SDIOTRAN_ERR("req gpio sdio_data->wake_ack = %d fail!!!",\ sdio_data->wake_ack); return ret; } ret = gpio_direction_input(sdio_data->wake_ack); if (ret<0) { SDIOTRAN_ERR("sdio_data->wake_ack = %d input set fail!!!",\ sdio_data->wake_ack); return ret; } marlinwake_irq_num = gpio_to_irq(sdio_data->wake_ack); ret = request_irq(marlinwake_irq_num, marlinwake_irq_handler,IRQF_TRIGGER_RISING |IRQF_NO_SUSPEND, "marlin_wake_intr", NULL ); //ret = request_irq(marlinwake_irq_num, marlinwake_irq_handler,IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND, "marlin_wake_intr", NULL ); if (ret != 0) { SDIOTRAN_ERR("request irq err!!!gpio is %d!!!",sdio_data->wake_ack); return ret; } SDIOTRAN_ERR("ok!!!irq is %d!!!",marlinwake_irq_num); return 0; } static void marlin_wake_intr_uninit(void) { disable_irq_nosync(marlinwake_irq_num); free_irq(marlinwake_irq_num,NULL); gpio_free(sdio_data->wake_ack); cancel_work_sync(&marlinwake_wq); //flush_workqueue(&marlinwake_wq); //destroy_workqueue(&marlinwake_wq); wake_lock_destroy(&BT_AP_wakelock); wake_lock_destroy(&marlinpub_wakelock); SDIOTRAN_ERR("ok!!!"); } static void sdio_tran_sync(void) { int datalen,i,j; SDIOTRAN_ERR("entry"); set_blklen(4); while(!sdio_dev_chn_idle(SDIO_SYNC_CHN)) { SDIOTRAN_ERR("sync chn is active!!!"); //return; } while(1) { datalen = sdio_dev_get_chn_datalen(SDIO_SYNC_CHN); if(datalen > 0) { SDIOTRAN_ERR("channel %d len is =%d!!!",SDIO_SYNC_CHN,datalen); break; } else SDIOTRAN_ERR("channel %d len is =%d!!!",SDIO_SYNC_CHN,datalen); } sync_data_ptr = kmalloc(4,GFP_KERNEL); if (NULL == sync_data_ptr) { SDIOTRAN_ERR("kmalloc sync buf err!!!"); return -1; } SDIOTRAN_ERR("kmalloc sync buf ok!!!"); for(i=0;i<4;i++) *(sync_data_ptr+i) = i; sdio_dev_write(SDIO_SYNC_CHN,sync_data_ptr,\ ((datalen > 4) ? 4: datalen)); kfree(sync_data_ptr); set_blklen(512); } void set_blklen(int blklen) { sdio_claim_host(sprd_sdio_func[1]); sdio_set_block_size(sprd_sdio_func[1], blklen); sdio_release_host(sprd_sdio_func[1]); SDIOTRAN_ERR("set blklen = %d!!!",blklen); } static void sdio_init_timer(void) { init_timer(&sleep_para.gpio_timer); sleep_para.gpio_timer.function = gpio_timer_handler; #if defined(CONFIG_WCN_EXTENSION) sleep_para.marlin_waketime = 350; sleep_para.bt_req_time = 100; sleep_para.ack_high_time = 200; sleep_para.wake_lock_time = HZ/2; #else sleep_para.marlin_waketime = 1500; sleep_para.bt_req_time = 200; sleep_para.ack_high_time = 300; sleep_para.wake_lock_time = HZ*2; #endif sleep_para.gpio_opt_tag = 0; atomic_set(&gpioreq_need_pulldown, 1);//sleep_para.gpioreq_need_pulldown = 1; } static void sdio_uninit_timer(void) { del_timer(&sleep_para.gpio_timer); sleep_para.marlin_waketime = 0; sleep_para.bt_req_time = 0; sleep_para.ack_high_time = 0; sleep_para.wake_lock_time = 0; sleep_para.gpio_opt_tag = 0; atomic_set(&gpioreq_need_pulldown, 1);//sleep_para.gpioreq_need_pulldown = 1; } static int marlin_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) { int ret; SDIOTRAN_ERR("sdio drv and dev match!!!"); mutex_init(&g_sdio_func_lock); ret = get_sdio_dev_func(func); if(ret < 0) { SDIOTRAN_ERR("get func err!!!\n"); return ret; } SDIOTRAN_ERR("get func ok!!!\n"); /* Enable Function 1 */ sdio_claim_host(sprd_sdio_func[1]); ret = sdio_enable_func(sprd_sdio_func[1]); sdio_set_block_size(sprd_sdio_func[1], 512); sdio_release_host(sprd_sdio_func[1]); if (ret < 0) { SDIOTRAN_ERR("enable func1 err!!! ret is %d",ret); return ret; } SDIOTRAN_ERR("enable func1 ok!!!"); spin_lock_init(&sleep_spinlock); sdio_init_timer(); clear_sdiohal_status(); wakeup_slave_pin_init(); marlin_wake_intr_init(); //marlin_sdio_sync_init(); //case6 #if defined(CONFIG_SDIODEV_TEST) gaole_creat_test(); #endif sdio_dev_intr_init(); #if defined(CONFIG_SDIODEV_TEST) sdio_tran_sync(); #endif /*case1 #if defined(CONFIG_SDIODEV_TEST) gaole_creat_test(); #endif */ //set_bt_pm_func(set_marlin_wakeup,set_marlin_sleep); set_apsdiohal_ready(); have_card = 1; return 0; } static void marlin_sdio_remove(struct sdio_func *func) { SDIOTRAN_DEBUG("entry"); sdio_uninit_timer(); free_sdio_dev_func(func); sdio_dev_intr_uninit(); marlin_wake_intr_uninit(); //marlin_sdio_sync_uninit(); set_apsdiohal_unready(); clear_sdiohal_status(); SDIOTRAN_DEBUG("ok"); } static int marlin_sdio_suspend(struct device *dev) { mutex_lock(&g_mgr_suspend.func_lock); SDIOTRAN_ERR("[%s]enter\n", __func__); gpio_marlin_req_tag = gpio_get_value(sdio_data->data_irq); if(gpio_marlin_req_tag) SDIOTRAN_ERR("err marlin_req!!!"); else irq_set_irq_type(sdio_irq_num,IRQF_TRIGGER_HIGH); gpio_marlinwake_tag = gpio_get_value(sdio_data->wake_ack); if(gpio_marlinwake_tag) SDIOTRAN_ERR("err marlinwake!!!"); else irq_set_irq_type(marlinwake_irq_num,IRQF_TRIGGER_HIGH); smp_mb(); SDIOTRAN_ERR("[%s]ok\n", __func__); return 0; } static int marlin_sdio_resume(struct device *dev) { SDIOTRAN_ERR("[%s]enter\n", __func__); /****************/ mutex_unlock(&g_mgr_suspend.func_lock); sleep_para.gpio_opt_tag = 0; set_marlin_wakeup(0, 1); mod_timer(&(sleep_para.gpio_timer),jiffies + msecs_to_jiffies(sleep_para.marlin_waketime)); /***************/ smp_mb(); SDIOTRAN_ERR("[%s]ok\n", __func__); return 0; } static void* sdio_dev_get_host(void) { struct device *dev; struct sdhci_host *host; struct platform_device *pdev; dev = bus_find_device_by_name(&platform_bus_type, NULL, sdio_data->sdhci); if(dev == NULL) { SDIOTRAN_ERR("sdio find dev by name failed!!!"); return NULL; } pdev = to_platform_device(dev); if(pdev == NULL) { SDIOTRAN_ERR("sdio dev get platform device failed!!!"); return NULL; } host = platform_get_drvdata(pdev); return container_of(host, struct mmc_host, private); } void marlin_sdio_uninit(void) { set_sprd_download_fin(0); sdio_unregister_driver(&marlin_sdio_driver); mgr_suspend_defint(); sdio_dev_host = NULL; have_card = 0; kfree(sdio_data); sdio_data = NULL; SDIOTRAN_ERR("ok"); } int marlin_sdio_init(void) { int ret; struct device_node *np; SDIOTRAN_ERR("entry"); if(have_card == 1){ marlin_sdio_uninit(); } mgr_suspend_init(); //atomic_set(&req_marlin_wait_tag, 0); atomic_set(&gpioreq_need_pulldown, 1); atomic_set(&is_wlan_open, 0); atomic_set(&set_marlin_cmplete, 1); sdio_data = kzalloc(sizeof(*sdio_data), GFP_KERNEL); if (!sdio_data) return -ENOMEM; np = of_find_node_by_name(NULL, "sprd-marlin"); if (!np) { SDIOTRAN_ERR("sprd-marlin not found"); return -1; } sdio_data->wake_ack = of_get_gpio(np, 0); sdio_data->data_irq = of_get_gpio(np, 1); sdio_data->wake_out = of_get_gpio(np, 2); sdio_data->io_ready = of_get_gpio(np, 3); of_property_read_u32(np, "cp-rfctl-offset", &(sdio_data->rfctl_off)); ret = of_property_read_string(np, "sdhci-name", &(sdio_data->sdhci)); if (ret) { SDIOTRAN_ERR("get sdhci-name failed"); return -1; } sdio_dev_host = sdio_dev_get_host(); if(NULL == sdio_dev_host) { SDIOTRAN_ERR("get host failed!!!"); return -1; } SDIOTRAN_ERR("sdio get host ok!!!"); ret = sdio_register_driver(&marlin_sdio_driver); if(0 != ret) { SDIOTRAN_ERR("sdio register drv err!!!ret is %d!!!",ret); return -1; } SDIOTRAN_ERR("sdio register drv succ!!!"); flush_delayed_work(&sdio_dev_host->detect); mmc_detect_change(sdio_dev_host, 0); return ret; } EXPORT_SYMBOL_GPL(marlin_sdio_init);