1859 lines
		
	
	
		
			60 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			1859 lines
		
	
	
		
			60 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| #include <linux/kernel.h>
 | |
| #include <linux/version.h>
 | |
| #include <linux/module.h>
 | |
| #include <linux/mmc/sdio.h>
 | |
| #include <linux/mmc/core.h>
 | |
| #include <linux/mmc/sdio_func.h>
 | |
| #include <linux/mmc/sdio_ids.h>
 | |
| #include <linux/mmc/card.h>
 | |
| #include <linux/suspend.h>
 | |
| #include <linux/jiffies.h>
 | |
| 
 | |
| #include <linux/delay.h>
 | |
| #include <linux/wait.h>
 | |
| 
 | |
| #include <linux/pm.h>
 | |
| 
 | |
| #include "nvt_bus.h"
 | |
| #include "nvt_wlan_linux.h"
 | |
| #include "nvt_bus_sdioif.h"
 | |
| #include "nvt_util_dbg.h"
 | |
| #include "nvt_diag.h"
 | |
| #include "nvt_wlan_priv.h"
 | |
| #include "nvt_iw.h"
 | |
| 
 | |
| 
 | |
| //20161020 nash: work around for power-saving wake up
 | |
| #define NVT_SDIO_CMD_RETRY                  3
 | |
| 
 | |
| //20161020 nash: sdio read/write function with retry
 | |
| //               (work around for hw power-saving)
 | |
| static void nvt_retry_sdio_writeb(struct sdio_func *func, u8 b,
 | |
|         u32 addr, s32 *err_ret)
 | |
| {
 | |
|         s32 cmd_retry = NVT_SDIO_CMD_RETRY;
 | |
| 
 | |
|         do {
 | |
|                 sdio_writeb(func, b, addr, err_ret);
 | |
| 
 | |
|         } while (*err_ret && --cmd_retry > 0);
 | |
| }
 | |
| 
 | |
| static s32 nvt_retry_sdio_writesb(struct sdio_func *func, u32 addr,
 | |
|         void *src, s32 count)
 | |
| {
 | |
|         s32 ret;
 | |
|         s32 cmd_retry = NVT_SDIO_CMD_RETRY;
 | |
| 
 | |
|         do {
 | |
|                 ret = sdio_writesb(func, addr, src, count);
 | |
| 
 | |
|         } while (ret && --cmd_retry > 0);
 | |
| 
 | |
|         return ret;
 | |
| }
 | |
| 
 | |
| static void nvt_retry_sdio_writew(struct sdio_func *func, u16 b,
 | |
|         u32 addr, s32 *err_ret)
 | |
| {
 | |
|         s32 cmd_retry = NVT_SDIO_CMD_RETRY;
 | |
| 
 | |
|         do {
 | |
|                 sdio_writew(func, b, addr, err_ret);
 | |
| 
 | |
|         } while (*err_ret && --cmd_retry > 0);
 | |
| }
 | |
| 
 | |
| static void nvt_retry_sdio_writel(struct sdio_func *func, u32 b,
 | |
|         u32 addr, s32 *err_ret)
 | |
| {
 | |
|         s32 cmd_retry = NVT_SDIO_CMD_RETRY;
 | |
| 
 | |
|         do {
 | |
|                 sdio_writel(func, b, addr, err_ret);
 | |
| 
 | |
|         } while (*err_ret && --cmd_retry > 0);
 | |
| }
 | |
| 
 | |
| 
 | |
| static s32 nvt_retry_sdio_readsb(struct sdio_func *func, void *dst,
 | |
|         u32 addr, s32 count)
 | |
| {
 | |
|         s32 ret;
 | |
|         s32 cmd_retry = NVT_SDIO_CMD_RETRY;
 | |
| 
 | |
|         do {
 | |
|                 ret = sdio_readsb(func, dst, addr, count);
 | |
| 
 | |
|         } while (ret && --cmd_retry > 0);
 | |
| 
 | |
|         return ret;
 | |
| }
 | |
| 
 | |
| static u8 nvt_retry_sdio_readb(struct sdio_func *func,
 | |
|         u32 addr, s32 *err_ret)
 | |
| {
 | |
|         u8 val;
 | |
|         s32 cmd_retry = NVT_SDIO_CMD_RETRY;
 | |
| 
 | |
|         do {
 | |
|                 val = sdio_readb(func, addr, err_ret);
 | |
| 
 | |
|         } while (*err_ret && --cmd_retry > 0);
 | |
| 
 | |
|         return val;
 | |
| 
 | |
| }
 | |
| 
 | |
| static u32 nvt_retry_sdio_readl(struct sdio_func *func, u32 addr, s32 *err_ret)
 | |
| {
 | |
|         u32 val;
 | |
|         s32 cmd_retry = NVT_SDIO_CMD_RETRY;
 | |
| 
 | |
|         do {
 | |
|                 val = sdio_readl(func, addr, err_ret);
 | |
| 
 | |
|         } while (*err_ret && --cmd_retry > 0);
 | |
| 
 | |
|         return val;
 | |
| }
 | |
| 
 | |
| static u8 nvt_retry_sdio_f0_readb(struct sdio_func *func,
 | |
|         u32 addr, s32 *err_ret)
 | |
| {
 | |
|         u8 val;
 | |
|         s32 cmd_retry = NVT_SDIO_CMD_RETRY;
 | |
| 
 | |
|         do {
 | |
|                 val = sdio_f0_readb(func, addr, err_ret);
 | |
| 
 | |
|         } while (*err_ret && --cmd_retry > 0);
 | |
| 
 | |
|         return val;
 | |
| }
 | |
| 
 | |
| static void nvt_retry_sdio_f0_writeb(struct sdio_func *func, u8 b,
 | |
|         u32 addr, s32 *err_ret)
 | |
| {
 | |
|         s32 cmd_retry = NVT_SDIO_CMD_RETRY;
 | |
| 
 | |
|         do {
 | |
|                 sdio_f0_writeb(func, b, addr, err_ret);
 | |
| 
 | |
|         } while (*err_ret && --cmd_retry > 0);
 | |
| }
 | |
| 
 | |
| static s32 nvt_retry_sdio_memcpy_toio(struct sdio_func *func, u32 addr,
 | |
|         void *src, s32 count)
 | |
| {
 | |
|         s32 ret;
 | |
|         s32 cmd_retry = NVT_SDIO_CMD_RETRY;
 | |
| 
 | |
|         do {
 | |
|                 ret = sdio_memcpy_toio(func, addr, src, count);
 | |
| 
 | |
|         } while (ret && --cmd_retry > 0);
 | |
| 
 | |
|         return ret;
 | |
| }
 | |
| 
 | |
| static s32 nvt_retry_sdio_memcpy_fromio(struct sdio_func *func, void *dst,
 | |
|         u32 addr, s32 count)
 | |
| {
 | |
|         s32 ret;
 | |
|         s32 cmd_retry = NVT_SDIO_CMD_RETRY;
 | |
| 
 | |
|         do {
 | |
|                 ret = sdio_memcpy_fromio(func, dst, addr, count);
 | |
| 
 | |
|         } while (ret && --cmd_retry > 0);
 | |
| 
 | |
|         return ret;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * nvt_usb_bus_tx_done_cb - default bus TX done callback function
 | |
|  * @skb: transmitted socket buffer
 | |
|  * @status_code: TX completed error code
 | |
|  *
 | |
|  * This function is the default callback function for TX done. the given socket
 | |
|  * buffer would be freed.
 | |
|  */
 | |
| static void nvt_sdio_bus_tx_done_cb(struct sk_buff *skb, s32 status_code)
 | |
| {
 | |
|         dev_kfree_skb_any(skb);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * nvt_sdio_pm_wait_resume - wait for resume
 | |
|  * @nvt_wdev_sdio: _nvt_wdev_sdio structure
 | |
|  * @wait_q: wait queue
 | |
|  *
 | |
|  * Return: 0:resume already, -1:still in suspend state
 | |
|  */
 | |
| static s32 nvt_sdio_pm_wait_resume(struct _nvt_wdev_sdio *nvt_wdev_sdio,
 | |
|         wait_queue_head_t *wait_q)
 | |
| {
 | |
| 
 | |
|         return 0;
 | |
| }
 | |
| 
 | |
| static s32 nvt_sdio_wait_ctrl_msg_resp(struct _nvt_wdev_sdio *nvt_wdev_sdio)
 | |
| {
 | |
|         s32 ret = 0;
 | |
| 
 | |
|         if (wait_event_timeout(
 | |
|                 nvt_wdev_sdio->ctrl_msg_resp_wait,
 | |
|                 nvt_wdev_sdio->ctrl_msg_resp_rdy,
 | |
|                 msecs_to_jiffies(NVT_SDIO_CTRL_MSG_TIMEOUT)) == 0) {
 | |
|                 nvt_dbg(ERROR, "%s: wait timeout\n", __func__);
 | |
|                 ret = -1;
 | |
|         }
 | |
| 
 | |
|         return ret;
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| static s32 nvt_sdio_rwu(struct _nvt_wdev_sdio *nvt_wdev_sdio, bool write,
 | |
|         u8 func_num, u32 addr, void *data, u32 data_len)
 | |
| {
 | |
|         s32 ret;
 | |
|         s32 retry = 0;
 | |
| 
 | |
|         if (nvt_wdev_sdio == NULL) {
 | |
|                 nvt_dbg(ERROR, "%s: nvt_wdev_sdio is NULL\n", __func__);
 | |
|                 return -EFAULT;
 | |
|         }
 | |
| 
 | |
|         if (func_num != NVT_SDIO_FUNC0 && func_num != NVT_SDIO_FUNC1) {
 | |
|                 nvt_dbg(ERROR, "%s: error sdio func\n", __func__);
 | |
|                 return -ENODEV;
 | |
|         }
 | |
| 
 | |
|         if (data_len != 1 && data_len != 2 && data_len != 4) {
 | |
|                 nvt_dbg(ERROR, "%s: data_len fail\n", __func__);
 | |
|                 return -EPERM;
 | |
|         }
 | |
| 
 | |
|         do {
 | |
|                 if (retry) {
 | |
|                         udelay(NVT_SDIO_ACCESS_SLEEP_US);
 | |
|                 }
 | |
| 
 | |
|                 ret = nvt_sdio_pm_wait_resume(nvt_wdev_sdio,
 | |
|                         &nvt_wdev_sdio->req_byte_wait);
 | |
|                 if (ret) {
 | |
|                         nvt_dbg(ERROR, "%s: waiting resume..\n", __func__);
 | |
|                         continue;
 | |
|                 }
 | |
| 
 | |
|                 if (write) {
 | |
|                         if (data_len == 1) {
 | |
|                                 //20161020 nash: nvt sdio retry fn
 | |
|                                 //(work around for hw power-saving)
 | |
|                                 nvt_retry_sdio_writeb(
 | |
|                                         nvt_wdev_sdio->func[func_num],
 | |
|                                         *(u8 *)data, addr, &ret);
 | |
| 
 | |
|                         } else if (data_len == 2) {
 | |
|                                 //20161020 nash: nvt sdio retry fn
 | |
|                                 //(work around for hw power-saving)
 | |
|                                 nvt_retry_sdio_writew(
 | |
|                                         nvt_wdev_sdio->func[func_num],
 | |
|                                         *(u16 *)data, addr, &ret);
 | |
| 
 | |
|                         } else if (data_len == 4) {
 | |
|                                 //20161020 nash: nvt sdio retry fn
 | |
|                                 //(work around for hw power-saving)
 | |
|                                 nvt_retry_sdio_writel(
 | |
|                                         nvt_wdev_sdio->func[func_num],
 | |
|                                         *(u32 *)data, addr, &ret);
 | |
|                         }
 | |
|                         if (ret) {
 | |
|                                 nvt_dbg(ERROR, "%s: sdio_writeX fail(%d)\n",
 | |
|                                         __func__, ret);
 | |
|                                 continue;
 | |
|                         }
 | |
| 
 | |
|                 } else {
 | |
|                         if (data_len == 1) {
 | |
|                                 //20161020 nash: nvt sdio retry fn
 | |
|                                 //(work around for hw power-saving)
 | |
|                                 *(u8 *)data = nvt_retry_sdio_readb(
 | |
|                                         nvt_wdev_sdio->func[func_num], addr,
 | |
|                                         &ret);
 | |
| 
 | |
|                         } else if (data_len == 2) {
 | |
|                                 //20161020 nash: nvt sdio retry fn
 | |
|                                 //(work around for hw power-saving)
 | |
|                                 *(u16 *)data = nvt_retry_sdio_readl(
 | |
|                                         nvt_wdev_sdio->func[func_num], addr,
 | |
|                                         &ret);
 | |
| 
 | |
|                         } else if (data_len == 4) {
 | |
|                                 //20161020 nash: nvt sdio retry fn
 | |
|                                 //(work around for hw power-saving)
 | |
|                                 *(u32 *)data = nvt_retry_sdio_readl(
 | |
|                                         nvt_wdev_sdio->func[func_num], addr,
 | |
|                                         &ret);
 | |
|                         }
 | |
|                         if (ret) {
 | |
|                                 nvt_dbg(ERROR, "%s: sdio_readX fail(%d)\n",
 | |
|                                         __func__, ret);
 | |
|                                 continue;
 | |
|                         }
 | |
|                 }
 | |
| 
 | |
|         } while (retry++ < NVT_SDIO_ACCESS_RETRY_LIMIT && ret != 0);
 | |
| 
 | |
|         return ret;
 | |
| 
 | |
| }
 | |
| 
 | |
| static s32 nvt_sdio_readu8(struct _nvt_wdev_sdio *nvt_wdev_sdio, u8 func_num,
 | |
|         u32 addr, u8 *ret_data)
 | |
| {
 | |
|         return nvt_sdio_rwu(nvt_wdev_sdio, false, func_num,
 | |
|                 addr, (void *)ret_data, 1);
 | |
| }
 | |
| 
 | |
| static s32 nvt_sdio_readu32(struct _nvt_wdev_sdio *nvt_wdev_sdio, u8 func_num,
 | |
|         u32 addr, u32 *ret_data)
 | |
| {
 | |
|         return nvt_sdio_rwu(nvt_wdev_sdio, false, func_num,
 | |
|                 addr, (void *)ret_data, 4);
 | |
| 
 | |
| }
 | |
| 
 | |
| static s32 nvt_sdio_writeu8(struct _nvt_wdev_sdio *nvt_wdev_sdio, u8 func_num,
 | |
|         u32 addr, u8 w_data)
 | |
| {
 | |
|          return nvt_sdio_rwu(nvt_wdev_sdio, true, func_num,
 | |
|                 addr, (void *)&w_data, 1);
 | |
| }
 | |
| 
 | |
| static s32 nvt_sdio_writeu32(struct _nvt_wdev_sdio *nvt_wdev_sdio, u8 func_num,
 | |
|         u32 addr, u32 w_data)
 | |
| {
 | |
|         return nvt_sdio_rwu(nvt_wdev_sdio, true, func_num,
 | |
|                 addr, (void *)&w_data, 4);
 | |
| 
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * nvt_sdio_dataread() - read data from SDIO interface
 | |
|  * @nvt_wdev_sdio: NVT SDIO device structure
 | |
|  * @func_num: SDIO function number
 | |
|  * @addr: address for reading
 | |
|  * @ret_data: data buffer for storing read data
 | |
|  * @rec_len: reading length
 | |
|  * @read_type: NVT_SDIO_FIFO_DATA or NVT_SDIO_CHUNK_DATA
 | |
|  *
 | |
|  * Return: 0:success, negative number:fail
 | |
|  */
 | |
| static s32 nvt_sdio_dataread(struct _nvt_wdev_sdio *nvt_wdev_sdio, u8 func_num,
 | |
|         u32 addr, u8 *ret_data, u32 rec_len, s32 read_type)
 | |
| {
 | |
|         s32 ret;
 | |
| 
 | |
|         if (nvt_wdev_sdio == NULL) {
 | |
|                 nvt_dbg(ERROR, "%s: nvt_wdev_sdio is NULL\n", __func__);
 | |
|                 return -EFAULT;
 | |
|         }
 | |
| 
 | |
|         if (func_num != NVT_SDIO_FUNC0 && func_num != NVT_SDIO_FUNC1) {
 | |
|                 nvt_dbg(ERROR, "%s: error sdio func\n", __func__);
 | |
|                 return -ENODEV;
 | |
|         }
 | |
| 
 | |
|         ret = nvt_sdio_pm_wait_resume(nvt_wdev_sdio,
 | |
|                                 &nvt_wdev_sdio->req_byte_wait);
 | |
|         if (ret) {
 | |
|                 nvt_dbg(ERROR, "%s: waiting resume..\n", __func__);
 | |
|                 return -EPERM;
 | |
|         }
 | |
| 
 | |
|         if (read_type == NVT_SDIO_FIFO_DATA) {
 | |
|                 return nvt_retry_sdio_readsb(nvt_wdev_sdio->func[func_num],
 | |
|                         (u8 *)ret_data, addr, rec_len);
 | |
| 
 | |
|         } else if (read_type == NVT_SDIO_CHUNK_DATA) {
 | |
|                 //20161020 nash: nvt sdio retry fn
 | |
|                 //               (work around for hw power-saving)
 | |
|                 return nvt_retry_sdio_memcpy_fromio(
 | |
|                         nvt_wdev_sdio->func[func_num],
 | |
|                         (u8 *)ret_data, addr, rec_len);
 | |
|         } else {
 | |
|                 return -EPERM;
 | |
|         }
 | |
| 
 | |
| }
 | |
| 
 | |
| static s32 nvt_sdio_datawrite(struct _nvt_wdev_sdio *nvt_wdev_sdio, u8 func_num,
 | |
|         u32 addr, u8 *data, u32 data_len, s32 write_type)
 | |
| {
 | |
|         s32 ret;
 | |
| 
 | |
|         if (nvt_wdev_sdio == NULL) {
 | |
|                 nvt_dbg(ERROR, "%s: nvt_wdev_sdio is NULL\n", __func__);
 | |
|                 return -EFAULT;
 | |
|         }
 | |
| 
 | |
|         if (func_num != NVT_SDIO_FUNC0 && func_num != NVT_SDIO_FUNC1) {
 | |
|                 nvt_dbg(ERROR, "%s: error sdio func\n", __func__);
 | |
|                 return -ENODEV;
 | |
|         }
 | |
| 
 | |
|         ret = nvt_sdio_pm_wait_resume(nvt_wdev_sdio,
 | |
|                                 &nvt_wdev_sdio->req_byte_wait);
 | |
|         if (ret) {
 | |
|                 nvt_dbg(ERROR, "%s: waiting resume..\n", __func__);
 | |
|                 return -EPERM;
 | |
|         }
 | |
|         if (write_type == NVT_SDIO_FIFO_DATA) {
 | |
|                 //20161020 nash: nvt sdio retry fn
 | |
|                 //         (work around for hw power-saving)
 | |
|                 return nvt_retry_sdio_writesb(nvt_wdev_sdio->func[func_num],
 | |
|                         addr, (void *)data, data_len);
 | |
|         } else if (write_type == NVT_SDIO_CHUNK_DATA) {
 | |
|                 //20161020 nash: nvt sdio retry fn
 | |
|                 //               (work around for hw power-saving)
 | |
|                 return nvt_retry_sdio_memcpy_toio(nvt_wdev_sdio->func[func_num],
 | |
|                         addr, (void *)data, data_len);
 | |
|         } else {
 | |
|                 return -EPERM;
 | |
|         }
 | |
| 
 | |
| }
 | |
| 
 | |
| s32 nvt_sdio_abort(struct _nvt_wdev_sdio *nvt_wdev_sdio, u8 fn)
 | |
| {
 | |
| 
 | |
|         //20160119 nash: using f0 api
 | |
|         s32 ret = 0;
 | |
|         /* issue abort cmd52 command through F0 */
 | |
|         //nvt_sdio_writeu8(nvt_wdev_sdio, NVT_SDIO_FUNC0, SDIO_CCCR_SELx, fn);
 | |
|         //20161020 nash: nvt sdio retry fn(work around for hw power-saving)
 | |
|         nvt_retry_sdio_f0_writeb(nvt_wdev_sdio->func[0], fn,
 | |
|                 SDIO_CCCR_SELx, &ret);
 | |
|         return ret;
 | |
| }
 | |
| 
 | |
| static s32 nvt_sdio_bus_release(struct _nvt_wdev_sdio *nvt_wdev_sdio, u8 fn)
 | |
| {
 | |
|         unsigned long timeout;
 | |
|         s32 ret = 0;
 | |
|         /* step1. indicate fun number */
 | |
|         //nvt_sdio_writeu8(nvt_wdev_sdio, NVT_SDIO_FUNC0, SDIO_CCCR_SELx, fn);
 | |
|         //20160119 nash: using f0 api
 | |
|         //20161020 nash: nvt sdio retry fn(work around for hw power-saving)
 | |
|         nvt_retry_sdio_f0_writeb(nvt_wdev_sdio->func[0], fn,
 | |
|                 SDIO_CCCR_SELx, &ret);
 | |
|         if (ret) {
 | |
|                 nvt_dbg(ERROR, "%s: write SDIO_CCCR_SELx fail(%d)!!\n",
 | |
|                                 __func__,
 | |
|                                 ret);
 | |
|                 return ret;
 | |
|         }
 | |
|         /* step2. release the bus indicated in step1*/
 | |
|         fn = (1 << 1);
 | |
|         //nvt_sdio_writeu8(nvt_wdev_sdio, NVT_SDIO_FUNC0, SDIO_CCCR_SUSPEND,
 | |
|         //        fn);
 | |
|         //20160119 nash: using f0 api
 | |
|         //20161020 nash: nvt sdio retry fn(work around for hw power-saving)
 | |
|         nvt_retry_sdio_f0_writeb(nvt_wdev_sdio->func[0], fn,
 | |
|                 SDIO_CCCR_SUSPEND, &ret);
 | |
|         if (ret) {
 | |
|                 nvt_dbg(ERROR, "%s: write SDIO_CCCR_SUSPEND fail(%d)!!\n",
 | |
|                                 __func__,
 | |
|                                 ret);
 | |
|                 return ret;
 | |
|         }
 | |
| 
 | |
|         timeout = jiffies + 2*HZ;
 | |
|         while (time_before(jiffies, timeout)) {
 | |
|                 //20160119 nash: using f0 api
 | |
|                 //nvt_sdio_readu8(nvt_wdev_sdio, NVT_SDIO_FUNC0,
 | |
|                 //        SDIO_CCCR_SUSPEND, &fn);
 | |
|                 //20161020 nash: nvt sdio retry fn
 | |
|                 //               (work around for hw power-saving)
 | |
|                 fn = nvt_retry_sdio_f0_readb(nvt_wdev_sdio->func[0],
 | |
|                         SDIO_CCCR_SUSPEND, &ret);
 | |
|                 if (ret) {
 | |
|                         nvt_dbg(ERROR,
 | |
|                                 "%s: read SDIO_CCCR_SUSPEND fail(%d)!!\n",
 | |
|                                 __func__,
 | |
|                                 ret);
 | |
|                         return ret;
 | |
|                 }
 | |
|                 if (!(fn & (1 << 1))) {
 | |
|                         break;
 | |
|                 }
 | |
|         }
 | |
| 
 | |
|         if (fn & (1 << 1)) {
 | |
|                 nvt_dbg(ERROR, "%s: the BR request isn't be satisified\n",
 | |
|                         __func__);
 | |
|         }
 | |
| 
 | |
|         return 0;
 | |
| }
 | |
| 
 | |
| static s32 nvt_sdio_send_tx_retry(struct _nvt_wdev_sdio *nvt_wdev_sdio)
 | |
| {
 | |
|         s32 ret;
 | |
|         s32 retry_cnt = 3;
 | |
|         //unsigned long timeout;
 | |
| 
 | |
|         //timeout = jiffies + 10*HZ;
 | |
|         do {
 | |
|                 ret = nvt_sdio_writeu32(nvt_wdev_sdio, NVT_SDIO_FUNC1,
 | |
|                         SDIO_HOST2CARD_INT, TX_RETRY);
 | |
| 
 | |
|                 if (ret) {
 | |
|                         nvt_sdio_abort(nvt_wdev_sdio, NVT_SDIO_FUNC1);
 | |
|                         nvt_sdio_bus_release(nvt_wdev_sdio, NVT_SDIO_FUNC1);
 | |
|                 }
 | |
|         } while (--retry_cnt > 0 && ret != 0);
 | |
| 
 | |
|         return 0;
 | |
| }
 | |
| 
 | |
| static s32 nvt_sdio_send_sync_pkt(struct _nvt_wdev_sdio *nvt_wdev_sdio)
 | |
| {
 | |
|         s32 ret;
 | |
| 
 | |
|         if (nvt_wdev_sdio == NULL) {
 | |
|                 nvt_dbg(ERROR, "%s: nvt_wdev_sdio is NULL\n", __func__);
 | |
|                 return -1;
 | |
|         }
 | |
| 
 | |
|         ret = nvt_sdio_writeu32(nvt_wdev_sdio, NVT_SDIO_FUNC1,
 | |
|                 SDIO_HOST2CARD_INT, DIAG_REQ);
 | |
|         if (ret) {
 | |
|                 nvt_dbg(ERROR, "%s: write u32 fail\n", __func__);
 | |
|         }
 | |
| 
 | |
|         return ret;
 | |
| }
 | |
| 
 | |
| static s32 nvt_sdio_read_int_status(struct _nvt_wdev_sdio *nvt_wdev_sdio)
 | |
| {
 | |
|         s32 ret;
 | |
|         u32 ret_data = 0;
 | |
|         u32 n;
 | |
| 
 | |
|         ret = nvt_sdio_readu32(nvt_wdev_sdio, NVT_SDIO_FUNC1,
 | |
|                 SDIO_CARD2HOST_INT, &ret_data);
 | |
|         if (ret) {
 | |
|                 nvt_dbg(ERROR, "%s: read int status fail\n", __func__);
 | |
|                 atomic_set(&nvt_wdev_sdio->intstatus, 0);
 | |
|         } else if (ret_data) {
 | |
|                 //20160706 nash: to prevent deadlock when trying to send
 | |
|                 //diagnostic message after receiving a event
 | |
|                 if (ret_data & DIAG_COMP) {
 | |
|                         nvt_wdev_sdio->ctrl_msg_resp_rdy = 1;
 | |
|                         if (waitqueue_active
 | |
|                                 (&nvt_wdev_sdio->ctrl_msg_resp_wait)) {
 | |
|                                 wake_up(&nvt_wdev_sdio->ctrl_msg_resp_wait);
 | |
|                         }
 | |
|                         ret_data &= ~DIAG_COMP;
 | |
|                 }
 | |
| 
 | |
|                 if (ret_data & PKT_VLD) {
 | |
|                         /*Record packet length for further use */
 | |
|                         nvt_wdev_sdio->pkt_len = (ret_data >> 16);
 | |
|                         nvt_wdev_sdio->rx_int_cnt++;
 | |
|                 }
 | |
| 
 | |
|                 ret_data &= 0xFFFF;
 | |
| 
 | |
|                 //nash:work around for twice ISR call for one interrupt
 | |
|                 if (ret_data == 0) {
 | |
|                         return -1;
 | |
|                 }
 | |
| 
 | |
|                 for_each_set_bit(n, (unsigned long *)&ret_data, 32) {
 | |
|                         set_bit(n,
 | |
|                         (unsigned long *)&nvt_wdev_sdio->intstatus.counter);
 | |
|                 }
 | |
|         }
 | |
| 
 | |
|         return ret;
 | |
| 
 | |
| }
 | |
| 
 | |
| static void nvt_sdio_irq_handler(struct sdio_func *func)
 | |
| {
 | |
|         struct _nvt_bus *nvt_bus;
 | |
|         struct _nvt_wdev_sdio *nvt_wdev_sdio;
 | |
|         s32 ret;
 | |
| 
 | |
|         nvt_bus = dev_get_drvdata(&func->dev);
 | |
|         //20151218 nash: coverity#48945
 | |
|         if (nvt_bus == NULL) {
 | |
|                 return;
 | |
|         }
 | |
|         if (nvt_bus->state == NVT_BUS_STATE_DOWN) {
 | |
|                 return;
 | |
|         }
 | |
| 
 | |
|         nvt_wdev_sdio = nvt_bus->type.nvt_wdev_sdio;
 | |
| 
 | |
|         if (in_interrupt()) {
 | |
|                 atomic_set(&nvt_wdev_sdio->int_pending, 1);
 | |
|         } else {
 | |
|                 ret = nvt_sdio_read_int_status(nvt_wdev_sdio);
 | |
|                 if (ret) {
 | |
|                         return;
 | |
|                 }
 | |
|         }
 | |
| 
 | |
|         atomic_inc(&nvt_wdev_sdio->datahdl_tskcnt);
 | |
|         schedule_work(&nvt_wdev_sdio->datahdl_work);
 | |
| 
 | |
|         return;
 | |
| }
 | |
| 
 | |
| static s32 nvt_sdio_register_isr(struct _nvt_wdev_sdio *nvt_wdev_sdio)
 | |
| {
 | |
|         s32 ret;
 | |
|         s32 retry = 0;
 | |
| 
 | |
|         sdio_claim_host(nvt_wdev_sdio->func[1]);
 | |
|         //20170123 nash: retry (a work around for power-saving/wake up)
 | |
|         do {
 | |
|                 ret = sdio_claim_irq(nvt_wdev_sdio->func[1],
 | |
|                         &nvt_sdio_irq_handler);
 | |
|         } while (retry++ < NVT_SDIO_ACCESS_RETRY_LIMIT && ret != 0);
 | |
| 
 | |
|         sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
| 
 | |
|         return ret;
 | |
| }
 | |
| 
 | |
| static s32 nvt_sdio_unregister_isr(struct _nvt_wdev_sdio *nvt_wdev_sdio)
 | |
| {
 | |
|         s32 ret;
 | |
|         s32 retry = 0;
 | |
| 
 | |
|         sdio_claim_host(nvt_wdev_sdio->func[1]);
 | |
|         //20170123 nash: retry (a work around for power-saving/wake up)
 | |
|         do {
 | |
|         ret = sdio_release_irq(nvt_wdev_sdio->func[1]);
 | |
|         } while (retry++ < NVT_SDIO_ACCESS_RETRY_LIMIT && ret != 0);
 | |
| 
 | |
|         sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
| 
 | |
|         return ret;
 | |
| }
 | |
| 
 | |
| static s32 nvt_sdio_data_handler_rx(struct _nvt_wdev_sdio *nvt_wdev_sdio)
 | |
| {
 | |
|         s32 ret;
 | |
|         u32 pad_len;
 | |
|         struct sk_buff *pkt_skb;
 | |
|         struct _nvt_bus *nvt_bus;
 | |
|         s32 max_rxburst = NVT_SDIO_RX_BRUST_NUM;
 | |
|         u32 ret_data = 0;
 | |
| 
 | |
|         nvt_bus = dev_get_drvdata(nvt_wdev_sdio->dev);
 | |
|         if (nvt_bus == NULL) {
 | |
|                 return -1;
 | |
|         }
 | |
| 
 | |
| #ifdef BUS_UTILIZATION
 | |
|         if (nvt_bus->nvt_bu->rx_enable && nvt_bus->nvt_bu->start_time == 0) {
 | |
|                 nvt_bus->nvt_bu->start_time = get_jiffies_64();
 | |
|         }
 | |
| #endif
 | |
|         ret = nvt_sdio_unregister_isr(nvt_wdev_sdio);
 | |
|         if (ret) {
 | |
|                 nvt_dbg(ERROR, "%s: unregister ISR fail\n", __func__);
 | |
|                 return ret;
 | |
|         }
 | |
| 
 | |
|         while (1) {
 | |
|                 /* Align read packet length with block size */
 | |
|                 pad_len = roundup(nvt_wdev_sdio->pkt_len,
 | |
|                         nvt_wdev_sdio->blocksize) - nvt_wdev_sdio->pkt_len;
 | |
| 
 | |
|                 pkt_skb = dev_alloc_skb(nvt_wdev_sdio->pkt_len + pad_len);
 | |
|                 if (!pkt_skb) {
 | |
|                         nvt_dbg(ERROR, "%s: dev_alloc_skb() fail\n", __func__);
 | |
|                         //nash:TODO
 | |
|                         sdio_claim_host(nvt_wdev_sdio->func[1]);
 | |
|                         nvt_sdio_abort(nvt_wdev_sdio, NVT_SDIO_FUNC1);
 | |
|                         nvt_sdio_bus_release(nvt_wdev_sdio, NVT_SDIO_FUNC1);
 | |
|                         sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
|                         goto fail;
 | |
|                 }
 | |
| 
 | |
|                 sdio_claim_host(nvt_wdev_sdio->func[1]);
 | |
|                 ret = nvt_sdio_dataread(nvt_wdev_sdio,
 | |
|                         NVT_SDIO_FUNC1, SDIO_RX_PORT,
 | |
|                         (u8 *)pkt_skb->data,
 | |
|                         nvt_wdev_sdio->pkt_len + pad_len,
 | |
|                         NVT_SDIO_FIFO_DATA);
 | |
|                 sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
|                 if (ret) {
 | |
|                         nvt_dbg(ERROR, "%s: read packet fail(%d)\n", __func__,
 | |
|                                 ret);
 | |
|                         dev_kfree_skb_any(pkt_skb);
 | |
|                         sdio_claim_host(nvt_wdev_sdio->func[1]);
 | |
|                         nvt_sdio_abort(nvt_wdev_sdio, NVT_SDIO_FUNC1);
 | |
|                         nvt_sdio_bus_release(nvt_wdev_sdio, NVT_SDIO_FUNC1);
 | |
|                         sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
| 
 | |
|                         nvt_bus->statistic.pkt_rx_err_cnt++;
 | |
| 
 | |
|                         goto fail;
 | |
|                 }
 | |
| 
 | |
|                 skb_put(pkt_skb, (nvt_wdev_sdio->pkt_len + pad_len));
 | |
|                 skb_trim(pkt_skb, nvt_wdev_sdio->pkt_len);
 | |
| 
 | |
| #if (NVT_SDIO_CFG_AUTO_PKT_DONE == 0)
 | |
|                 /* Tell the device that read is done */
 | |
|                 sdio_claim_host(nvt_wdev_sdio->func[1]);
 | |
|                 ret = nvt_sdio_writeu32(nvt_wdev_sdio, NVT_SDIO_FUNC1,
 | |
|                         SDIO_HOST2CARD_INT, RX_PKT_DONE);
 | |
|                 sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
|                 if (ret) {
 | |
|                         nvt_dbg(ERROR, "%s: send packet done fail\n", __func__);
 | |
|                         dev_kfree_skb_any(pkt_skb);
 | |
|                         //nash:TODO
 | |
|                         sdio_claim_host(nvt_wdev_sdio->func[1]);
 | |
|                         nvt_sdio_abort(nvt_wdev_sdio, NVT_SDIO_FUNC1);
 | |
|                         nvt_sdio_bus_release(nvt_wdev_sdio, NVT_SDIO_FUNC1);
 | |
|                         sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
| 
 | |
|                         //20170120 nash: should enable ISR again
 | |
|                         nvt_sdio_register_isr(nvt_wdev_sdio);
 | |
| 
 | |
|                         nvt_bus->statistic.pkt_rx_err_cnt++;
 | |
| 
 | |
|                         return ret;
 | |
|                 }
 | |
| #endif
 | |
| 
 | |
| #ifdef BUS_UTILIZATION
 | |
|                 if (nvt_bus->nvt_bu->rx_enable) {
 | |
|                         nvt_bus->nvt_bu->total_pkt_size_in_byte +=
 | |
|                                 pkt_skb->len;
 | |
| 
 | |
|                         if (++nvt_bus->nvt_bu->rx_runtime_cnt >=
 | |
|                                 nvt_bus->nvt_bu->rx_total_cnt) {
 | |
| 
 | |
|                                 nvt_bus_bu_show_result(nvt_bus, false);
 | |
|                         }
 | |
|                 }
 | |
|                 dev_kfree_skb_any(pkt_skb);
 | |
| #else
 | |
|                 /* Send to upper layer */
 | |
|                 skb_queue_tail(&nvt_wdev_sdio->rx_skb_list, pkt_skb);
 | |
| #endif
 | |
| 
 | |
|                 nvt_bus->statistic.pkt_rx_cnt++;
 | |
| 
 | |
|                 if (--max_rxburst <= 0) {
 | |
|                         break;
 | |
|                 }
 | |
|                 ret_data = 0;
 | |
|                 sdio_claim_host(nvt_wdev_sdio->func[1]);
 | |
|                 ret = nvt_sdio_readu32(nvt_wdev_sdio, NVT_SDIO_FUNC1,
 | |
|                         SDIO_CARD2HOST_INT, &ret_data);
 | |
|                 sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
| 
 | |
|                 if (ret_data & PKT_VLD) {
 | |
|                         nvt_wdev_sdio->pkt_len = (ret_data >> 16);
 | |
|                 } else {
 | |
|                         break;
 | |
|                 }
 | |
| 
 | |
|                 nvt_bus->statistic.pkt_rx_cnt++;
 | |
| 
 | |
|         }
 | |
| 
 | |
|         nvt_sdio_register_isr(nvt_wdev_sdio);
 | |
|         if (ret) {
 | |
|                 nvt_dbg(ERROR, "%s: register ISR fail\n", __func__);
 | |
|         }
 | |
| 
 | |
|         nvt_process_rx_list(nvt_bus, &nvt_wdev_sdio->rx_skb_list);
 | |
| 
 | |
|         return ret;
 | |
| 
 | |
| fail:
 | |
| 
 | |
|         //20170120 nash: should enable ISR again
 | |
|         nvt_sdio_register_isr(nvt_wdev_sdio);
 | |
| 
 | |
| //20160222 nash:
 | |
| #if (NVT_SDIO_CFG_AUTO_PKT_DONE == 0)
 | |
|         nvt_dbg(ERROR, "%s: fail and tell rx done\n", __func__);
 | |
| 
 | |
|         /* Tell the device that read is done */
 | |
|         sdio_claim_host(nvt_wdev_sdio->func[1]);
 | |
|         ret = nvt_sdio_writeu32(nvt_wdev_sdio, NVT_SDIO_FUNC1,
 | |
|                 SDIO_HOST2CARD_INT, RX_PKT_DONE);
 | |
|         sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
| #endif
 | |
| 
 | |
|         return ret;
 | |
| 
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * nvt_sdio_data_handler_tx - TX data handler
 | |
|  * @nvt_wdev_sdio: _nvt_wdev_sdio structure
 | |
|  *
 | |
|  * return: 1:tx data pending, 0:no tx data pending, < 0: error
 | |
|  */
 | |
| static s32 nvt_sdio_data_handler_tx(struct _nvt_wdev_sdio *nvt_wdev_sdio)
 | |
| {
 | |
|         struct _nvt_bus *nvt_bus;
 | |
| 
 | |
|         //nash: adjust later
 | |
|         s32 max_frame_tx_cnt = 1;
 | |
|         s32 i;
 | |
|         s32 ret = 0;
 | |
|         u32 pad_len;
 | |
|         u32 pkt_len = 0;
 | |
|         s32 retry_cnt;
 | |
|         u32 q_len;
 | |
|         struct sk_buff *skb;
 | |
| 
 | |
| #if (NVT_SDIO_GET_AVAI_SPACE_BEFORE_TX == 1)
 | |
|         u8 txbd_ptr[8];
 | |
| #endif
 | |
|         nvt_bus = dev_get_drvdata(nvt_wdev_sdio->dev);
 | |
|         //20151218 nash: coverity#48944
 | |
|         if (nvt_bus == NULL) {
 | |
|                 return -1;
 | |
|         }
 | |
| 
 | |
|         q_len = skb_queue_len(&nvt_wdev_sdio->tx_skb_list);
 | |
|         if (q_len == 0) {
 | |
|                 return 0;
 | |
|         }
 | |
| 
 | |
|         for (i = 0; i < max_frame_tx_cnt; i++) {
 | |
| 
 | |
|                 //20160304 nash:
 | |
|                 #if (NVT_SDIO_GET_AVAI_SPACE_BEFORE_TX == 1)
 | |
|                 retry_cnt = 1000;
 | |
|                 do {
 | |
|                         sdio_claim_host(nvt_wdev_sdio->func[1]);
 | |
|                         ret = nvt_sdio_dataread(nvt_wdev_sdio, NVT_SDIO_FUNC1,
 | |
|                                 SDIO_TX_WPTR, txbd_ptr, 8,
 | |
|                                 NVT_SDIO_CHUNK_DATA);
 | |
|                         sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
|                         if (ret) {
 | |
|                                 nvt_dbg(ERROR, "%s: read TX BD FAIL(%d)\n",
 | |
|                                         __func__, ret);
 | |
| 
 | |
|                                 sdio_claim_host(nvt_wdev_sdio->func[1]);
 | |
|                                 nvt_sdio_abort(nvt_wdev_sdio, NVT_SDIO_FUNC1);
 | |
|                                 nvt_sdio_bus_release(nvt_wdev_sdio,
 | |
|                                         NVT_SDIO_FUNC1);
 | |
|                                 sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
|                                 return -1;
 | |
|                         }
 | |
|                         if (txbd_ptr[0] != txbd_ptr[4]) {
 | |
|                                 break;
 | |
|                         } else {
 | |
|                                 mdelay(1);
 | |
|                         }
 | |
| 
 | |
|                 } while (--retry_cnt > 0);
 | |
| 
 | |
|                 if (retry_cnt == 0) {
 | |
|                         nvt_dbg(ERROR,
 | |
|                         "%s: Waiting TX BD table is available timeout\n",
 | |
|                         __func__);
 | |
|                         return -1;
 | |
|                 }
 | |
|                 #endif
 | |
|                 skb = skb_dequeue(&nvt_wdev_sdio->tx_skb_list);
 | |
|                 if (skb == NULL) {
 | |
|                         break;
 | |
|                 }
 | |
| 
 | |
|                 /* Align read packet length with block size */
 | |
|                 pad_len = roundup(skb->len,
 | |
|                         nvt_wdev_sdio->blocksize) - skb->len;
 | |
|                 //nash:TODO
 | |
|         #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
 | |
|         if (skb_availroom(skb) > pad_len) {
 | |
|         #else
 | |
|         if (skb_tailroom(skb) > pad_len) {
 | |
|         #endif
 | |
|                         skb_put(skb, pad_len);
 | |
|                         pkt_len = skb->len;
 | |
|                 } else {
 | |
|                         pkt_len = skb->len + pad_len;
 | |
|                 }
 | |
| 
 | |
|                 retry_cnt = 3;
 | |
|                 sdio_claim_host(nvt_wdev_sdio->func[1]);
 | |
|                 do {
 | |
|                         ret = nvt_sdio_datawrite(nvt_wdev_sdio, NVT_SDIO_FUNC1,
 | |
|                                 SDIO_TX_PORT, (u8 *)skb->data, pkt_len,
 | |
|                                 NVT_SDIO_FIFO_DATA);
 | |
| 
 | |
|                         if (ret) {
 | |
|                                 nvt_dbg(ERROR, "%s: data write fail(%d)\n",
 | |
|                                         __func__, ret);
 | |
| 
 | |
|                                 nvt_sdio_abort(nvt_wdev_sdio, NVT_SDIO_FUNC1);
 | |
|                                 nvt_sdio_bus_release(nvt_wdev_sdio,
 | |
|                                         NVT_SDIO_FUNC1);
 | |
| 
 | |
|                                 nvt_sdio_send_tx_retry(nvt_wdev_sdio);
 | |
| //20160222 nash:
 | |
| #if (NVT_SDIO_CFG_AUTO_PKT_DONE == 1)
 | |
|                         }
 | |
| #else
 | |
|                         } else {
 | |
|                                 ret = nvt_sdio_writeu32(nvt_wdev_sdio,
 | |
|                                                 NVT_SDIO_FUNC1,
 | |
|                                                 SDIO_HOST2CARD_INT,
 | |
|                                                 TX_PKT_DONE);
 | |
|                                 if (ret) {
 | |
|                                         nvt_dbg(ERROR,
 | |
|                                         "%s: send TX packet done fail\n",
 | |
|                                         __func__);
 | |
|                                 }
 | |
|                         }
 | |
| #endif
 | |
|                         retry_cnt--;
 | |
|                 } while (ret != 0 && retry_cnt >= 0);
 | |
|                 sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
| 
 | |
|                 if (ret != 0) {
 | |
|                         nvt_dbg(ERROR, "%s: tx packet fail\n", __func__);
 | |
|                         nvt_bus->statistic.pkt_tx_err_cnt++;
 | |
|                         return ret;
 | |
|                 }
 | |
| 
 | |
| #ifdef BUS_UTILIZATION
 | |
|                 if (nvt_bus->nvt_bu->tx_enable) {
 | |
|                         nvt_bus->nvt_bu->total_pkt_size_in_byte += skb->len;
 | |
|                 }
 | |
| #endif
 | |
|                 nvt_bus->tx_done_callback(skb, 0);
 | |
|                 nvt_bus->statistic.pkt_tx_cnt++;
 | |
| 
 | |
|         }
 | |
| 
 | |
|         //nash:TODO:tx flow control
 | |
| 
 | |
| 
 | |
| #ifdef BUS_UTILIZATION
 | |
| 
 | |
|         if (nvt_bus->nvt_bu->tx_enable) {
 | |
| 
 | |
|                 if (++nvt_bus->nvt_bu->tx_runtime_cnt >=
 | |
|                         nvt_bus->nvt_bu->tx_total_cnt) {
 | |
| 
 | |
|                         nvt_bus_bu_show_result(nvt_bus, true);
 | |
|                 }
 | |
|         }
 | |
| #endif
 | |
| 
 | |
|         if (q_len > max_frame_tx_cnt) {
 | |
|                 return 1;
 | |
|         }
 | |
| 
 | |
|         return ret;
 | |
| }
 | |
| 
 | |
| static void nvt_sdio_data_handler(struct work_struct *work)
 | |
| {
 | |
|         struct _nvt_wdev_sdio *nvt_wdev_sdio;
 | |
|         s32 ret;
 | |
|         u32 int_status;
 | |
|         s32 n;
 | |
|         u8 rxbd_ptr[8] = {0};
 | |
| 
 | |
|         nvt_wdev_sdio = container_of(work, struct _nvt_wdev_sdio, datahdl_work);
 | |
| 
 | |
|       do {
 | |
|         if (!atomic_read(&nvt_wdev_sdio->datahdl_tskcnt)) {
 | |
|             nvt_dbg(ERROR, "%s: weird, Event triggered, but, tskcnt=0\n",
 | |
|                     __func__);
 | |
|         } else {
 | |
|             atomic_dec(&nvt_wdev_sdio->datahdl_tskcnt);
 | |
|         }
 | |
|                 ret = 0;
 | |
|                 if (atomic_read(&nvt_wdev_sdio->int_pending)) {
 | |
|                         atomic_set(&nvt_wdev_sdio->int_pending, 0);
 | |
|                         sdio_claim_host(nvt_wdev_sdio->func[1]);
 | |
|                         nvt_sdio_read_int_status(nvt_wdev_sdio);
 | |
|                         sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
|                 }
 | |
| 
 | |
|                 int_status = atomic_xchg(&nvt_wdev_sdio->intstatus, 0);
 | |
| 
 | |
|                 if (int_status & DIAG_COMP) {
 | |
|                         nvt_wdev_sdio->ctrl_msg_resp_rdy = 1;
 | |
|                         //wake_up_interruptible(
 | |
|                         //        &nvt_wdev_sdio->ctrl_msg_resp_wait);
 | |
|                         if (waitqueue_active
 | |
|                                 (&nvt_wdev_sdio->ctrl_msg_resp_wait)) {
 | |
|                                 wake_up(&nvt_wdev_sdio->ctrl_msg_resp_wait);
 | |
|                         }
 | |
|                         int_status &= ~DIAG_COMP;
 | |
|                 }
 | |
| 
 | |
|                 if (int_status & (PKT_VLD | RX_PKT_WORK_ARD)) {
 | |
|                         if ((int_status & (PKT_VLD)) == 0) {
 | |
|                                 sdio_claim_host(nvt_wdev_sdio->func[1]);
 | |
|                                 ret = nvt_sdio_dataread(nvt_wdev_sdio,
 | |
|                                     NVT_SDIO_FUNC1, SDIO_RX_WPTR, rxbd_ptr, 8,
 | |
|                                     NVT_SDIO_CHUNK_DATA);
 | |
|                                 sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
|                                 if (ret) {
 | |
|                                         nvt_dbg(ERROR,
 | |
|                                                 "%s: read RX BD FAIL(%d)\n",
 | |
|                                                 __func__, ret);
 | |
| 
 | |
|                                         sdio_claim_host(nvt_wdev_sdio->func[1]);
 | |
|                                         nvt_sdio_abort(nvt_wdev_sdio,
 | |
|                                                 NVT_SDIO_FUNC1);
 | |
|                                         nvt_sdio_bus_release(nvt_wdev_sdio,
 | |
|                                                 NVT_SDIO_FUNC1);
 | |
|                                         sdio_release_host(
 | |
|                                                 nvt_wdev_sdio->func[1]);
 | |
| 
 | |
|                                 //Read RX data only if RX write ptr != read ptr
 | |
|                                 } else if (rxbd_ptr[0] != rxbd_ptr[4]) {
 | |
|                                         nvt_dbg(ERROR,
 | |
|                                             "%s: read RX by work ard\n",
 | |
|                                             __func__);
 | |
|                                         nvt_sdio_data_handler_rx(nvt_wdev_sdio);
 | |
|                                 } else {
 | |
|                                         nvt_dbg(ERROR,
 | |
|                                             "%s: RX_WORK_ARD, but no data!!\n",
 | |
|                                             __func__);
 | |
|                                 }
 | |
| 
 | |
|                         } else {
 | |
|                         nvt_sdio_data_handler_rx(nvt_wdev_sdio);
 | |
|                         }
 | |
|                         int_status &= ~(PKT_VLD | RX_PKT_WORK_ARD);
 | |
|                 }
 | |
| 
 | |
| //20170717 nash:
 | |
| #if 0
 | |
|                 if (int_status & RX_PKT_WORK_ARD) {
 | |
|                         nvt_dbg(INFO, "%s: RX_PKT_WORK_ARD\n",
 | |
|                                 __func__);
 | |
| //20160222 nash: TODO
 | |
| #if (NVT_SDIO_CFG_AUTO_PKT_DONE == 0)
 | |
|                         /* work around */
 | |
|                         sdio_claim_host(nvt_wdev_sdio->func[1]);
 | |
|                         nvt_sdio_writeu32(nvt_wdev_sdio,
 | |
|                                 NVT_SDIO_FUNC1,
 | |
|                                 SDIO_HOST2CARD_INT, RX_PKT_DONE);
 | |
|                         sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
| #endif
 | |
|                         int_status &= ~RX_PKT_WORK_ARD;
 | |
|                 }
 | |
| #endif
 | |
| 
 | |
|                 if (int_status & PKT_TX_SIZE_ERR) {
 | |
|                         nvt_dbg(INFO, "%s: PKT_TX_SIZE_ERR\n",
 | |
|                                 __func__);
 | |
|                 }
 | |
| 
 | |
|                 if (int_status) {
 | |
|                         for_each_set_bit(n, (unsigned long *)&int_status, 32) {
 | |
|                                 set_bit(n,
 | |
|                                 (unsigned long *)
 | |
|                                 &nvt_wdev_sdio->intstatus.counter);
 | |
|                         }
 | |
|                 }
 | |
| 
 | |
|                 ret = nvt_sdio_data_handler_tx(nvt_wdev_sdio);
 | |
|                 if (ret < 0) {
 | |
|                         nvt_dbg(ERROR, "%s: data tx fail\n", __func__);
 | |
|                 }
 | |
| 
 | |
|                 //nash:CHECK
 | |
|                 //if (atomic_read(&nvt_wdev_sdio->intstatus)&0xFF || ret == 1) {
 | |
|                 if (ret == 1) {
 | |
|                         atomic_inc(&nvt_wdev_sdio->datahdl_tskcnt);
 | |
|                 }
 | |
| 
 | |
|         } while (atomic_read(&nvt_wdev_sdio->datahdl_tskcnt) > 0);
 | |
| }
 | |
| 
 | |
| static s32 nvt_sdio_bus_init(struct _nvt_bus *bus)
 | |
| {
 | |
|         s32 ret;
 | |
| 
 | |
|         ret = nvt_sdio_register_isr(bus->type.nvt_wdev_sdio);
 | |
| 
 | |
|         bus->state = NVT_BUS_STATE_UP;
 | |
| 
 | |
|         /* set default tx_done callback function */
 | |
|         ret |= nvt_bus_register_txdone_callback(bus, nvt_sdio_bus_tx_done_cb);
 | |
| 
 | |
|         return ret;
 | |
| }
 | |
| 
 | |
| static s32 nvt_sdio_bus_down(struct _nvt_bus *bus)
 | |
| {
 | |
|         struct _nvt_wdev_sdio *nvt_wdev_sdio;
 | |
|         struct sk_buff *cur;
 | |
|         struct sk_buff *next;
 | |
| 
 | |
|         bus->state = NVT_BUS_STATE_DOWN;
 | |
| 
 | |
|         nvt_wdev_sdio = bus->type.nvt_wdev_sdio;
 | |
| 
 | |
|         nvt_sdio_unregister_isr(nvt_wdev_sdio);
 | |
| 
 | |
|         cancel_work_sync(&nvt_wdev_sdio->datahdl_work);
 | |
| 
 | |
|         skb_queue_walk_safe(&nvt_wdev_sdio->tx_skb_list, cur, next) {
 | |
|                 skb_unlink(cur, &nvt_wdev_sdio->tx_skb_list);
 | |
|                 dev_kfree_skb_any(cur);
 | |
|         }
 | |
| 
 | |
|         return 0;
 | |
| }
 | |
| 
 | |
| static s32 nvt_sdio_bus_tx(struct _nvt_bus *nvt_bus, struct sk_buff *skb)
 | |
| {
 | |
|         //struct _nvt_adapter *nvt_adapter;
 | |
|         //struct _tx_info *tx_info;
 | |
|         struct _nvt_wdev_sdio *nvt_wdev_sdio;
 | |
| 
 | |
| 
 | |
|         if (nvt_bus == NULL) {
 | |
|                 nvt_dbg(ERROR, "%s: nvt_bus is NULL\n", __func__);
 | |
|                 return -1;
 | |
|         }
 | |
| 
 | |
|         if (nvt_bus->state != NVT_BUS_STATE_UP) {
 | |
|                 return -1;
 | |
|         }
 | |
| 
 | |
|         nvt_wdev_sdio = nvt_bus->type.nvt_wdev_sdio;
 | |
| 
 | |
|         skb_queue_tail(&nvt_wdev_sdio->tx_skb_list, skb);
 | |
| 
 | |
|         //nash:TODO:flow control
 | |
|         #if 0
 | |
|         nvt_adapter = nvt_bus->nvt_adapter;
 | |
|         tx_info = nvt_adapter->nvt_priv.tx_info;
 | |
|         if (nvt_wdev_usb->tx_blkurb_inuse_count > nvt_wdev_usb->tx_wm_h &&
 | |
|                 !tx_info->bus_flow_blocked) {
 | |
|                 tx_info->bus_flow_blocked = true;
 | |
|         }
 | |
|         #endif
 | |
| 
 | |
|         if (atomic_read(&nvt_wdev_sdio->datahdl_tskcnt) == 0) {
 | |
|                 atomic_inc(&nvt_wdev_sdio->datahdl_tskcnt);
 | |
|                 schedule_work(&nvt_wdev_sdio->datahdl_work);
 | |
|         }
 | |
| 
 | |
|         return 0;
 | |
| 
 | |
| }
 | |
| 
 | |
| static s32 nvt_sdio_bus_ctrl_tx(struct _nvt_bus *bus, u8 *buf, u32 len)
 | |
| {
 | |
|         s32 ret;
 | |
|         s32 retval;
 | |
|         u8 addr_zone_ori;
 | |
|         s32 retry = 0;
 | |
| 
 | |
|         struct _nvt_wdev_sdio *nvt_wdev_sdio = bus->type.nvt_wdev_sdio;
 | |
| 
 | |
| 
 | |
|         if (bus->state != NVT_BUS_STATE_UP) {
 | |
|                 nvt_dbg(ERROR, "%s: bus state is not UP\n", __func__);
 | |
|                 return -EIO;
 | |
|         }
 | |
| 
 | |
|         /*switch to diagnostic address zone */
 | |
|         sdio_claim_host(nvt_wdev_sdio->func[1]);
 | |
|         ret = nvt_sdio_readu8(nvt_wdev_sdio, NVT_SDIO_FUNC1, SDIO_ADDR_ZONE,
 | |
|                 &addr_zone_ori);
 | |
|         if (ret) {
 | |
|                 sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
|                 nvt_dbg(ERROR, "%s: read addr_zone reg fail\n", __func__);
 | |
|                 bus->statistic.ctl_tx_err_cnt++;
 | |
|                 return ret;
 | |
|         }
 | |
| 
 | |
|         ret = nvt_sdio_writeu8(nvt_wdev_sdio, NVT_SDIO_FUNC1, SDIO_ADDR_ZONE,
 | |
|                 (u8)SDIO_ADDR_ZONE_DIAG);
 | |
|         if (ret) {
 | |
|                 sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
|                 nvt_dbg(ERROR, "%s: write addr_zone reg fail\n", __func__);
 | |
|                 bus->statistic.ctl_tx_err_cnt++;
 | |
|                 return ret;
 | |
|         }
 | |
|         sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
| 
 | |
|         do {
 | |
|                 sdio_claim_host(nvt_wdev_sdio->func[1]);
 | |
|                 ret = nvt_sdio_datawrite(nvt_wdev_sdio, NVT_SDIO_FUNC1,
 | |
|                         SDIO_DIAG_DATA_ADDR_OFFSET, buf, len,
 | |
|                         NVT_SDIO_CHUNK_DATA);
 | |
|                 if (ret) {
 | |
|                         nvt_dbg(ERROR, "%s: data write fail\n", __func__);
 | |
|                         sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
|                         continue;
 | |
|                 }
 | |
| 
 | |
|                 //send the packet for sync
 | |
|                 nvt_wdev_sdio->ctrl_msg_resp_rdy = 0;
 | |
|                 ret = nvt_sdio_send_sync_pkt(nvt_wdev_sdio);
 | |
|                 if (ret) {
 | |
|                         nvt_dbg(ERROR, "%s: send sync packet fail\n", __func__);
 | |
|                         sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
|                         continue;
 | |
|                 }
 | |
|                 sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
| 
 | |
|                 ret = nvt_sdio_wait_ctrl_msg_resp(nvt_wdev_sdio);
 | |
|                 if (ret) {
 | |
|                         ret = -ETIME;
 | |
|                         nvt_dbg(ERROR, "%s: wait resp timeout\n", __func__);
 | |
|                         break;
 | |
|                 }
 | |
| 
 | |
|         } while (ret != 0 && --retry > 0);
 | |
| 
 | |
|         retval = ret;
 | |
| 
 | |
|         /*restore original address zone */
 | |
|         sdio_claim_host(nvt_wdev_sdio->func[1]);
 | |
|         ret = nvt_sdio_writeu8(nvt_wdev_sdio, NVT_SDIO_FUNC1, SDIO_ADDR_ZONE,
 | |
|                 addr_zone_ori);
 | |
|         sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
|         if (ret) {
 | |
|                 nvt_dbg(ERROR, "%s: recover addr_zone fail\n", __func__);
 | |
|                 return -1;
 | |
|         }
 | |
| 
 | |
|         if (retval) {
 | |
|                 bus->statistic.ctl_tx_err_cnt++;
 | |
|         } else {
 | |
|                 bus->statistic.ctl_tx_cnt++;
 | |
|         }
 | |
|         return retval;
 | |
| }
 | |
| 
 | |
| static s32 nvt_sdio_bus_ctrl_rx(struct _nvt_bus *bus, u8 *buf, u32 len)
 | |
| {
 | |
|         s32 ret;
 | |
|         s32 retval = 0;
 | |
|         u8 addr_zone_ori;
 | |
|         u8 *nvt_hdr;
 | |
|         u32 read_len;
 | |
| 
 | |
|         struct _nvt_wdev_sdio *nvt_wdev_sdio = bus->type.nvt_wdev_sdio;
 | |
| 
 | |
| 
 | |
|         if (bus->state != NVT_BUS_STATE_UP) {
 | |
|                 nvt_dbg(ERROR, "%s: bus state is not UP\n", __func__);
 | |
|                 bus->statistic.ctl_rx_err_cnt++;
 | |
|                 return -EIO;
 | |
|         }
 | |
| 
 | |
|         /*switch to diagnostic address zone */
 | |
|         sdio_claim_host(nvt_wdev_sdio->func[1]);
 | |
|         ret = nvt_sdio_readu8(nvt_wdev_sdio, NVT_SDIO_FUNC1, SDIO_ADDR_ZONE,
 | |
|                 &addr_zone_ori);
 | |
|         if (ret) {
 | |
|                 sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
|                 nvt_dbg(ERROR, "%s: read addr_zone reg fail\n", __func__);
 | |
|                 bus->statistic.ctl_rx_err_cnt++;
 | |
|                 return -1;
 | |
|         }
 | |
| 
 | |
|         ret = nvt_sdio_writeu8(nvt_wdev_sdio, NVT_SDIO_FUNC1, SDIO_ADDR_ZONE,
 | |
|                 SDIO_ADDR_ZONE_DIAG);
 | |
|         if (ret) {
 | |
|                 sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
|                 nvt_dbg(ERROR, "%s: write addr_zone reg fail\n", __func__);
 | |
|                 bus->statistic.ctl_rx_err_cnt++;
 | |
|                 return -1;
 | |
|         }
 | |
|         sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
| 
 | |
|         nvt_hdr = kmalloc(NVT_DIAG_HDR_LEN, GFP_KERNEL);
 | |
|         if (!nvt_hdr) {
 | |
|                 nvt_dbg(ERROR, "%s: alloc fail\n", __func__);
 | |
|                 retval = -ENOMEM;
 | |
|                 bus->statistic.ctl_rx_err_cnt++;
 | |
|                 goto alloc_fail;
 | |
|         }
 | |
| 
 | |
|         //read first 4 bytes (header)
 | |
|         sdio_claim_host(nvt_wdev_sdio->func[1]);
 | |
|         ret = nvt_sdio_dataread(nvt_wdev_sdio, NVT_SDIO_FUNC1,
 | |
|                 SDIO_DIAG_DATA_ADDR_OFFSET, nvt_hdr, NVT_DIAG_HDR_LEN,
 | |
|                 NVT_SDIO_CHUNK_DATA);
 | |
|         sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
|         if (ret) {
 | |
|                 nvt_dbg(ERROR, "%s: read header fail\n", __func__);
 | |
|                 retval = -1;
 | |
|                 bus->statistic.ctl_rx_err_cnt++;
 | |
|                 goto alloc_fail;
 | |
|         }
 | |
| 
 | |
|         //read whole message
 | |
|         ret = nvt_diag_rsp_header_check((struct _nvt_diag_resp *)(nvt_hdr));
 | |
|         if (ret) {
 | |
|                 nvt_dbg(ERROR, "%s: RX header error (%d)\n", __func__, ret);
 | |
|                 retval = -1;
 | |
|                 bus->statistic.ctl_rx_err_cnt++;
 | |
|                 goto alloc_fail;
 | |
|         }
 | |
|         read_len = ((struct _nvt_diag_resp *)(nvt_hdr))->resp_len;
 | |
|         read_len += NVT_DIAG_HDR_LEN;
 | |
|         sdio_claim_host(nvt_wdev_sdio->func[1]);
 | |
|         ret = nvt_sdio_dataread(nvt_wdev_sdio, NVT_SDIO_FUNC1,
 | |
|                 SDIO_DIAG_DATA_ADDR_OFFSET, buf, read_len, NVT_SDIO_CHUNK_DATA);
 | |
|         sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
|         if (ret) {
 | |
|                 nvt_dbg(ERROR, "%s: read message fail\n", __func__);
 | |
|                 retval = -1;
 | |
|                 bus->statistic.ctl_rx_err_cnt++;
 | |
|                 goto alloc_fail;
 | |
|         }
 | |
| 
 | |
|         retval = read_len;
 | |
| 
 | |
|         bus->statistic.ctl_rx_cnt++;
 | |
| 
 | |
| alloc_fail:
 | |
| 
 | |
|         kfree(nvt_hdr);
 | |
| 
 | |
|         /*restore original address zone */
 | |
|         sdio_claim_host(nvt_wdev_sdio->func[1]);
 | |
|         ret = nvt_sdio_writeu8(nvt_wdev_sdio, NVT_SDIO_FUNC1, SDIO_ADDR_ZONE,
 | |
|                 addr_zone_ori);
 | |
|         sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
|         if (ret) {
 | |
|                 nvt_dbg(ERROR, "%s: recover addr_zone fail\n", __func__);
 | |
|                 return -1;
 | |
|         }
 | |
| 
 | |
| 
 | |
|         return retval;
 | |
| }
 | |
| 
 | |
| static struct _nvt_wdev_bus_ops nvt_sdio_bus_ops = {
 | |
|         .init = nvt_sdio_bus_init,
 | |
|         .stop = nvt_sdio_bus_down,
 | |
|         .tx_data = nvt_sdio_bus_tx,
 | |
|         .tx_ctrl = nvt_sdio_bus_ctrl_tx,
 | |
|         .rx_ctrl = nvt_sdio_bus_ctrl_rx,
 | |
| };
 | |
| 
 | |
| static struct sdio_device_id nvt_sdio_supp_table[] = {
 | |
|         { SDIO_DEVICE(NVT_SDIO_VENDOR_ID, NVT_SDIO_DEVICE_ID_18202) },
 | |
|         { }
 | |
| };
 | |
| 
 | |
| static s32 nvt_sdio_attach_bus(struct _nvt_wdev_sdio *nvt_wdev_sdio)
 | |
| {
 | |
|         struct _nvt_bus *nvt_bus;
 | |
|         s32 ret = 0;
 | |
| 
 | |
|         if (nvt_wdev_sdio == NULL) {
 | |
|                 return -1;
 | |
|         }
 | |
| 
 | |
| 
 | |
|         sdio_claim_host(nvt_wdev_sdio->func[1]);
 | |
| 
 | |
|         /* set F1 blocksize */
 | |
|         ret = sdio_set_block_size(nvt_wdev_sdio->func[1],
 | |
|                 NVT_SDIO_F1_BLOCKSIZE);
 | |
|         if (ret) {
 | |
|                 nvt_dbg(ERROR, "%s: set F1 block size fail\n", __func__);
 | |
|                 sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
|                 return ret;
 | |
|         }
 | |
| 
 | |
|         ret = sdio_enable_func(nvt_wdev_sdio->func[1]);
 | |
|         if (ret) {
 | |
|                 nvt_dbg(ERROR, "%s: enable F1 fail(%d)\n", __func__, ret);
 | |
|                 sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
|                 return ret;
 | |
|         }
 | |
|         sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
| 
 | |
|         /* Make block size work */
 | |
|         nvt_wdev_sdio->func[1]->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE;
 | |
| 
 | |
|         /* struct member init */
 | |
|         atomic_set(&nvt_wdev_sdio->suspend, false);
 | |
|         init_waitqueue_head(&nvt_wdev_sdio->req_byte_wait);
 | |
|         init_waitqueue_head(&nvt_wdev_sdio->req_word_wait);
 | |
|         init_waitqueue_head(&nvt_wdev_sdio->req_buffer_wait);
 | |
|         init_waitqueue_head(&nvt_wdev_sdio->ctrl_msg_resp_wait);
 | |
| 
 | |
|         nvt_wdev_sdio->blocksize = NVT_SDIO_F1_BLOCKSIZE;
 | |
|         //nash:TODO??
 | |
|         nvt_wdev_sdio->rxpending = false;
 | |
|         //nash:TODO??
 | |
|         nvt_wdev_sdio->txminmax = 1;
 | |
|         nvt_wdev_sdio->pkt_len = 0;
 | |
|         nvt_wdev_sdio->is_polling = false;
 | |
|         if (nvt_wdev_sdio->is_polling == true) {
 | |
|                 nvt_wdev_sdio->pollrate = 1;
 | |
|         }
 | |
|         nvt_wdev_sdio->polltick = 0;
 | |
|         nvt_wdev_sdio->activity = true;
 | |
|         nvt_wdev_sdio->idletime = 1;
 | |
|         nvt_wdev_sdio->idlecount = 0;
 | |
| 
 | |
|         nvt_wdev_sdio->rx_int_cnt = 0;
 | |
|         INIT_WORK(&nvt_wdev_sdio->datahdl_work, nvt_sdio_data_handler);
 | |
| 
 | |
|         atomic_set(&nvt_wdev_sdio->datahdl_tskcnt, 0);
 | |
|         atomic_set(&nvt_wdev_sdio->intstatus, 0);
 | |
| 
 | |
|         skb_queue_head_init(&nvt_wdev_sdio->tx_skb_list);
 | |
|         skb_queue_head_init(&nvt_wdev_sdio->rx_skb_list);
 | |
| 
 | |
|         nvt_bus = nvt_bus_attach(nvt_wdev_sdio->dev, nvt_sdio_bus_ops);
 | |
|         if (nvt_bus == NULL) {
 | |
|                 nvt_dbg(ERROR, "%s: nvt_bus created fail\n", __func__);
 | |
|                 //20151218 nash: coverity#48936
 | |
|                 //ret = -ENOMEM;
 | |
|                 return -ENOMEM;
 | |
|         }
 | |
| 
 | |
|         dev_set_drvdata(&nvt_wdev_sdio->func[1]->dev, nvt_bus);
 | |
|         nvt_wdev_sdio->dev = &nvt_wdev_sdio->func[1]->dev;
 | |
| 
 | |
|         nvt_bus->type.nvt_wdev_sdio = nvt_wdev_sdio;
 | |
| 
 | |
|         nvt_bus->nvt_wdev_bus_ops.init(nvt_bus);
 | |
| 
 | |
|         nvt_diag_create_node(nvt_bus);
 | |
| 
 | |
| #if (NVT_SDIO_CFG_AUTO_PKT_DONE == 1)
 | |
|         //20160222 nash: config SDIO to atuo packet done mode
 | |
|         sdio_claim_host(nvt_wdev_sdio->func[1]);
 | |
|         ret = nvt_sdio_writeu32(nvt_wdev_sdio, NVT_SDIO_FUNC1,
 | |
|                 SDIO_TX_CONFIG,
 | |
|                 (AUTO_TX_PKT_DONE | AUTO_RX_PKT_DONE));
 | |
| 
 | |
|         sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
|         if (ret) {
 | |
|                 nvt_dbg(ERROR, "%s: config to auto pkt done mode fail\n",
 | |
|                         __func__);
 | |
| 
 | |
|                 sdio_claim_host(nvt_wdev_sdio->func[1]);
 | |
|                 nvt_sdio_abort(nvt_wdev_sdio, NVT_SDIO_FUNC1);
 | |
|                 nvt_sdio_bus_release(nvt_wdev_sdio, NVT_SDIO_FUNC1);
 | |
|                 sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
|                 return -1;
 | |
|         }
 | |
| #endif
 | |
| 
 | |
|         return 0;
 | |
| }
 | |
| 
 | |
| #ifdef CONFIG_PM_SLEEP
 | |
| 
 | |
| /**
 | |
|  * nvt_sdio_pm_is_fw_wakeup: polling if fw is ready when resume
 | |
|  * @nvt_bus: _nvt_bus structure
 | |
|  *
 | |
|  * Use fw get version diagnostic command to poll if firmware is ready
 | |
|  *
 | |
|  * Return: < 0: not wakeup or failure, 1:waked up
 | |
|  */
 | |
| //20160818 nash: for new deep sleep follow, we did not need this function
 | |
| #if 0
 | |
| static s32 nvt_sdio_pm_is_fw_wakeup(struct _nvt_bus *nvt_bus)
 | |
| {
 | |
|         s32 ret;
 | |
|         //20151218 nash: coverity#48981
 | |
|         s32 pkt_len = 0;
 | |
|         s32 retry = 10;
 | |
| 
 | |
|         struct _nvt_diag_req *diag_req = NULL;
 | |
|         struct _nvt_diag_resp *diag_resp = NULL;
 | |
| 
 | |
|         diag_req = kzalloc(sizeof(struct _nvt_diag_req), GFP_KERNEL);
 | |
|         if (diag_req == NULL) {
 | |
|                 nvt_dbg(ERROR, "%s: diag_req kzalloc is failed\n", __func__);
 | |
|                 ret = -1;
 | |
|                 goto alloc_failed;
 | |
|         }
 | |
| 
 | |
|         diag_resp = kzalloc(sizeof(struct _nvt_diag_resp), GFP_KERNEL);
 | |
|         if (diag_resp == NULL) {
 | |
|                 nvt_dbg(ERROR, "%s: diag_resp kzalloc is failed\n", __func__);
 | |
|                 ret = -1;
 | |
|                 goto alloc_failed;
 | |
|         }
 | |
| 
 | |
|         nvt_diag_pack_req(NVT_DIAG_CMD_CLASS_0, NVT_DIAG_GET_FW_VER_CMD, 0, 0,
 | |
|                 NULL, diag_req, &pkt_len);
 | |
| 
 | |
|         do {
 | |
|                 ret = nvt_bus->nvt_wdev_bus_ops.tx_ctrl(nvt_bus,
 | |
|                         (u8 *)diag_req, pkt_len);
 | |
|                 if (ret < 0) {
 | |
|                         if (ret == -ETIME) {
 | |
|                                 retry = 2;
 | |
|                         }
 | |
| 
 | |
|                         nvt_dbg(ERROR, "%s: failed to send diag req!!\n",
 | |
|                                 __func__);
 | |
|                         mdelay(100);
 | |
|                         continue;
 | |
|                 }
 | |
| 
 | |
|                 ret = nvt_bus->nvt_wdev_bus_ops.rx_ctrl(nvt_bus, (
 | |
|                         u8 *)diag_resp, sizeof(struct _nvt_diag_resp));
 | |
|                 if (ret < 0) {
 | |
|                         nvt_dbg(ERROR, "%s: failed to recv diag resp!!\n",
 | |
|                                 __func__);
 | |
|                         mdelay(100);
 | |
|                         continue;
 | |
|                 } else {
 | |
|                         ret = 1;
 | |
|                 }
 | |
|         } while (--retry > 0 && ret < 0);
 | |
| 
 | |
| 
 | |
| alloc_failed:
 | |
|         kfree(diag_req);
 | |
|         kfree(diag_resp);
 | |
| 
 | |
|         return ret;
 | |
| 
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static s32 nvt_sdio_pm_suspend(struct device *dev)
 | |
| {
 | |
|         mmc_pm_flag_t flags;
 | |
|         struct _nvt_bus *nvt_bus;
 | |
|         struct _nvt_if *nvt_if;
 | |
|         struct _nvt_wdev_sdio *nvt_wdev_sdio;
 | |
|         struct sk_buff *skb;
 | |
|         struct sk_buff *pnext;
 | |
|         s32 ret;
 | |
| 
 | |
|         nvt_dbg(INFO, "%s: enter\n", __func__);
 | |
| 
 | |
|         nvt_bus = dev_get_drvdata(dev);
 | |
|         //20151218 nash:  coverity#48947
 | |
|         if (nvt_bus == NULL) {
 | |
|                 return -1;
 | |
|         }
 | |
|         nvt_wdev_sdio = nvt_bus->type.nvt_wdev_sdio;
 | |
|         nvt_if = nvt_get_if_by_index(nvt_bus->nvt_adapter, 0);
 | |
|         if (!nvt_if) {
 | |
|                 nvt_dbg(ERROR, "%s: get NULL nvt_if!!\n", __func__);
 | |
|                 return -EINVAL;
 | |
|         }
 | |
| 
 | |
|         nvt_dbg(INFO, "%s: mode=%d\n", __func__, nvt_if->sleep_mode);
 | |
| 
 | |
|         if (nvt_if->sleep_mode == NVT_SLEEP_MODE_DISABLE) {
 | |
|                 nvt_dbg(ERROR, "%s: sleep mode is disable!!\n", __func__);
 | |
|                 return -EINVAL;
 | |
| 
 | |
|         } else if (nvt_if->sleep_mode == NVT_SLEEP_MODE_NORMAL ||
 | |
|                         nvt_if->sleep_mode == NVT_SLEEP_MODE_WOW) {
 | |
| 
 | |
|                 /* keep power */
 | |
|                 flags = sdio_get_host_pm_caps(nvt_wdev_sdio->func[1]);
 | |
|                 if (flags & MMC_PM_KEEP_POWER) {
 | |
|                         ret = sdio_set_host_pm_flags(nvt_wdev_sdio->func[1],
 | |
|                                         MMC_PM_KEEP_POWER);
 | |
|                         if (ret) {
 | |
|                                 nvt_dbg(ERROR,
 | |
|                                         "%s: set KEEP_POWER flag fail!!\n",
 | |
|                                         __func__);
 | |
|                                 return ret;
 | |
|                         }
 | |
|                 } else {
 | |
|                         nvt_dbg(ERROR, "%s: not support KEEP_POWER\n",
 | |
|                                         __func__);
 | |
|                 }
 | |
| 
 | |
|                 //20160726 nash: for new deep sleep flow, request cpf first
 | |
|                 ret = nvt_fw_request_cpf(nvt_bus);
 | |
|                 if (ret) {
 | |
|                         nvt_dbg(ERROR, "%s: request cpf fail!!\n", __func__);
 | |
|                         return ret;
 | |
|                 }
 | |
| 
 | |
|                 //data flush
 | |
|                 nvt_flush_txq(nvt_bus->nvt_adapter);
 | |
| 
 | |
|                 cancel_work_sync(&nvt_wdev_sdio->datahdl_work);
 | |
|                 atomic_set(&nvt_wdev_sdio->datahdl_tskcnt, 0);
 | |
| 
 | |
|                 skb_queue_walk_safe(&nvt_wdev_sdio->tx_skb_list, skb, pnext) {
 | |
|                         skb_unlink(skb, &nvt_wdev_sdio->tx_skb_list);
 | |
|                         dev_kfree_skb_any(skb);
 | |
|                 }
 | |
| 
 | |
|                 skb_queue_walk_safe(&nvt_wdev_sdio->rx_skb_list, skb, pnext) {
 | |
|                         skb_unlink(skb, &nvt_wdev_sdio->rx_skb_list);
 | |
|                         dev_kfree_skb_any(skb);
 | |
|                 }
 | |
| 
 | |
|                 atomic_set(&nvt_wdev_sdio->int_pending, 0);
 | |
| 
 | |
|         }
 | |
| 
 | |
| 
 | |
|         return 0;
 | |
| 
 | |
| }
 | |
| 
 | |
| static s32 nvt_sdio_pm_resume(struct device *dev)
 | |
| {
 | |
|         s32 ret;
 | |
|         struct _nvt_bus *nvt_bus;
 | |
|         struct _nvt_if *nvt_if;
 | |
|         struct _nvt_wdev_sdio *nvt_wdev_sdio;
 | |
| //        u32 entry_addr;
 | |
| 
 | |
|         nvt_dbg(INFO, "%s: enter\n", __func__);
 | |
| 
 | |
|         nvt_bus = dev_get_drvdata(dev);
 | |
|         //20151218 nash:  coverity#48946
 | |
|         if (nvt_bus == NULL) {
 | |
|                 return -1;
 | |
|         }
 | |
|         nvt_wdev_sdio = nvt_bus->type.nvt_wdev_sdio;
 | |
|         nvt_if = nvt_get_if_by_index(nvt_bus->nvt_adapter, 0);
 | |
|         if (!nvt_if) {
 | |
|                 nvt_dbg(ERROR, "%s: get NULL nvt_if!!\n", __func__);
 | |
|                 return -EINVAL;
 | |
|         }
 | |
| 
 | |
|         nvt_dbg(INFO, "%s: mode=%d\n", __func__, nvt_if->sleep_mode);
 | |
| 
 | |
|         if (nvt_if->sleep_mode == NVT_SLEEP_MODE_DISABLE) {
 | |
|                 nvt_dbg(ERROR, "%s: sleep mode is disable!!\n", __func__);
 | |
|                 return -EINVAL;
 | |
| 
 | |
|         } else if (nvt_if->sleep_mode == NVT_SLEEP_MODE_NORMAL) {
 | |
|                 sdio_claim_host(nvt_wdev_sdio->func[1]);
 | |
|                 ret = nvt_sdio_writeu8(nvt_wdev_sdio, NVT_SDIO_FUNC1,
 | |
|                                 SDIO_RESUME, (u8)SDIO_WAKEUP);
 | |
|                 sdio_release_host(nvt_wdev_sdio->func[1]);
 | |
|                 if (ret) {
 | |
|                         nvt_dbg(ERROR, "%s: set SDIO_WAKEUP fail\n", __func__);
 | |
|                         return ret;
 | |
|                 }
 | |
| 
 | |
|                 //20160715 nash: for FW new deep sleep flow
 | |
|                 ret = nvt_fw_deep_sleep_resume(nvt_bus);
 | |
|                 if (ret) {
 | |
|                         nvt_dbg(ERROR, "%s: deep sleep resume follow fail!!\n",
 | |
|                                 __func__);
 | |
|                         return ret;
 | |
|                 }
 | |
| 
 | |
|                 #if 0
 | |
|                 ret = nvt_sdio_pm_is_fw_wakeup(nvt_bus);
 | |
|                 if (ret != 1) {
 | |
|                         nvt_dbg(ERROR, "%s: polling if waked up fail!!\n",
 | |
|                                 __func__);
 | |
|                         return ret;
 | |
|                 }
 | |
|                 #endif
 | |
|         } else if (nvt_if->sleep_mode != NVT_SLEEP_MODE_WOW) {
 | |
|                 return -EINVAL;
 | |
|         }
 | |
| 
 | |
|         return 0;
 | |
| }
 | |
| 
 | |
| static const struct dev_pm_ops nvt_sdio_pm_ops = {
 | |
|         .suspend = nvt_sdio_pm_suspend,
 | |
|         .resume = nvt_sdio_pm_resume,
 | |
| };
 | |
| 
 | |
| /* 20151113: nash:work around because nt96660 mmc host driver suspend/resume not
 | |
|     ready yet
 | |
|  */
 | |
| s32 nvt_sdio_wa_suspend(struct _nvt_bus *nvt_bus)
 | |
| {
 | |
|         struct _nvt_wdev_sdio *nvt_wdev_sdio = nvt_bus->type.nvt_wdev_sdio;
 | |
| 
 | |
|         return nvt_sdio_pm_suspend(nvt_wdev_sdio->dev);
 | |
| }
 | |
| 
 | |
| s32 nvt_sdio_wa_resume(struct _nvt_bus *nvt_bus)
 | |
| {
 | |
|         struct _nvt_wdev_sdio *nvt_wdev_sdio = nvt_bus->type.nvt_wdev_sdio;
 | |
| 
 | |
|         return nvt_sdio_pm_resume(nvt_wdev_sdio->dev);
 | |
| }
 | |
| //nash:work around:end
 | |
| 
 | |
| #else
 | |
| /* 20151113: nash:work around because nt96660 mmc host driver suspend/resume not
 | |
|     ready yet
 | |
|  */
 | |
| s32 nvt_sdio_wa_suspend(struct _nvt_bus *nvt_bus)
 | |
| {
 | |
|         return -1;
 | |
| }
 | |
| 
 | |
| s32 nvt_sdio_wa_resume(struct _nvt_bus *nvt_bus)
 | |
| {
 | |
|         return -1;
 | |
| }
 | |
| //nash:work around:end
 | |
| #endif /* CONFIG_PM_SLEEP */
 | |
| 
 | |
| static s32 nvt_sdio_probe(struct sdio_func *func,
 | |
|                                 const struct sdio_device_id *id)
 | |
| {
 | |
|         struct _nvt_wdev_sdio *nvt_wdev_sdio;
 | |
|         struct _nvt_bus *nvt_bus = NULL;
 | |
|         s32 ret;
 | |
| 
 | |
|         nvt_dbg(INFO, "%s: enter\n", __func__);
 | |
|         nvt_dbg(INFO, "%s: Class=%x\n", __func__, func->class);
 | |
|         nvt_dbg(INFO, "%s: sdio vendor ID: 0x%04x\n", __func__, func->vendor);
 | |
|         nvt_dbg(INFO, "%s: sdio device ID: 0x%04x\n", __func__, func->device);
 | |
|         nvt_dbg(INFO, "%s: Function#: %d\n", __func__, func->num);
 | |
| 
 | |
|         //nash:TODO
 | |
|         /* Function number check */
 | |
| 
 | |
|         nvt_wdev_sdio = kmalloc(sizeof(struct _nvt_wdev_sdio), GFP_KERNEL);
 | |
|         if (nvt_wdev_sdio == NULL) {
 | |
|                 nvt_dbg(ERROR, "%s: alloc nvt_wdev_sdio fail\n", __func__);
 | |
|                 ret = -ENOMEM;
 | |
|                 goto alloc_failed;
 | |
|         }
 | |
| 
 | |
|         nvt_wdev_sdio->func[0] = func->card->sdio_func[0];
 | |
|         nvt_wdev_sdio->func[1] = func;
 | |
|         nvt_wdev_sdio->dev = &nvt_wdev_sdio->func[1]->dev;
 | |
| 
 | |
|         ret = nvt_sdio_attach_bus(nvt_wdev_sdio);
 | |
|         if (ret) {
 | |
|                 nvt_dbg(ERROR, "%s: nvt_sdio_attach_bus fail(err:%d)\n",
 | |
|                         __func__, ret);
 | |
|                 goto dscr_err;
 | |
|         }
 | |
| 
 | |
|         nvt_bus = dev_get_drvdata(nvt_wdev_sdio->dev);
 | |
|         //20151218 nash:  coverity#48948
 | |
|         if (nvt_bus == NULL) {
 | |
|                 goto dscr_err;
 | |
|         }
 | |
| 
 | |
|         ret = nvt_fw_download(nvt_bus, NVT_FW_DEFAULT_FW_MODE);
 | |
|         if (ret < 0) {
 | |
|                 nvt_dbg(ERROR, "%s: fw download fail\r\n", __func__);
 | |
|                 goto attach_bus_err;
 | |
|         }
 | |
| 
 | |
|         ret = nvt_register_to_system(nvt_wdev_sdio->dev);
 | |
|         if (ret < 0) {
 | |
|                 nvt_dbg(ERROR, "%s: nvt_register_to_system fail(%d)\n",
 | |
|                         __func__, ret);
 | |
|                 goto attach_bus_err;
 | |
|         }
 | |
| 
 | |
|         /* get firmware capability */
 | |
|         nvt_set_firmware_capability(nvt_bus->nvt_adapter);
 | |
| 
 | |
|         /* Intialized fw configuration by WIDs */
 | |
|         nvt_init_fw_by_wids(nvt_bus, NL80211_IFTYPE_STATION);
 | |
|         return 0;
 | |
| 
 | |
| attach_bus_err:
 | |
|         nvt_diag_delete_node(nvt_bus);
 | |
|         nvt_bus->nvt_wdev_bus_ops.stop(nvt_bus);
 | |
| 
 | |
| dscr_err:
 | |
|         kfree(nvt_wdev_sdio);
 | |
| 
 | |
| alloc_failed:
 | |
|         return ret;
 | |
| }
 | |
| 
 | |
| static void nvt_sdio_remove(struct sdio_func *func)
 | |
| {
 | |
|         struct _nvt_wdev_sdio *nvt_wdev_sdio;
 | |
|         struct _nvt_bus *nvt_bus;
 | |
|         struct _nvt_adapter *nvt_adapter;
 | |
| 
 | |
|         nvt_dbg(INFO, "%s: enter\n", __func__);
 | |
| 
 | |
|         nvt_bus = dev_get_drvdata(&func->dev);
 | |
|         if (nvt_bus == NULL) {
 | |
|                 nvt_dbg(INFO, "%s: nvt_bus is NULL\n", __func__);
 | |
|                 return;
 | |
|         }
 | |
| 
 | |
|         nvt_wdev_sdio = nvt_bus->type.nvt_wdev_sdio;
 | |
| 
 | |
|         nvt_adapter = nvt_bus->nvt_adapter;
 | |
| 
 | |
|         nvt_diag_delete_node(nvt_bus);
 | |
| 
 | |
|         if (nvt_bus->nvt_adapter != NULL) {
 | |
|                 nvt_del_all_if(nvt_bus->nvt_adapter);
 | |
|         }
 | |
|         nvt_bus->nvt_wdev_bus_ops.stop(nvt_bus);
 | |
| 
 | |
|         nvt_bus_detach(nvt_wdev_sdio->dev);
 | |
| 
 | |
|         kfree(nvt_wdev_sdio);
 | |
| 
 | |
|         kfree(nvt_adapter);
 | |
| 
 | |
| }
 | |
| 
 | |
| static struct sdio_driver nvtfmac_sdio_dvr = {
 | |
|         .name = "nvtwifi_sdio",
 | |
|         .probe = nvt_sdio_probe,
 | |
|         .remove = nvt_sdio_remove,
 | |
|         .id_table = nvt_sdio_supp_table,
 | |
| 
 | |
| #ifdef CONFIG_PM_SLEEP
 | |
|         .drv = {
 | |
|                 .pm = &nvt_sdio_pm_ops,
 | |
|         },
 | |
| #endif /* CONFIG_PM_SLEEP */
 | |
| };
 | |
| 
 | |
| void nvtfmac_sdio_init(void)
 | |
| {
 | |
|         nvt_dbg(INFO, "%s:\n", __func__);
 | |
|         sdio_register_driver(&nvtfmac_sdio_dvr);
 | |
| }
 | |
| 
 | |
| void nvtfmac_sdio_exit(void)
 | |
| {
 | |
|         nvt_dbg(INFO, "%s:\n", __func__);
 | |
|         sdio_unregister_driver(&nvtfmac_sdio_dvr);
 | |
| }
 | |
| 
 | 
