summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/eswin/sdio/sdio.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/eswin/sdio/sdio.c')
-rw-r--r--drivers/net/wireless/eswin/sdio/sdio.c925
1 files changed, 925 insertions, 0 deletions
diff --git a/drivers/net/wireless/eswin/sdio/sdio.c b/drivers/net/wireless/eswin/sdio/sdio.c
new file mode 100644
index 000000000000..94a8b26b5ae9
--- /dev/null
+++ b/drivers/net/wireless/eswin/sdio/sdio.c
@@ -0,0 +1,925 @@
+/**
+ ******************************************************************************
+ *
+ * @file sdio.c
+ *
+ * @brief sdio driver function definitions
+ *
+ * Copyright (C) ESWIN 2015-2020
+ *
+ ******************************************************************************
+ */
+
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+#include "core.h"
+#include <uapi/linux/sched/types.h>
+//#include "debug.h"
+#include "ecrnx_defs.h"
+#include "ecrnx_sdio.h"
+#include "sdio.h"
+
+#include "sdio_host_interface.h"
+
+#define SDIO_DEBUG 1
+
+
+#define SDIO_ADDR_INFO (unsigned int)(0x081)
+#define SDIO_ADDR_INFO_ASYNC (unsigned int)(0x082)
+#define SDIO_ADDR_DATA (unsigned int)(0x080)
+#define NEXT_BUF_SZ_OFFSET (0)
+#define SLAVE_BUF_SZ_OFFSET (4)
+#define SDIO_AVL_NOTIFY_FLAG (0x5A5A5A5A)
+
+static atomic_t suspend;
+int stop_tx= 0;
+
+#ifdef CONFIG_SHOW_TX_SPEED
+unsigned long sdio_tx_last_jiffies;
+unsigned long sdio_tx_len_totol;
+unsigned long sdio_tx_error_cnt;
+#endif
+
+#ifdef CONFIG_SHOW_RX_SPEED
+unsigned long sdio_rx_last_jiffies;
+unsigned long sdio_rx_len_totol;
+unsigned long sdio_rx_error_cnt;
+#endif
+
+
+struct eswin_sdio * g_sdio;
+
+#if SDIO_DEBUG
+static struct eswin_sdio *trS;
+static struct dentry *p_debugfs_sdio;
+
+static int debugfs_sdio_read(void *data, u64 *val)
+{
+ struct eswin_sdio *tr_sdio = trS;
+
+ int pdata[10];
+
+ ECRNX_PRINT("%s\n", __func__);
+ ECRNX_PRINT("%s, func: 0x%x!!", __func__, tr_sdio->func);
+ ECRNX_PRINT("%s, func: 0x%x!!", __func__, tr_sdio->slave_avl_buf);
+ ECRNX_PRINT("%s, data addr: 0x%x, sdio_info: 0x%x, 0x%x !!", __func__, pdata, &tr_sdio->sdio_info, &(tr_sdio->sdio_info));
+#if 0
+ sdio_claim_host(tr_sdio->func);
+ /* replace the sdio_memcpy_fromio with sdio_readsb. in which the op code is 0, the addr will not change during transmittion */
+ ret = sdio_readsb(tr_sdio->func, pdata/* &tr_sdio->sdio_info */, SDIO_ADDR_INFO, 4);
+ //ret = 0;
+ if (ret < 0) {
+ ECRNX_PRINT(" debugfs_sdio_read failde, ret: %d\n", ret);
+ //print_hex_dump(KERN_DEBUG, "status - 1 ", DUMP_PREFIX_NONE, 16, 1, &priv->sdio_info, 32, false);
+ sdio_release_host(tr_sdio->func);
+ return ret;
+ }
+ print_hex_dump(KERN_DEBUG, "status - 1 ", DUMP_PREFIX_NONE, 16, 1, pdata, 16, false);
+ sdio_release_host(tr_sdio->func);
+#endif
+ return 0;
+}
+
+static int debugfs_sdio_write(void *data1, u64 val)
+{
+ int ret = 0;
+ //int cmd = (int)val;
+ char cmd[16] = {1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7};
+ struct sk_buff * skb;
+ char * p;
+
+#if 1
+ struct eswin_sdio *tr_sdio = trS;
+
+ ECRNX_PRINT(" %s, tr_sdio->func: %x\n", __func__, g_sdio->func);
+
+ sdio_claim_host(tr_sdio->func);
+
+ /* replace the sdio_memcpy_xxio with sdio_xxxxsb. in sdio_xxxxsb the op code is 0, the addr will not change during transmittion */
+ ret = sdio_writesb(tr_sdio->func, 0x100, &cmd, 16);
+ if (ret < 0) {
+ ECRNX_PRINT(" debugfs_sdio_write failde, ret: %d\n", ret);
+ }
+
+ sdio_release_host(tr_sdio->func);
+#else
+
+ skb = dev_alloc_skb(4096);
+ memset(skb->data, 0x34, 4096);
+
+ p = (char *)(skb->data);
+ for (ret=0; ret<4096; ret++) {
+ *p++ = (char)ret;
+ }
+
+ sdio_host_send(skb->data, 4096, 0x100);
+#endif
+ return ret;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(debugfs_sdio,
+ debugfs_sdio_read,
+ debugfs_sdio_write,
+ "%llu\n");
+
+void debugfs_sdio_init(void)
+{
+ ECRNX_PRINT("%s\n", __func__);
+ p_debugfs_sdio = debugfs_create_file("sdiod", 0777, NULL, NULL, &debugfs_sdio);
+}
+
+void debug_sdio_rx_callback(struct sk_buff *skb)
+{
+ ECRNX_PRINT("rx-callback: %d\n", skb->len);
+ print_hex_dump(KERN_DEBUG, DBG_PREFIX_SDIO_RX, DUMP_PREFIX_NONE, 16, 1, skb->data, skb->len, false);
+ dev_kfree_skb(skb);
+}
+#endif
+
+
+static inline u16 sdio_num_slots(struct eswin_sdio *tr_sdio, int dir)
+{
+ return (tr_sdio->slot[dir].head - tr_sdio->slot[dir].tail);
+}
+
+#if 0
+static void sdio_credit_skb(struct eswin_sdio *tr_sdio)
+{
+ struct sk_buff *skb;
+ struct hif *hif;
+ struct wim *wim;
+ struct wim_credit_report *cr;
+ u8 *p;
+ int i;
+ int size = sizeof(*hif) + sizeof(struct wim) + sizeof(*cr);
+
+ if (!once) {
+ once = true;
+ return;
+ }
+
+ skb = dev_alloc_skb(size);
+
+ p = skb->data;
+ hif = (void *)p;
+ hif->type = HIF_TYPE_WIM;
+ hif->subtype = HIF_WIM_SUB_EVENT;
+ hif->vifindex = 0;
+ hif->len = sizeof(*wim) + sizeof(*cr);
+
+ p += sizeof(*hif);
+ wim = (void *)p;
+ wim->event = WIM_EVENT_CREDIT_REPORT;
+
+ p += sizeof(*wim);
+ cr = (void *)p;
+ cr->h.type = WIM_TLV_AC_CREDIT_REPORT;
+ cr->h.len = sizeof(struct wim_credit_report_param);
+
+ cr->v.change_index = 0;
+
+ for (i = 0; i < CREDIT_QUEUE_MAX; i++) {
+ u8 room = tr_sdio->front[i] - tr_sdio->rear[i];
+
+ room = min(tr_sdio->credit_max[i], room);
+ cr->v.ac[i] = tr_sdio->credit_max[i] - room;
+
+#ifdef CONFIG_NRC_HIF_PRINT_FLOW_CONTROL
+ ECRNX_PRINT(" credit[%d] %d f:%d, r:%d\n",
+ i, cr->v.ac[i], tr_sdio->front[i],
+ tr_sdio->rear[i]);
+#endif
+ }
+
+ skb_put(skb, hif->len+sizeof(*hif));
+
+ if (tr_sdio->tr->rx_callback) {
+ tr_sdio->tr->rx_callback(tr_sdio->tr->umac_priv, skb);
+ } else {
+ dev_kfree_skb(skb);
+ }
+}
+#endif
+
+static int sdio_update_status(struct eswin_sdio *tr_sdio)
+{
+ u32 rear;
+ int ac, ret;
+ u8 * buf = kmalloc(4,GFP_ATOMIC);
+
+ //ECRNX_PRINT("%s\n", __func__);
+ sdio_claim_host(tr_sdio->func);
+ trS = tr_sdio;
+ /* replace the sdio_memcpy_fromio with sdio_readsb. in which the op code is 0, the addr will not change during transmittion */
+ ret = sdio_readsb(tr_sdio->func, buf, SDIO_ADDR_INFO_ASYNC, 4);
+ if (ret < 0) {
+ ECRNX_PRINT(" sdio_update_status, ret: %d\n", ret);
+ kfree(buf);
+ //print_hex_dump(KERN_DEBUG, "status - 1 ", DUMP_PREFIX_NONE, 16, 1, &priv->sdio_info, 32, false);
+ sdio_release_host(tr_sdio->func);
+ return ret;
+ }
+
+ /*
+ if (slave_avl_buf < tr_sdio->curr_tx_size)
+ {
+ sdio_release_host(tr_sdio->func);
+ return -1;
+ }
+ */
+ spin_lock(&tr_sdio->lock);
+ tr_sdio->slave_avl_buf = *(unsigned int*)buf;
+ spin_unlock(&tr_sdio->lock);
+ kfree(buf);
+ sdio_release_host(tr_sdio->func);
+
+ return 0;
+}
+
+static void sdio_poll_status(struct work_struct *work)
+{
+ struct eswin_sdio *tr_sdio = container_of(to_delayed_work(work), struct eswin_sdio, work);
+
+ if( sdio_update_status(tr_sdio)) {
+ schedule_delayed_work(&tr_sdio->work, msecs_to_jiffies(1000));
+ } else {
+ wake_up_interruptible(&tr_sdio->wait);
+ }
+}
+
+int continue_transfer = 0;
+
+static struct sk_buff *sdio_rx_skb(struct eswin_sdio *tr_sdio)
+{
+ struct sk_buff *skb = NULL;
+ int ret;
+ int recv_len;
+ unsigned int slave_avl_buf;
+ //unsigned int slave_avl_buf_last;
+ //unsigned int real_length;
+
+ if(tr_sdio->next_rx_size)
+ {
+ //clear next rx buffer size
+ tr_sdio->next_rx_size = 0;
+ }
+ else
+ {
+ /* Wait until at least one rx slot is non-empty */
+ ret = wait_event_interruptible(tr_sdio->wait, (tr_sdio->recv_len > 1 || kthread_should_stop()));
+ if (ret < 0)
+ goto fail;
+ }
+
+
+
+ if (kthread_should_stop())
+ goto fail;
+
+ recv_len = tr_sdio->recv_len;
+ skb = dev_alloc_skb(recv_len);
+
+ sdio_claim_host(tr_sdio->func);
+
+ /* replace the sdio_memcpy_fromio with sdio_readsb. in which the op code is 0, the addr will not change during transmittion */
+ ret = sdio_readsb(tr_sdio->func, skb->data, SDIO_ADDR_DATA, recv_len);
+ if (ret){
+ print_hex_dump(KERN_DEBUG, DBG_PREFIX_SDIO_RX, DUMP_PREFIX_NONE, 16, 1, skb->data, recv_len, false);
+ ECRNX_PRINT("[eswin-err] rx-len: %d, ret %d\n", recv_len, ret);
+ stop_tx = 1;
+ sdio_release_host(tr_sdio->func);
+ goto fail;
+ }
+
+ //get next rx size
+ tr_sdio->next_rx_size=*(unsigned int *)&skb->data[NEXT_BUF_SZ_OFFSET];
+ if(tr_sdio->next_rx_size > 1)
+ {
+ tr_sdio->recv_len = tr_sdio->next_rx_size;
+ }
+ else
+ {
+ tr_sdio->recv_len = 1;
+ }
+ //get slave avl buf cnt
+ slave_avl_buf = *(unsigned int *)&skb->data[SLAVE_BUF_SZ_OFFSET] >> 16;
+ spin_lock(&tr_sdio->lock);
+ tr_sdio->slave_avl_buf = slave_avl_buf;
+ spin_unlock(&tr_sdio->lock);
+
+ skb_put(skb, recv_len);
+ skb_queue_tail(&tr_sdio->skb_rx_list,skb);
+
+ sdio_release_host(tr_sdio->func);
+
+ if (slave_avl_buf > (SDIO_PKG_MAX_CNT-1))
+ {
+ if (atomic_read(&tr_sdio->slave_buf_suspend))
+ {
+ wake_up_interruptible(&tr_sdio->wait);
+ }
+ }
+
+ //ECRNX_PRINT("rx-len: %d, skb: 0x%x \n", skb->len, skb);
+ return skb;
+
+fail:
+ if (skb)
+ dev_kfree_skb(skb);
+ return NULL;
+}
+
+
+#include <linux/sched.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
+#include <uapi/linux/sched/types.h>
+#endif
+//extern int sched_setscheduler(struct task_struct *, int, const struct sched_param *);
+
+static int sdio_rx_unpack_thread(void *data)
+{
+ struct eswin_sdio *tr_sdio = (struct eswin_sdio *)data;
+ struct eswin *tr = tr_sdio->tr;
+ struct sk_buff *skb;
+ struct sk_buff *skb_frag;
+ struct sdio_rx_head_t * rx_head;
+ int ret;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
+ struct sched_param param = { .sched_priority = 1 };
+ param.sched_priority = 56;
+ sched_setscheduler(get_current(), SCHED_FIFO, &param);
+#else
+ sched_set_fifo(get_current());
+#endif
+ ECRNX_PRINT("rx unpack thread entry\n");
+
+ while (!kthread_should_stop())
+ {
+ ret = wait_event_interruptible(tr_sdio->wait_unpack, skb_peek(&tr_sdio->skb_rx_list)
+ || kthread_should_stop() );
+ if(kthread_should_stop())
+ continue;
+ if (ret < 0)
+ {
+ ECRNX_ERR("rx unpack thread error!\n");
+ return 0;
+ }
+ while(NULL != (skb = skb_dequeue(&tr_sdio->skb_rx_list)))
+ {
+ while (skb->len > 8)//valid data must contain 8 byte head
+ {
+ rx_head = (struct sdio_rx_head_t *)skb->data;
+ if(rx_head->data_len == 0)
+ break;
+ if (*(unsigned int *)&skb->data[8] == SDIO_AVL_NOTIFY_FLAG && rx_head->data_len == 12)
+ {
+ skb_pull(skb, ALIGN(rx_head->data_len, 4));
+ continue;
+ }
+ skb_frag = dev_alloc_skb(rx_head->data_len);
+ memcpy(skb_frag->data,skb->data,rx_head->data_len);
+ skb_put(skb_frag,rx_head->data_len);
+
+ if (skb_frag->len > skb->len)
+ {
+ ECRNX_ERR("skb len error!!! frag len:%d skb->len:%d\n",skb_frag->len,skb->len);
+ print_hex_dump(KERN_DEBUG, DBG_PREFIX_SDIO_RX, DUMP_PREFIX_NONE, 16, 1, skb->data, skb->len, false);
+ BUG_ON(1);
+ }
+ tr->rx_callback(tr->umac_priv, skb_frag);
+ skb_pull(skb,ALIGN(rx_head->data_len, 4));
+ }
+ dev_kfree_skb(skb);
+ }
+ }
+ ECRNX_PRINT("rx unpack thread exit\n");
+ return 0;
+}
+
+static int sdio_rx_thread(void *data)
+{
+ struct eswin_sdio *tr_sdio = (struct eswin_sdio *)data;
+ struct eswin *tr = tr_sdio->tr;
+ struct sk_buff *skb;
+ struct sk_buff *skb_frag;
+ struct sdio_rx_head_t * rx_head;
+ int is_suspend;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
+ struct sched_param param = { .sched_priority = 1 };
+ param.sched_priority = 56;
+ sched_setscheduler(get_current(), SCHED_FIFO, &param);
+#else
+ sched_set_fifo(get_current());
+#endif
+ ECRNX_PRINT("rx thread entry, loopbakc: %d\n", tr->loopback);
+
+ while (!kthread_should_stop())
+ {
+ skb = sdio_rx_skb(tr_sdio);
+ is_suspend = atomic_read(&suspend);
+ ECRNX_DBG("rx_cb: 0x%x, skb: 0x%x, is_suspend: 0x%x \n", tr->rx_callback, skb, is_suspend);
+ if ((!tr->rx_callback) || (skb && is_suspend)){
+ dev_kfree_skb(skb);
+ }
+ else if (skb){
+ wake_up_interruptible(&tr_sdio->wait_unpack);
+#if 0
+ while (skb->len > 8)//valid data must contain 8 byte head
+ {
+ //printk("skb len 0:%d",skb->len);
+ rx_head = (struct sdio_rx_head_t *)skb->data;
+ if(rx_head->data_len == 0)
+ break;
+ skb_frag = dev_alloc_skb(rx_head->data_len);
+ memcpy(skb_frag->data,skb->data,rx_head->data_len);
+ skb_put(skb_frag,rx_head->data_len);
+ //printk("skb_frag len 1:%d",skb_frag->len);
+
+ if (skb_frag->len > skb->len)
+ {
+ printk("skb len error!!! frag len:%d skb->len:%d\n",skb_frag->len,skb->len);
+ print_hex_dump(KERN_DEBUG, "eswin-rx: ", DUMP_PREFIX_NONE, 16, 1, skb->data, skb->len, false);
+ BUG_ON(1);
+ }
+
+ tr->rx_callback(tr->umac_priv, skb_frag);
+ skb_pull(skb,ALIGN(rx_head->data_len, 4));
+ //printk("skb len 1:%d",skb->len);
+ //break;
+
+ }
+ dev_kfree_skb(skb);
+ //dev_alloc_skb
+ //tr->rx_callback(tr->umac_priv, skb);
+ //debug_sdio_rx_callback(skb);
+#endif
+ }
+ else {
+ ECRNX_ERR("rx-head: %d, rx-tail: %d, rx-cnt: %d\n",
+ tr_sdio->slot[RX_SLOT].head, tr_sdio->slot[RX_SLOT].tail, tr_sdio->sdio_info.info_rd);
+ break;
+ }
+ }
+ ECRNX_PRINT("rx thread exit\n");
+
+ return 0;
+}
+
+#if 0
+static int sdio_noop(struct eswin *tr, struct sk_buff *skb)
+{
+ ECRNX_PRINT("%s exit!!", __func__);
+ return 0;
+}
+#endif
+
+void dump_sdio_buf(struct tx_buff_node *node)
+{
+ int i = 0;
+
+ ECRNX_PRINT("sdio tx len %d\n", node?node->len:-1);
+ /*for (i = 0; i < node->len; ++i) {
+ if (i % 16 == 0 && i) {
+ printk("\n");
+ }
+ printk("%02x ", ((unsigned char *)(node->buff))[i]);
+ }*/
+
+}
+
+static int sdio_xmit(struct eswin *tr, struct tx_buff_pkg_node *node)
+{
+ struct eswin_sdio *tr_sdio = (struct eswin_sdio *)tr->drv_priv;
+ struct eswin * tr1 = tr_sdio->tr;
+ int ret,flag;
+ unsigned int slave_avl_buf = 0;
+
+ //dump_sdio_buf(node);
+ spin_lock(&tr_sdio->lock);
+ slave_avl_buf = tr_sdio->slave_avl_buf;
+ spin_unlock(&tr_sdio->lock);
+ if(slave_avl_buf < node->node_cnt && (node->flag & FLAG_MSG_TYPE_MASK) == TX_FLAG_TX_DESC)
+ {
+ atomic_set(&tr_sdio->slave_buf_suspend, 1);
+ ret = wait_event_interruptible(tr_sdio->wait, node->node_cnt < tr_sdio->slave_avl_buf);
+ if (ret < 0)
+ {
+ ECRNX_PRINT("[transa] sdio_xmit, wait_event_interruptible fail, ret = %d slave_avl_buf=%d\n", ret, tr_sdio->slave_avl_buf);
+ return ret;
+ }
+ cancel_delayed_work_sync(&tr_sdio->work);
+ atomic_set(&tr_sdio->slave_buf_suspend, 0);
+ }
+ sdio_claim_host(tr_sdio->func);
+ /* replace the sdio_memcpy_xxio with sdio_xxxxsb. in sdio_xxxxsb the op code is 0, the addr will not change during transmittion */
+ ret = sdio_writesb(tr_sdio->func, node->flag, node->buff, node->len);
+ if(ret) {
+ //stop_tx = 1;
+ ECRNX_ERR("sdio_xmit error, len = %d, ret:%d slave_avl_buf=%d ,node->node_cnt :%d\n",\
+ node->len, ret, tr_sdio->slave_avl_buf,node->node_cnt);
+ print_hex_dump(KERN_DEBUG, DBG_PREFIX_SDIO_TX, DUMP_PREFIX_NONE, 32, 1,
+ node->buff, 64, false);
+ }
+
+ spin_lock(&tr_sdio->lock);
+ if ((node->flag & FLAG_MSG_TYPE_MASK) == TX_FLAG_TX_DESC)
+ {
+ tr_sdio->slave_avl_buf -= node->node_cnt;
+ }
+ spin_unlock(&tr_sdio->lock);
+
+ sdio_release_host(tr_sdio->func);
+ ECRNX_DBG(" sdio_xmit ok, len = %d, flag: 0x%02x!\n", node->len, node->flag);
+ return ret;
+}
+
+static int sdio_start(struct eswin *tr)
+{
+ int ret=0;
+
+ struct eswin_sdio *tr_sdio = (struct eswin_sdio *)tr->drv_priv;
+
+ ECRNX_PRINT("%s\n", __func__);
+
+ /* Start rx thread */
+ //if(tr->loopback == 1)
+ // return 0;
+ tr_sdio->curr_tx_size = 0;
+ ret = sdio_update_status(tr_sdio);
+ INIT_DELAYED_WORK(&tr_sdio->work, sdio_poll_status);
+
+ tr_sdio->kthread = kthread_run(sdio_rx_thread, tr_sdio, "sdio-rx");
+ tr_sdio->kthread_unpack = kthread_run(sdio_rx_unpack_thread, tr_sdio, "sdio-rx-unpack");
+
+ atomic_set(&suspend, 0);
+ atomic_set(&tr_sdio->slave_buf_suspend, 0);
+ spin_lock_init(&tr_sdio->lock);
+
+ return ret;
+}
+
+
+static int sdio_suspend(struct eswin *tr)
+{
+ atomic_set(&suspend, 1);
+ return 0;
+}
+
+static int sdio_resume(struct eswin *tr)
+{
+ atomic_set(&suspend, 0);
+ return 0;
+}
+
+
+static int sdio_raw_write(struct eswin *tr, const void *data, const u32 len)
+{
+ int ret;
+ struct eswin_sdio *tr_sdio = (struct eswin_sdio *)tr->drv_priv;
+
+ ECRNX_PRINT(" %s, entry~", __func__);
+
+ sdio_claim_host(tr_sdio->func);
+ /* replace the sdio_memcpy_xxio with sdio_xxxxsb. in sdio_xxxxsb the op code is 0, the addr will not change during transmittion */
+ ret = sdio_writesb(tr_sdio->func, SDIO_ADDR_DATA, data, len);
+ sdio_release_host(tr_sdio->func);
+
+ return ret;
+}
+
+
+static int sdio_wait_ack(struct eswin *tr)
+{
+ int data = 0;
+ u8 * buf = kmalloc(4,GFP_ATOMIC);
+ struct eswin_sdio *tr_sdio = (struct eswin_sdio *)tr->drv_priv;
+
+ ECRNX_PRINT(" %s, entry~", __func__);
+
+ sdio_claim_host(tr_sdio->func);
+ /* replace the sdio_memcpy_xxio with sdio_xxxxsb. in sdio_xxxxsb the op code is 0, the addr will not change during transmittion */
+ sdio_readsb(tr_sdio->func, buf, SDIO_ADDR_DATA, 1);
+ sdio_release_host(tr_sdio->func);
+ data = *(int *)buf;
+ kfree(buf);
+
+ return data;
+}
+
+
+
+static struct sdio_ops eswin_sdio_ops = {
+ .start = sdio_start,
+ .xmit = sdio_xmit,
+ .suspend = sdio_suspend,
+ .resume = sdio_resume,
+ .write = sdio_raw_write,
+ .wait_ack = sdio_wait_ack,
+};
+
+
+
+static void eswin_sdio_irq_handler(struct sdio_func *func)
+{
+ struct eswin_sdio *tr_sdio = sdio_get_drvdata(func);
+ u32 rear;
+ unsigned char lowbyte, highbyte;
+ int ret, ac;
+
+ //printk(" %s, entry~\n", __func__);
+
+ sdio_claim_host(tr_sdio->func);
+ lowbyte = sdio_readb(tr_sdio->func, 0x00, &ret);
+ highbyte = sdio_readb(tr_sdio->func, 0x01, &ret);
+ tr_sdio->recv_len = (highbyte << 8) | lowbyte;
+ //printk("%s %u, %hhu, %hhu!", __func__, tr_sdio->recv_len, highbyte, lowbyte);
+
+#if 0
+ //_info(" eswin_sdio_irq_handler, len: %d\n", tr_sdio->recv_len);
+ if(tr_sdio->recv_len == 1) {
+
+ ret = sdio_memcpy_fromio(tr_sdio->func, &tr_sdio->sdio_info, SDIO_ADDR_INFO, 0x10 /*priv->recv_len*/);
+ if (ret < 0) {
+ ECRNX_PRINT(" eswin_sdio_irq_handler, info-ret: %d\n", ret);
+ //print_hex_dump(KERN_DEBUG, "status - 2 ", DUMP_PREFIX_NONE, 16, 1, &priv->sdio_info, 32, false);
+ sdio_release_host(tr_sdio->func);
+ return;
+ }
+ //ECRNX_PRINT(" get info\n");
+ //ECRNX_PRINT(" eswin_sdio_irq_handler, info-wr: %#x, info-rd: %#x\n", priv->sdio_info.info_wr, priv->sdio_info.info_rd);
+
+ tr_sdio->slot[TX_SLOT].head = tr_sdio->sdio_info.info_wr;
+ tr_sdio->slot[RX_SLOT].head = tr_sdio->sdio_info.info_rd;
+
+
+ //if (hdev->nw->loopback)
+ // return;
+
+ if((tr_sdio->sdio_info.credit_vif0 != tr_sdio->credit_vif0)
+ ||(tr_sdio->sdio_info.credit_vif1 != tr_sdio->credit_vif1)){
+ /* Update VIF0 credit */
+ rear = tr_sdio->sdio_info.credit_vif0;
+ tr_sdio->credit_vif0 = rear;
+ for (ac = 0; ac < 4; ac++)
+ tr_sdio->rear[ac] = (rear >> 8*ac) & 0xff;
+
+
+ /* Update VIF1 credit */
+ rear = tr_sdio->sdio_info.credit_vif1;
+ tr_sdio->credit_vif1 = rear;
+ for (ac = 0; ac < 4; ac++)
+ tr_sdio->rear[6+ac] = (rear >> 8*ac) & 0xff;
+
+ //need_credit = 1;
+ //sdio_release_host(tr_sdio->func);
+ //sdio_credit_skb(tr_sdio);
+ //sdio_claim_host(func);
+ }
+#if 0
+ printk("irq: wr: %d, rd: %d, credit:%d/%d, %d/%d, %d/%d, %d/%d\n",
+ priv->sdio_info.info_wr, priv->sdio_info.info_rd,
+ priv->rear[3], priv->front[3],
+ priv->rear[2], priv->front[2],
+ priv->rear[1], priv->front[1],
+ priv->rear[0], priv->front[0]);
+#endif
+
+#ifdef CONFIG_NRC_HIF_PRINT_FLOW_CONTROL
+ //nrc_dbg(NRC_DBG_HIF, "-%s\n", __func__);
+#endif
+
+ }
+ else
+ {
+ //ECRNX_PRINT(" get data len: %d\n", priv->recv_len);
+ }
+#endif
+ sdio_release_host(tr_sdio->func);
+ wake_up_interruptible(&tr_sdio->wait);
+}
+
+static void eswin_sdio_irq2_handler(struct sdio_func *func)
+{
+ struct eswin_sdio *tr_sdio = g_sdio;
+ unsigned char lowbyte, highbyte;
+ int ret, recv_len;
+ struct sk_buff * skb;
+
+ ECRNX_PRINT(" %s, entry~\n", __func__);
+
+ sdio_claim_host(func);
+ lowbyte = sdio_readb(func, 0x00, &ret);
+ highbyte = sdio_readb(func, 0x01, &ret);
+ sdio_release_host(func);
+
+ recv_len = (highbyte << 8) | lowbyte;
+ skb = dev_alloc_skb(recv_len);
+ skb_put(skb, recv_len);
+
+ sdio_claim_host(func);
+ /* replace the sdio_memcpy_fromio with sdio_readsb. in which the op code is 0, the addr will not change during transmittion */
+ ret = sdio_readsb(func, skb->data, SDIO_ADDR_DATA, recv_len);
+ if (ret){
+ ECRNX_ERR("[eswin-err] rx-len: %d\n", skb->len);
+ }
+
+ sdio_release_host(func);
+
+ print_hex_dump(KERN_DEBUG, DBG_PREFIX_SDIO_RX, DUMP_PREFIX_NONE, 16, 1, skb->data, skb->len, false);
+ dev_kfree_skb(skb);
+}
+
+struct device *eswin_sdio_get_dev(void *plat)
+{
+ struct eswin* tr = (struct eswin*)plat;
+
+ return tr->dev;
+}
+
+
+static int eswin_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
+{
+ int ret;
+ struct eswin_sdio * tr_sdio;
+ struct eswin* tr;
+
+ ECRNX_PRINT("%s entry, func: %d!!", __func__, func->num);
+
+ if (func->num == 1) {
+ tr = eswin_core_create(sizeof(* tr_sdio), &func->dev, &eswin_sdio_ops);
+ if(!tr) {
+ dev_err(&func->dev, "failed to allocate core\n");
+ return -ENOMEM;
+ }
+
+ tr_sdio = (struct eswin_sdio *)tr->drv_priv;
+ g_sdio = tr_sdio;
+ tr_sdio->tr = tr;
+ tr_sdio->func = func;
+ } else {
+ g_sdio->func2 = func;
+
+ sdio_claim_host(func);
+ sdio_enable_func(func);
+ func->max_blksize = ESWIN_SDIO_BLK_SIZE;
+ sdio_set_block_size(func, func->max_blksize);
+ sdio_claim_irq(func, eswin_sdio_irq2_handler);
+ sdio_release_host(func);
+ return 0;
+ }
+
+ tr_sdio->slot[TX_SLOT].size = 456;
+ tr_sdio->slot[RX_SLOT].size = 492;
+
+ tr_sdio->credit_max[0] = CREDIT_AC0;
+ tr_sdio->credit_max[1] = CREDIT_AC1;
+ tr_sdio->credit_max[2] = CREDIT_AC2;
+ tr_sdio->credit_max[3] = CREDIT_AC3;
+
+ tr_sdio->credit_max[6] = CREDIT_AC0;
+ tr_sdio->credit_max[7] = CREDIT_AC1;
+ tr_sdio->credit_max[8] = CREDIT_AC2;
+ tr_sdio->credit_max[9] = CREDIT_AC3;
+
+ init_waitqueue_head(&tr_sdio->wait);
+ init_waitqueue_head(&tr_sdio->wait_unpack);
+
+ func->max_blksize = ESWIN_SDIO_BLK_SIZE;
+
+ skb_queue_head_init(&tr_sdio->skb_rx_list);
+ //skb_queue_head_init(tr_sdio->skb_rx_unpack_list);
+
+ sdio_claim_host(func);
+
+ ret = sdio_enable_func(func);
+ if(ret) {
+ ECRNX_PRINT("failed to enable sdio func\n");
+ goto release;
+ }
+
+ ret = sdio_set_block_size(func, func->max_blksize);
+ if(ret) {
+ ECRNX_PRINT("failed to set sdio func block size\n");
+ goto release;
+ }
+
+ ret = sdio_claim_irq(func, eswin_sdio_irq_handler);
+ if(ret) {
+ ECRNX_PRINT("failed to claim sdio irq\n");
+ goto release;
+ }
+
+ sdio_release_host(func);
+ sdio_set_drvdata(func, tr_sdio);
+
+#ifdef CONFIG_SHOW_TX_SPEED
+ sdio_tx_last_jiffies = jiffies;
+ sdio_tx_len_totol = 0;
+ sdio_tx_error_cnt = 0;
+#endif
+
+#ifdef CONFIG_SHOW_RX_SPEED
+ sdio_rx_last_jiffies = jiffies;
+ sdio_rx_len_totol = 0;
+ sdio_rx_error_cnt = 0;
+#endif
+
+
+ ret = eswin_core_register(tr);
+ if(ret) {
+ ECRNX_PRINT("failed to register core\n");
+
+ }
+
+ debugfs_sdio_init();
+
+ ECRNX_PRINT("%s exit!!", __func__);
+ return 0;
+
+release:
+ sdio_release_host(func);
+ return ret;
+}
+
+static void eswin_sdio_remove(struct sdio_func *func)
+{
+ struct eswin_sdio *tr_sdio = sdio_get_drvdata(func);
+ struct eswin *tr = tr_sdio->tr;
+
+
+ ECRNX_PRINT(" %s entry!!\n", __func__);
+ debugfs_remove_recursive(p_debugfs_sdio);
+
+ eswin_core_unregister(tr);
+
+ sdio_claim_host(func);
+ sdio_release_irq(func);
+ sdio_disable_func(func);
+ sdio_release_host(func);
+
+ kthread_stop(tr_sdio->kthread);
+ wake_up_interruptible(&tr_sdio->wait);
+ kthread_stop(tr_sdio->kthread_unpack);
+ wake_up_interruptible(&tr_sdio->wait_unpack);
+
+ eswin_core_destroy(tr);
+ ECRNX_PRINT(" %s exit!!\n", __func__);
+}
+
+
+static const struct sdio_device_id eswin_sdio_dev[] =
+{
+ { SDIO_DEVICE(ESWIN_SDIO_VENDER, ESWIN_SDIO_DEVICE) },
+ {},
+};
+
+static struct sdio_driver eswin_sdio_driver =
+{
+ .name = "eswin_sdio",
+ .id_table = eswin_sdio_dev,
+ .probe = eswin_sdio_probe,
+ .remove = eswin_sdio_remove,
+};
+
+static int __init eswin_sdio_init(void)
+{
+ int ret;
+ ECRNX_PRINT(" %s entry!!", __func__);
+
+ ret = sdio_register_driver(&eswin_sdio_driver);
+ if (ret)
+ ECRNX_PRINT("sdio driver registration failed: %d\n", ret);
+
+ ECRNX_PRINT(" %s exit!!", __func__);
+ return ret;
+}
+
+static void __exit eswin_sdio_exit(void)
+{
+ ECRNX_PRINT(" %s entry!!", __func__);
+ sdio_unregister_driver(&eswin_sdio_driver);
+ ECRNX_PRINT(" %s exit!!", __func__);
+}
+
+int ecrnx_sdio_register_drv(void)
+{
+ return eswin_sdio_init();
+}
+
+void ecrnx_sdio_unregister_drv(void)
+{
+ return eswin_sdio_exit();
+}
+
+//module_init(eswin_sdio_init);
+//module_exit(eswin_sdio_exit);
+
+//MODULE_AUTHOR("Transa-Semi");
+//MODULE_LICENSE("Dual BSD/GPL");
+//MODULE_DESCRIPTION("Driver support for Transa-Semi 802.11 WLAN SDIO driver");
+//MODULE_SUPPORTED_DEVICE("Transa-Semi 802.11 devices");
+