#include #include #include #include #include #include #include #include #include #include #include #include #include #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); }