diff options
Diffstat (limited to 'drivers/char/mux_sdio_opt1_hal')
| -rw-r--r-- | drivers/char/mux_sdio_opt1_hal/Makefile | 1 | ||||
| -rw-r--r-- | drivers/char/mux_sdio_opt1_hal/ipc_dev.c | 247 | ||||
| -rw-r--r-- | drivers/char/mux_sdio_opt1_hal/ipc_gpio.c | 168 | ||||
| -rw-r--r-- | drivers/char/mux_sdio_opt1_hal/ipc_gpio.h | 48 | ||||
| -rw-r--r-- | drivers/char/mux_sdio_opt1_hal/ipc_info.c | 246 | ||||
| -rw-r--r-- | drivers/char/mux_sdio_opt1_hal/ipc_info.h | 52 | ||||
| -rw-r--r-- | drivers/char/mux_sdio_opt1_hal/ipc_sdio.c | 1800 |
7 files changed, 2562 insertions, 0 deletions
diff --git a/drivers/char/mux_sdio_opt1_hal/Makefile b/drivers/char/mux_sdio_opt1_hal/Makefile new file mode 100644 index 00000000..79ec33ce --- /dev/null +++ b/drivers/char/mux_sdio_opt1_hal/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_MUX_SDIO_OPT1_HAL) += ipc_gpio.o ipc_sdio.o ipc_info.o ipc_dev.o diff --git a/drivers/char/mux_sdio_opt1_hal/ipc_dev.c b/drivers/char/mux_sdio_opt1_hal/ipc_dev.c new file mode 100644 index 00000000..191166b6 --- /dev/null +++ b/drivers/char/mux_sdio_opt1_hal/ipc_dev.c @@ -0,0 +1,247 @@ +/*************************************************************** + A globalmem driver as an example of char device drivers + + The initial developer of the original code is Baohua Song + <author@linuxdriver.cn>. All Rights Reserved. +****************************************************************/ +#include <linux/module.h> +#include <linux/types.h> +#include <linux/fs.h> +#include <linux/errno.h> +#include <linux/mm.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/cdev.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <asm/io.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include "ipc_info.h" + +#define MCOM_FIFO_SIZE 0x1000 /*全局内存最大4K字节*/ + +/*globalmem设备结构体*/ +struct mcom_dev { + dev_t devno ; + struct cdev cdev; /*cdev结构体*/ + char buf[MCOM_FIFO_SIZE]; /*全局内存*/ +}; + +struct mcom_dev *mcom_devp; /*设备结构体指针*/ + +/*文件打开函数*/ +int mcom_open(struct inode *inode, struct file *filp) +{ + /*将设备结构体指针赋值给文件私有数据指针*/ + filp->private_data = mcom_devp; + printk("%s[%d] %s\r\n", __FILE__, __LINE__, __func__); + return 0; +} + +/*文件释放函数*/ +int mcom_release(struct inode *inode, struct file *filp) +{ + return 0; +} + + +const char* ipc_info_str[] = { + "IDLE", + "IRQ_IND", + "CONNECT_REQ", + "CONNECTED", + "DISCONNECT_REQ", + "DISCONNECTED", + "IPC_STATUS_FLOW_STOP", + "IPC_STATUS_INVALID_PACKET", + "CRC_ERROR", + "PACKET_ERROR", + "INVAILD" +}; + + + +const char* _get_status_string(u32 status) +{ + if(status >= IPC_STATUS_INVAILD) { + return ipc_info_str[IPC_STATUS_INVAILD]; + } + + return ipc_info_str[status]; +} + + +#define MAX_MCOM_DELAY_SECOND 3 +#define MAX_MCOM_READ_DELAY_MS (MAX_MCOM_DELAY_SECOND*1000) + +static u32 s_write_sdio_count = 0; +static u32 s_read_sdio_count = 0; +static u32 s_write_payload_count = 0; +static u32 s_read_payload_count = 0; + +static u32 s_prv_read_time = 0; + + +/*读函数*/ +static ssize_t mcom_read(struct file *filp, char __user *buf, size_t size,loff_t *ppos) +{ + int ret = 0; + int len = 0; + int speed = 0; + int payload_speed = 0; + struct mcom_dev *dev = filp->private_data; /*获得设备结构体指针*/ + char* buf_ptr = &dev->buf[0]; + IPC_INFO_T* ipc_ptr = NULL; + + msleep(MAX_MCOM_READ_DELAY_MS); + + len = sprintf(buf_ptr, "\r\n\t\tphy_speed\tpayload_speed\t\trate\tmux\t\tsdio\t\t saved\tflow_stop\tinvalid\tovfl\tperr\tcerr\tstatus\r\n"); + + ret = len; + + ipc_ptr = ipc_info_getinfo(IPC_TX_CHANNEL); + + if(!s_prv_read_time) { + speed = 0; + payload_speed = 0; + } else { + speed = (ipc_ptr->sdio_count - s_write_sdio_count)/(jiffies_to_msecs(jiffies) - jiffies_to_msecs(s_prv_read_time))*1000; + payload_speed = (ipc_ptr->payload_count - s_write_payload_count)/(jiffies_to_msecs(jiffies) - jiffies_to_msecs(s_prv_read_time))*1000; + } + + len = sprintf(&buf_ptr[ret], "MCOM Write:\t%u\t\t%u\t\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%s\r\n", \ + speed,payload_speed, ipc_ptr->rate, ipc_ptr->mux_count,ipc_ptr->sdio_count, ipc_ptr->saved_count, + ipc_ptr->flow_stop_count,ipc_ptr->invalid_pkt_count, ipc_ptr->overflow_count, ipc_ptr->packet_error_count, \ + ipc_ptr->crc_error_count,_get_status_string(ipc_ptr->status)); + + ipc_ptr->rate = 0; + s_write_sdio_count = ipc_ptr->sdio_count; + s_write_payload_count = ipc_ptr->payload_count; + ret += len; + + ipc_ptr = ipc_info_getinfo(IPC_RX_CHANNEL); + + if(!s_prv_read_time) { + speed = 0; + payload_speed = 0; + } else { + speed = (ipc_ptr->sdio_count - s_read_sdio_count)/(jiffies_to_msecs(jiffies) - jiffies_to_msecs(s_prv_read_time))*1000; + payload_speed = (ipc_ptr->payload_count - s_read_payload_count)/(jiffies_to_msecs(jiffies) - jiffies_to_msecs(s_prv_read_time))*1000; + } + + len = sprintf(&buf_ptr[ret], "MCOM Read:\t%u\t\t%u\t\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%s\r\n", + speed, payload_speed, ipc_ptr->rate,ipc_ptr->mux_count,ipc_ptr->sdio_count, ipc_ptr->saved_count, + ipc_ptr->flow_stop_count, ipc_ptr->invalid_pkt_count, ipc_ptr->overflow_count, ipc_ptr->packet_error_count, + ipc_ptr->crc_error_count, _get_status_string(ipc_ptr->status)); + + s_read_sdio_count = ipc_ptr->sdio_count; + s_read_payload_count = ipc_ptr->payload_count; + ipc_ptr->rate = 0; + + ret += len; + + buf_ptr[ret] = 0; + + s_prv_read_time = jiffies; + + len = (ret < size) ? ret : size; + + /*内核空间->用户空间*/ + if (copy_to_user(buf, (void*)buf_ptr, len)) { + len = - EFAULT; + } + + return len; +} + +/*写函数*/ +static ssize_t mcom_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos) +{ + + return - EFAULT; +} + + + +/*文件操作结构体*/ +static const struct file_operations mcom_fops = { + .owner = THIS_MODULE, + .read = mcom_read, + .open = mcom_open, + .release = mcom_release, +}; + +static char *mcom_devnode(struct device *dev, mode_t *mode) +{ + return kasprintf(GFP_KERNEL, "mcom/%s", dev_name(dev)); +} + + + +static struct class *mcom_class; +static int mcom_major = 0; +/*设备驱动模块加载函数*/ +int __init mcom_init(void) +{ + int err = 0; + int result; + dev_t devno ; + + /* 申请设备号*/ + result = alloc_chrdev_region(&devno, 0, 1, "mcom"); + mcom_major = MAJOR(devno); + if (result < 0) + return result; + + /* 动态申请设备结构体的内存*/ + mcom_devp = kmalloc(sizeof(struct mcom_dev), GFP_KERNEL); + if (!mcom_devp) { /*申请失败*/ + result = - ENOMEM; + goto fail_malloc; + } + + memset(mcom_devp, 0, sizeof(struct mcom_dev)); + + /*初始化并注册cdev*/ + cdev_init(&mcom_devp->cdev, &mcom_fops); + mcom_devp->cdev.owner = THIS_MODULE; + mcom_devp->cdev.ops = &mcom_fops; + err = cdev_add(&mcom_devp->cdev, devno, 1); + if (err) + printk(KERN_NOTICE "%s:%sError %d\r\n",__FILE__, __func__, err); + + mcom_devp->devno = devno; + + mcom_class = class_create(THIS_MODULE, "mcom"); + if (IS_ERR(mcom_class)) { + printk(KERN_ERR "Error creating mcom class.\n"); + cdev_del(&mcom_devp->cdev); + goto fail_malloc; + } + mcom_class->devnode = mcom_devnode; + device_create(mcom_class, NULL, devno, NULL, "mcom"); + + return 0; + +fail_malloc: + unregister_chrdev_region(devno, 1); + return result; +} + +/*模块卸载函数*/ +void __init mcom_exit(void) +{ + cdev_del(&mcom_devp->cdev); /*注销cdev*/ + kfree(mcom_devp); /*释放设备结构体内存*/ + unregister_chrdev_region(&mcom_devp->devno, 1); /*释放设备号*/ +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Harald Welte <laforge@openezx.org>"); +MODULE_DESCRIPTION("GSM TS 07.10 Multiplexer"); + +module_init(mcom_init); +module_exit(mcom_exit); + diff --git a/drivers/char/mux_sdio_opt1_hal/ipc_gpio.c b/drivers/char/mux_sdio_opt1_hal/ipc_gpio.c new file mode 100644 index 00000000..048683d4 --- /dev/null +++ b/drivers/char/mux_sdio_opt1_hal/ipc_gpio.c @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2012 Spreadtrum Communications Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/irq.h> +#include <linux/printk.h> +#include <linux/sched.h> +#include <linux/wakelock.h> +#include <linux/bitops.h> +#include <mach/board.h> +#include "ipc_gpio.h" +#include <linux/proc_fs.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/dma-mapping.h> + +#define SDHCI_RESEND_SUPPORT + +#define PIN_STATUS_ACTIVE 1 +#define PIN_STATUS_INACTIVE 0 + + +#define DRIVER_NAME "sdhci_hal" + +#define DBG(f, x...) pr_debug(DRIVER_NAME " [%s()]: " f, __func__,## x) + +#define MAX_MIPC_WAIT_TIMEOUT 2000 + +static unsigned int cp_to_ap_req_irq; + + +static wait_queue_head_t s_gpio_cp_ready_wq; + +extern void process_modem_packet(unsigned long data); + +int cp2ap_req(void) +{ + DBG("[SDIO_IPC] cp_to_ap_req=%d\r\n",gpio_get_value(GPIO_CP_TO_AP_RDY)); + return (PIN_STATUS_ACTIVE == gpio_get_value(GPIO_CP_TO_AP_RDY)); +} + +void ap2cp_req_enable(void) +{ + DBG("SDIO ap2cp_req_enable AP_TO_CP_RTS is 1\r\n"); + gpio_set_value(GPIO_AP_TO_CP_RTS, PIN_STATUS_ACTIVE); +} + +void ap2cp_req_disable(void) +{ + DBG("SDIO ap2cp_req_disable AP_TO_CP_RTS is 0\r\n"); + gpio_set_value(GPIO_AP_TO_CP_RTS, PIN_STATUS_INACTIVE); +} + +extern u32 wake_up_mipc_rx_thread(u32 need_to_rx_data); +extern void set_cp_awake(bool awake); + + +static irqreturn_t cp_to_ap_req_handle(int irq, void *handle) +{ + int cur = cp2ap_req(); + + if(cur) { + irq_set_irq_type(cp_to_ap_req_irq, IRQ_TYPE_LEVEL_LOW); + } else { + irq_set_irq_type(cp_to_ap_req_irq, IRQ_TYPE_LEVEL_HIGH); + } + + DBG("[SDIO_IPC]cp_to_ap_req_handle:%d\r\n ", cur); + + if(cur) { + set_cp_awake(true); + wake_up_mipc_rx_thread(1); + } + return IRQ_HANDLED; +} + +/*****************************************************************************\ + * * + * Driver init/exit * + * * +\*****************************************************************************/ + +int sdhci_hal_gpio_init(void) +{ + int ret; + + DBG("sdhci_hal_gpio init \n"); + + init_waitqueue_head(&s_gpio_cp_ready_wq); + + //config ap_to_cp_rts + ret = gpio_request(GPIO_AP_TO_CP_RTS, "ap_to_cp_rts"); + if (ret) { + DBG("Cannot request GPIO %d\r\n", GPIO_AP_TO_CP_RTS); + gpio_free(GPIO_AP_TO_CP_RTS); + return ret; + } + gpio_direction_output(GPIO_AP_TO_CP_RTS, 1); //use new sdio dma option, default value change to 1 + gpio_export(GPIO_AP_TO_CP_RTS, 1); + + + //config cp_out_rdy + ret = gpio_request(GPIO_CP_TO_AP_RDY, "cp_out_rdy"); + if (ret) { + DBG("Cannot request GPIO %d\r\n",GPIO_CP_TO_AP_RDY); + gpio_free(GPIO_CP_TO_AP_RDY); + return ret; + } + gpio_direction_input(GPIO_CP_TO_AP_RDY); + gpio_export(GPIO_CP_TO_AP_RDY, 1); + + return 0; +} + +int sdhci_hal_gpio_irq_init(void) +{ + int ret; + + DBG("sdhci_hal_gpio init \n"); + + cp_to_ap_req_irq = gpio_to_irq(GPIO_CP_TO_AP_RDY); + if (cp_to_ap_req_irq < 0) + return -1; + ret = request_threaded_irq(cp_to_ap_req_irq, cp_to_ap_req_handle, + NULL, IRQF_NO_SUSPEND|IRQF_DISABLED|IRQF_TRIGGER_HIGH, + "cp_to_ap_req_irq", NULL); + if (ret) { + DBG("lee :cannot alloc cp_to_ap_req_irq, err %d\r\r\n", ret); + return ret; + } + enable_irq_wake(cp_to_ap_req_irq); + if(cp2ap_req()) { + printk("[SDIO_IPC]initial check call cp_to_ap_req_handle()\r\n "); + cp_to_ap_req_handle(0, NULL); + } + return 0; + +} + +void sdhci_hal_gpio_exit(void) +{ + DBG("sdhci_hal gpio exit\r\n"); + + gpio_free(GPIO_AP_TO_CP_RTS); + gpio_free(GPIO_CP_TO_AP_RDY); + + free_irq(cp_to_ap_req_irq, NULL); +} + +//MODULE_AUTHOR("Bin.Xu<bin.xu@spreadtrum.com>"); +//MODULE_DESCRIPTION("GPIO driver about mux sdio"); +//MODULE_LICENSE("GPL"); diff --git a/drivers/char/mux_sdio_opt1_hal/ipc_gpio.h b/drivers/char/mux_sdio_opt1_hal/ipc_gpio.h new file mode 100644 index 00000000..46c12ace --- /dev/null +++ b/drivers/char/mux_sdio_opt1_hal/ipc_gpio.h @@ -0,0 +1,48 @@ +/* + * linux/drivers/mmc/host/sdio_hal.h - Secure Digital Host Controller Interface driver + * + * Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ +#ifndef __SDHCI_HAL_H +#define __SDHCI_HAL_H + +#include <linux/scatterlist.h> +#include <linux/compiler.h> +#include <linux/types.h> +#include <linux/io.h> +#include <linux/bitops.h> + +#define SDHCI_TRANSFER_DIRECT_WRITE 0 +#define SDHCI_TRANSFER_DIRECT_READ 1 + +#define SDHCI_TRANSFER_OK 0 +#define SDHCI_TRANSFER_ERROR 1 +#define SDHCI_TRANSFER_TIMEOUT 2 + +#define SDHCI_SUCCESS 0 +#define SDHCI_ERROR 1 +#define SDHCI_TIMEOUT 2 + +int sdhci_hal_gpio_init(void); +int sdhci_hal_gpio_irq_init(void); +void sdhci_hal_gpio_exit(void); + +int cp2ap_req(void); +void set_ap_status(void); +void clear_ap_status(void); +int cp2ap_rts(void); +void ap2cp_req_enable(void); +void ap2cp_req_disable(void); +void ap2cp_rdy_enable(void); +void ap2cp_rdy_disable(void); +u32 sdhci_connect(u32 direction); +u32 sdhci_disconnect(u32 status); +u32 sdhci_resetconnect(u32 status); +void check_cp2ap_rdy(void); + +#endif /* __SDHCI_H */ diff --git a/drivers/char/mux_sdio_opt1_hal/ipc_info.c b/drivers/char/mux_sdio_opt1_hal/ipc_info.c new file mode 100644 index 00000000..a5584a80 --- /dev/null +++ b/drivers/char/mux_sdio_opt1_hal/ipc_info.c @@ -0,0 +1,246 @@ +/*************************************************************** + A globalmem driver as an example of char device drivers + + The initial developer of the original code is Baohua Song + <author@linuxdriver.cn>. All Rights Reserved. +****************************************************************/ +#include <linux/types.h> +#include <linux/slab.h> +#include "ipc_info.h" + +extern u32 mux_ipc_GetTxTransferSavedCount(void); + +IPC_INFO_T s_ipc_info[IPC_CHANNEL_NUM]; + + + +#include "./../ts0710.h" +#include "./../ts0710_mux.h" + +/*For BP UART problem Begin*/ +#ifdef TS0710SEQ2 +#define FIRST_BP_SEQ_OFFSET 0 /*offset from start flag */ +#define SECOND_BP_SEQ_OFFSET 0 /*offset from start flag */ +#define FIRST_AP_SEQ_OFFSET 0 /*offset from start flag */ +#define SECOND_AP_SEQ_OFFSET 0 /*offset from start flag */ +#define SLIDE_BP_SEQ_OFFSET 0 /*offset from start flag */ +#define SEQ_FIELD_SIZE 0 +#else +#define SLIDE_BP_SEQ_OFFSET 1 /*offset from start flag */ +#define SEQ_FIELD_SIZE 0 +#endif + +#define ADDRESS_FIELD_OFFSET (1 + SEQ_FIELD_SIZE) /*offset from start flag */ + +#define TS0710MUX_MAX_TOTAL_FRAME_SIZE (DEF_TS0710_MTU + TS0710_MAX_HDR_SIZE + FLAG_SIZE) + +extern __u32 mux_crc_check(__u8 * data, __u32 length, __u8 check_sum); + +int sdio_frame_check(char* buf_ptr, size_t length) +{ + __u8* frame_start_ptr = NULL; + __u8* end_ptr = NULL; + short_frame* short_pkt = NULL; + long_frame* long_pkt = NULL; + int framelen = 0; + __u32 crc_error = 0; + __u8 *uih_data_start = NULL; + __u32 uih_len = 0; + int ret = 0; + + frame_start_ptr = (__u8*)buf_ptr; + end_ptr = (__u8*)buf_ptr + length; + + /*while(frame_start_ptr < end_ptr) + { + short_pkt = NULL; + long_pkt = NULL; + crc_error = 0; + framelen = 0; + uih_data_start = NULL; + uih_len = 0; + + if(TS0710_BASIC_FLAG != *frame_start_ptr) + { + printk("%s:Error,mux frame head error!\r\n", __func__); + ret = -1; + break; + } + + short_pkt = (short_frame *) (frame_start_ptr + ADDRESS_FIELD_OFFSET); + + if(short_pkt->h.length.ea == 1) //short frame + { + framelen = TS0710_MAX_HDR_SIZE + short_pkt->h.length.len + 1 + SEQ_FIELD_SIZE; + } + else + { + long_pkt = (long_frame *) (frame_start_ptr + ADDRESS_FIELD_OFFSET); + framelen = TS0710_MAX_HDR_SIZE + GET_LONG_LENGTH(long_pkt->h.length) + 2 + SEQ_FIELD_SIZE; + } + + if (framelen > (TS0710MUX_MAX_TOTAL_FRAME_SIZE + SEQ_FIELD_SIZE)) + { + printk("%s:Error,mux frame length:%d is bigger than Max frame size:%d\r\n", + __func__, framelen, (TS0710MUX_MAX_TOTAL_FRAME_SIZE + SEQ_FIELD_SIZE)); + ret = -1; + break; + } + + if (*(frame_start_ptr + framelen - 1) != TS0710_BASIC_FLAG) + { + int k = 0; + printk("%s:Error,mux frame end error!, framelen start addr:0x%x, framelen:%d, offset:%d, \r\n", + __func__, (__u32)frame_start_ptr, framelen, (__u32)frame_start_ptr - (__u32)buf_ptr); + + printk("\r\n =================\r\n0x%X,0x%X,0x%X,0x%X,0x%X,0x%X,0x%X,0x%X\r\n================\r\n", + frame_start_ptr[framelen -8],frame_start_ptr[framelen -7],frame_start_ptr[framelen -6],frame_start_ptr[framelen -5], + frame_start_ptr[framelen -4],frame_start_ptr[framelen -3],frame_start_ptr[framelen -2],frame_start_ptr[framelen -1]); + + ret = -1; + break; + } + + + if ((short_pkt->h.length.ea) == 1) //short frame + { + uih_len = short_pkt->h.length.len; + uih_data_start = short_pkt->data; + crc_error = mux_crc_check((__u8*) (frame_start_ptr + SLIDE_BP_SEQ_OFFSET),SHORT_CRC_CHECK + 1, *(uih_data_start + uih_len)); + } + else + { + uih_len = GET_LONG_LENGTH(long_pkt->h.length); + uih_data_start = long_pkt->h.data; + crc_error =mux_crc_check((__u8*) (frame_start_ptr +SLIDE_BP_SEQ_OFFSET),LONG_CRC_CHECK + 1,*(uih_data_start +uih_len)); + } + + if(crc_error) + { + printk("%s:Error,mux frame crc error!\r\n", __func__); + ret = -1; + break; + } + + frame_start_ptr += framelen; + + if(frame_start_ptr > end_ptr) + { + printk("%s:Error,mux frame data error!\r\n", __func__); + ret = -1; + break; + } + }*/ + + return ret; +} + + + +void ipc_info_init(void) +{ + memset(&s_ipc_info[0], 0, sizeof(IPC_INFO_T)*IPC_CHANNEL_NUM); +} + + +void ipc_info_rate(u32 channel, u32 value) +{ + if(channel >= IPC_CHANNEL_NUM) { + return; + } + + s_ipc_info[channel].rate = value; +} + +void ipc_info_change_status(u32 channel, u32 status) +{ + if(channel >= IPC_CHANNEL_NUM) { + return; + } + + s_ipc_info[channel].status = status; +} + + +void ipc_info_mux_write(u32 size) +{ + s_ipc_info[IPC_TX_CHANNEL].mux_count += size; +} + + +void ipc_info_mux_read(u32 size) +{ + s_ipc_info[IPC_RX_CHANNEL].mux_count += size; +} + +void ipc_info_mux_read_overflow(u32 size) +{ + s_ipc_info[IPC_RX_CHANNEL].overflow_count += size; +} + + +void ipc_info_sdio_write(u32 size) +{ + s_ipc_info[IPC_TX_CHANNEL].sdio_count += size; +} + + +void ipc_info_sdio_write_payload(u32 size) +{ + s_ipc_info[IPC_TX_CHANNEL].payload_count += size; +} + +void ipc_info_sdio_write_overflow(u32 size) +{ + s_ipc_info[IPC_TX_CHANNEL].overflow_count += size; +} + +void ipc_info_sdio_read(u32 size) +{ + s_ipc_info[IPC_RX_CHANNEL].sdio_count += size; +} + +void ipc_info_sdio_read_payload(u32 size) +{ + s_ipc_info[IPC_RX_CHANNEL].payload_count += size; +} + + + +void ipc_info_sdio_read_saved_count(u32 size) +{ + s_ipc_info[IPC_RX_CHANNEL].saved_count = size; +} + +void ipc_info_error_status(u32 channel, u32 status) +{ + if(channel >= IPC_CHANNEL_NUM) { + return; + } + + s_ipc_info[channel].status = status; + + if (IPC_STATUS_FLOW_STOP == status) { + s_ipc_info[channel].flow_stop_count++; + } else if (IPC_STATUS_INVALID_PACKET == status) { + s_ipc_info[channel].invalid_pkt_count++; + } else if (IPC_STATUS_CRC_ERROR == status) { + s_ipc_info[channel].crc_error_count++; + } else if (IPC_STATUS_PACKET_ERROR == status) { + s_ipc_info[channel].packet_error_count++; + } +} + + +IPC_INFO_T* ipc_info_getinfo(u32 channel) +{ + if(channel >= IPC_CHANNEL_NUM) { + return NULL; + } + s_ipc_info[IPC_TX_CHANNEL].saved_count = mux_ipc_GetTxTransferSavedCount(); + return &s_ipc_info[channel]; +} + + + + diff --git a/drivers/char/mux_sdio_opt1_hal/ipc_info.h b/drivers/char/mux_sdio_opt1_hal/ipc_info.h new file mode 100644 index 00000000..f603440a --- /dev/null +++ b/drivers/char/mux_sdio_opt1_hal/ipc_info.h @@ -0,0 +1,52 @@ +#ifndef __ipc_info_H__ +#define __ipc_info_H__ + +#define IPC_STATUS_IDLE 0 +#define IPC_STATUS_IRQ_IND 1 +#define IPC_STATUS_CONNECT_REQ 2 +#define IPC_STATUS_CONNECTED 3 +#define IPC_STATUS_DISCONNECT_REQ 4 +#define IPC_STATUS_DISCONNECTED 5 +#define IPC_STATUS_FLOW_STOP 6 +#define IPC_STATUS_INVALID_PACKET 7 +#define IPC_STATUS_CRC_ERROR 8 +#define IPC_STATUS_PACKET_ERROR 9 +#define IPC_STATUS_INVAILD 10 + + +#define IPC_TX_CHANNEL 0 +#define IPC_RX_CHANNEL 1 +#define IPC_CHANNEL_NUM 2 + +typedef struct IPC_INFO_Tag { + u32 status; + u32 rate; + u32 mux_count; + u32 sdio_count; + u32 payload_count; + u32 saved_count; + u32 flow_stop_count; + u32 invalid_pkt_count; + u32 packet_error_count; + u32 crc_error_count; + u32 overflow_count; +} IPC_INFO_T; + + +void ipc_info_init(void); +void ipc_info_change_status(u32 channel, u32 status); +void ipc_info_rate(u32 channel, u32 value); +void ipc_info_mux_write(u32 size); +void ipc_info_mux_read(u32 size); +void ipc_info_mux_read_overflow(u32 size); +void ipc_info_sdio_write(u32 size); +void ipc_info_sdio_write_payload(u32 size); +void ipc_info_sdio_write_overflow(u32 size); +void ipc_info_sdio_read(u32 size); +void ipc_info_sdio_read_payload(u32 size); +void ipc_info_sdio_read_saved_count(u32 size); +void ipc_info_error_status(u32 channel, u32 status); +void ipc_info_sdio_write_overflow(u32 size); +IPC_INFO_T* ipc_info_getinfo(u32 channel); +int sdio_frame_check(char* buf_ptr, size_t length); +#endif /* __MICP_PERF_H__*/ diff --git a/drivers/char/mux_sdio_opt1_hal/ipc_sdio.c b/drivers/char/mux_sdio_opt1_hal/ipc_sdio.c new file mode 100644 index 00000000..d7977864 --- /dev/null +++ b/drivers/char/mux_sdio_opt1_hal/ipc_sdio.c @@ -0,0 +1,1800 @@ +/* + * Copyright (C) 2012 Spreadtrum Communications Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + + +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <linux/list.h> +#include <linux/wakelock.h> +#include <linux/semaphore.h> +#include <linux/wait.h> +#include <linux/sched.h> +#include <linux/kthread.h> +#include <linux/slab.h> +#include <linux/io.h> +#include "ipc_gpio.h" +//#include <mach/modem_interface.h> +#include <linux/sprdmux.h> +#include <linux/mmc/sdio_channel.h> +#include <linux/kfifo.h> +#include "ipc_info.h" +#define MUX_IPC_READ_DISABLE 0x01 +#define MUX_IPC_WRITE_DISABLE 0x02 + + + +//#define SDIO_READ_DEBUG +//#define SDIO_LOOPBACK_TEST +//#define SDIO_DEBUG_ENABLE + +//#define SDIO_LOOP_TEST +#define SDIO_VARIABLE_DL_LEN +//#define SC9620_JTAG_DL_MOD +#define HEADER_TAG 0x7e7f +#define HEADER_TYPE 0xaa00 + +#define IPC_DRIVER_NAME "IPC_SDIO" + +#define MAX_SDIO_TX_WAIT_TIMEOUT 100 + +#define MAX_SDIO_CP_AWAKE_TIMEOUT 500 +#define MAX_SDIO_SLEEP_TIMEOUT 50 +#define MAX_SDIO_CP_ACK_TIMEOUT 1000 +#define MAX_SDIO_TX_WATERMARK 4 +#define MAX_SDIO_RX_RETRY 3 +#define MAX_SDIO_TX_RETRY 3 + +#define SDIO_GPIO_RES_FREE 0 +#define SDIO_GPIO_RES_USING 1 + + +#define MAX_MIPC_RX_FRAME_SIZE (64*1024) +#define MAX_MIPC_TX_FRAME_SIZE (32*1024) + +#define MIN_MIPC_TX_FRAME_SIZE (4 * 1024) + +#define MAX_MIPC_RX_CACHE_SIZE MAX_MIPC_RX_FRAME_SIZE*4 + +//#define MAX_MIPC_TX_FIFO_SIZE (1 << 7) // 128 +#define MAX_MIPC_TX_FIFO_SIZE (1 << 3) // 16 + + +#define MAX_MIPC_TX_FRAME_NUM (MAX_MIPC_TX_FIFO_SIZE - 1) + +#ifdef SDIO_VARIABLE_DL_LEN +#define DEFAULT_RX_FRAME_SIZE (4*1024) +#else +#define DEFAULT_RX_FRAME_SIZE MAX_MIPC_RX_FRAME_SIZE +#endif + +#define NEXT_TX_FIFO_IDX(idx) (((idx) + 1) & (MAX_MIPC_TX_FIFO_SIZE - 1)) + +#define MIPC_TX_REQ_FLAG ((s_mipc_tx_ctrl.fifo_wr != s_mipc_tx_ctrl.fifo_rd) && s_tx_flow_info) + + + +//#define IPC_DBG(f, x...) pr_debug(IPC_DRIVER_NAME " [%s()]: " f, __func__,## x) +#define IPC_DBG printk + +/* Log Macros */ +#define IPC_SDIO_INFO(fmt...) pr_debug("IPC_SDIO: " fmt) +#define IPC_SDIO_DEBUG(fmt...) pr_debug("IPC_SDIO: " fmt) +#define IPC_SDIO_WARN(fmt...) pr_warn("IPC_SDIO: " fmt) +#define IPC_SDIO_ERR(fmt...) pr_err("IPC_SDIO: Error: " fmt) + + + +struct packet_header { + u16 tag; + u16 flag; + u32 length; + u32 frame_num; + union { + u32 emergency_cmd; + u32 ul_frame_num; + } u; + u32 next_length; + u32 reserved[3]; +} __packed; + +typedef struct MIPC_TRANSF_FRAME_Tag { + u8* buf_ptr; + u32 buf_size; + u32 pos; + u32 flag; + struct list_head link; +} MIPC_TRANSF_FRAME_T; + +typedef struct MIPC_FRAME_LIST_Tag { + struct list_head frame_list_head; + struct mutex list_mutex; + u32 counter; +} MIPC_FRAME_LIST_T; + +typedef struct MIPC_TRANSFER_Tag { + struct mutex transfer_mutex; + struct list_head frame_fifo; + wait_queue_head_t frame_free_wq; + MIPC_TRANSF_FRAME_T* cur_frame_ptr; + u32 frame_count; + u32 counter; +} MIPC_TRANSFER_T; + +typedef struct CP_AWAKE_STATUS_Tag { + u32 ack_time; + bool awake; + spinlock_t lock; + wait_queue_head_t wait; +} CP_AWAKE_STATUS_T; + +typedef struct MIPC_TX_CTRL_Tag { + MIPC_TRANSF_FRAME_T* fifo[MAX_MIPC_TX_FIFO_SIZE]; + volatile u32 fifo_rd; + volatile u32 fifo_wr; + u32 fifo_ack; + u32 last_ack_frame; +} MIPC_TX_CTRL_T; + +static struct kfifo s_mipc_rx_cache_kfifo; + +u8* s_mipc_rx_buf = NULL; +u32 s_mipc_next_rx_len = DEFAULT_RX_FRAME_SIZE; + + +MIPC_TRANSF_FRAME_T s_tx_transfer_frame[MAX_MIPC_TX_FRAME_NUM]; + +static struct wake_lock s_ipc_sdio_wake_lock; +static struct wake_lock s_cp_req_wake_lock; + + +static struct mutex ipc_mutex; +wait_queue_head_t s_mux_read_rts; +wait_queue_head_t s_rx_fifo_avail; +wait_queue_head_t s_modem_ready; +wait_queue_head_t s_sdio_gpio_res_free; + + +u32 s_mux_ipc_event_flags = 0; + +static struct task_struct *s_mux_ipc_rx_thread; +static struct task_struct *s_mux_ipc_tx_thread; +static struct task_struct *s_mux_ipc_sdio_thread; + + +static int sdio_tx_wait_time = 1; +MIPC_FRAME_LIST_T s_mipc_tx_free_frame_list; +MIPC_TRANSFER_T s_mipc_tx_tansfer; + +static u32 s_mux_ipc_enable = 0; +static u32 s_sdio_gpio_res_status = SDIO_GPIO_RES_FREE; + +static wait_queue_head_t s_mux_ipc_tx_wq; +static wait_queue_head_t s_mux_ipc_rx_wq; + + + +static u32 s_mux_ipc_module_inited = 0; + +static int sdio_transfer_crc_check_enable = 0; +static int sdio_transfer_frame_check_enable = 0; + +u32 s_mipc_rx_event_flags = 0; + +static wait_queue_head_t s_mux_ipc_sdio_wq; +static u32 s_mipc_rx_req_flag = 0; +static u32 s_mipc_enable_change_flag = 0; + +static CP_AWAKE_STATUS_T s_cp_awake_status; +//static u32 s_mipc_tx_above_watermark = 0; +static u32 s_tx_flow_info = 0xFF; +static u32 s_acked_tx_frame = 0; +static u32 s_last_tx_frame = 0; +static u32 s_tx_frame_counter; //tx frame number start from 1 + +static MIPC_TX_CTRL_T s_mipc_tx_ctrl; + +//static atomic_t s_mipc_read_pending; + + + +static DEFINE_MUTEX(sdio_tx_lock); +static int mux_ipc_xmit_buf(const char *buf, ssize_t len); +static int mux_ipc_sdio_write( const char *buf, size_t count); +static int mux_ipc_sdio_read( char *buf, size_t count); +static u32 _FreeAllTxTransferFrame(void); +static void _FreeFrame(MIPC_TRANSF_FRAME_T* frame_ptr, MIPC_FRAME_LIST_T* frame_list_ptr); +static u32 _FlushTxTransfer(void); +static void _WaitTxTransferFree(void); +static void _WakeTxTransfer(void); + + + +#include<mach/hardware.h> + +/* use this method we can get time everytime */ +#define TIMER_REG(off) (SPRD_TIMER_BASE + (off)) +#define SYSCNT_REG(off) (SPRD_SYSCNT_BASE + (off)) +#define SYSCNT_COUNT SYSCNT_REG(0x0004) + +/* no need double-reading */ +#define SYSCNT_SHADOW_COUNT SYSCNT_REG(0x000c) + +#if 0 +static u32 inline get_sys_cnt(void) +{ +#if defined(CONFIG_ARCH_SC7710) + return __raw_readl(SYSCNT_SHADOW_COUNT); +#else + u32 val1, val2; + val1 = __raw_readl(SYSCNT_COUNT); + val2 = __raw_readl(SYSCNT_COUNT); + while(val2 != val1) { + val1 = val2; + val2 = __raw_readl(SYSCNT_COUNT); + } + return val2; +#endif +} +#endif + +static u32 _is_mux_ipc_enable(void) +{ +#ifdef SC9620_JTAG_DL_MOD + return 1; +#else + return s_mux_ipc_enable; +#endif +} + + +void wait_cp_bootup(void) +{ + wait_event(s_modem_ready, _is_mux_ipc_enable()); +} + +void init_cp_awake_status(void) +{ + s_cp_awake_status.ack_time = 0; + s_cp_awake_status.awake = false; + spin_lock_init(&s_cp_awake_status.lock); + init_waitqueue_head(&s_cp_awake_status.wait); +} + +void set_cp_awake(bool awake) +{ + unsigned long flags; + bool last_status; + + spin_lock_irqsave(&s_cp_awake_status.lock, flags); + last_status = s_cp_awake_status.awake; + s_cp_awake_status.awake = awake; + if(s_cp_awake_status.awake) { + s_cp_awake_status.ack_time = jiffies; + } + spin_unlock_irqrestore(&s_cp_awake_status.lock, flags); + + if(!last_status && awake) { + wake_up(&s_cp_awake_status.wait); + } +} + +bool get_cp_awake(void) +{ + unsigned long flags; + bool ret = false; + + spin_lock_irqsave(&s_cp_awake_status.lock, flags); + if(s_cp_awake_status.awake) { + unsigned long diff = (long)jiffies - (long)s_cp_awake_status.ack_time; + + if(jiffies_to_msecs(diff) > MAX_SDIO_CP_AWAKE_TIMEOUT) { + s_cp_awake_status.awake = false; + } + } + ret = s_cp_awake_status.awake; + spin_unlock_irqrestore(&s_cp_awake_status.lock, flags); + + return ret; +} + +bool wait_cp_awake(unsigned long timeout) +{ + if(get_cp_awake()) { + return true; + } + if(!wait_event_timeout(s_cp_awake_status.wait, s_cp_awake_status.awake, + timeout)) { + IPC_SDIO_ERR("[MIPC] SDIO wait cp awake for a long time!\r\n"); + } + + return s_cp_awake_status.awake; +} + +//MIPC_TX_CTRL_T ops +void init_mipc_tx_ctrl(void) +{ + memset(s_mipc_tx_ctrl.fifo, 0, sizeof(MIPC_TRANSF_FRAME_T*) * MAX_MIPC_TX_FIFO_SIZE); + s_mipc_tx_ctrl.fifo_rd = 0; + s_mipc_tx_ctrl.fifo_wr = 0; + s_mipc_tx_ctrl.fifo_ack = 0; + s_mipc_tx_ctrl.last_ack_frame = 0; + s_tx_frame_counter = 1; //tx frame number start from 1 +} + +void put_to_sdio_tx_fifo(MIPC_TRANSF_FRAME_T* frame) +{ + s_mipc_tx_ctrl.fifo[s_mipc_tx_ctrl.fifo_wr] = frame; + smp_mb(); + s_mipc_tx_ctrl.fifo_wr = NEXT_TX_FIFO_IDX(s_mipc_tx_ctrl.fifo_wr); + + wake_up(&s_mux_ipc_sdio_wq); +} + +MIPC_TRANSF_FRAME_T* get_from_sdio_tx_fifo(void) +{ + MIPC_TRANSF_FRAME_T* ret = NULL; + + if(s_mipc_tx_ctrl.fifo_rd != s_mipc_tx_ctrl.fifo_wr) { + smp_mb(); + ret = s_mipc_tx_ctrl.fifo[s_mipc_tx_ctrl.fifo_rd]; + smp_mb(); + s_mipc_tx_ctrl.fifo_rd = NEXT_TX_FIFO_IDX(s_mipc_tx_ctrl.fifo_rd); + } + + return ret; +} + +void ack_tx_frame_num(u32 frame_num) +{ + struct packet_header * pkt = NULL; + + while(s_mipc_tx_ctrl.fifo_ack != s_mipc_tx_ctrl.fifo_rd) { + smp_mb(); + if(s_mipc_tx_ctrl.fifo[s_mipc_tx_ctrl.fifo_ack]) { + pkt = (struct packet_header *)s_mipc_tx_ctrl.fifo[s_mipc_tx_ctrl.fifo_ack]->buf_ptr; + + if((long)(frame_num) - (long)(pkt->frame_num) >= 0) { + //calc tx payload + ipc_info_sdio_write_payload(pkt->length); + _FreeFrame(s_mipc_tx_ctrl.fifo[s_mipc_tx_ctrl.fifo_ack], &s_mipc_tx_free_frame_list); + + smp_mb(); + s_mipc_tx_ctrl.fifo_ack = NEXT_TX_FIFO_IDX(s_mipc_tx_ctrl.fifo_ack); + } else { + break; + } + } else { + IPC_SDIO_ERR("[MIPC] ERROR s_mipc_tx_ctrl.fifo[s_mipc_tx_ctrl.fifo_ack] == 0!\r\n"); + break; + } + } + s_mipc_tx_ctrl.last_ack_frame = frame_num; +} + + +void nack_tx_frame_num(void) +{ + //test + { + u32 ack, rd; + ack = s_mipc_tx_ctrl.fifo_ack; + rd = s_mipc_tx_ctrl.fifo_rd; + while(ack != rd) { + ipc_info_error_status(IPC_TX_CHANNEL, IPC_STATUS_INVALID_PACKET); + ack = NEXT_TX_FIFO_IDX(ack); + } + } + + IPC_SDIO_INFO("[mipc]before nack_tx_frame_num fifo_rd:%ld\n", s_mipc_tx_ctrl.fifo_rd); + s_mipc_tx_ctrl.fifo_rd = s_mipc_tx_ctrl.fifo_ack; + IPC_SDIO_INFO("[mipc]nack_tx_frame_num fifo_rd:%d\n", s_mipc_tx_ctrl.fifo_rd); +} + + + +bool implicit_ack_tx_frame(void) +{ + if(s_mipc_tx_ctrl.fifo_ack != s_mipc_tx_ctrl.fifo_rd) { + if(!s_mipc_tx_ctrl.fifo[s_mipc_tx_ctrl.fifo_ack]) { + IPC_SDIO_ERR("[MIPC] ERROR implicit_ack_tx_frame fifo[fifo_ack] == 0!\r\n"); + } else { + //calc tx payload + struct packet_header * pkt; + pkt = (struct packet_header *)s_mipc_tx_ctrl.fifo[s_mipc_tx_ctrl.fifo_ack]->buf_ptr; + ipc_info_sdio_write_payload(pkt->length); + + _FreeFrame(s_mipc_tx_ctrl.fifo[s_mipc_tx_ctrl.fifo_ack], &s_mipc_tx_free_frame_list); + s_mipc_tx_ctrl.fifo_ack = NEXT_TX_FIFO_IDX(s_mipc_tx_ctrl.fifo_ack); + return true; + } + } else { + IPC_SDIO_ERR("[MIPC] ERROR implicit_ack_tx_frame fifo_ack == fifo_rd!\r\n"); + } + return false; +} + +void free_all_tx_frame_in_fifo(void) +{ + //free written items + while(s_mipc_tx_ctrl.fifo_ack != s_mipc_tx_ctrl.fifo_rd) { + if(s_mipc_tx_ctrl.fifo[s_mipc_tx_ctrl.fifo_ack]) { + _FreeFrame(s_mipc_tx_ctrl.fifo[s_mipc_tx_ctrl.fifo_ack], &s_mipc_tx_free_frame_list); + s_mipc_tx_ctrl.fifo_ack = NEXT_TX_FIFO_IDX(s_mipc_tx_ctrl.fifo_ack); + + } + } + //free un-written items + while(s_mipc_tx_ctrl.fifo_rd != s_mipc_tx_ctrl.fifo_wr) { + if(s_mipc_tx_ctrl.fifo[s_mipc_tx_ctrl.fifo_rd]) { + _FreeFrame(s_mipc_tx_ctrl.fifo[s_mipc_tx_ctrl.fifo_rd], &s_mipc_tx_free_frame_list); + s_mipc_tx_ctrl.fifo_rd = NEXT_TX_FIFO_IDX(s_mipc_tx_ctrl.fifo_rd); + + } + } + s_mipc_tx_ctrl.fifo_ack = 0; + s_mipc_tx_ctrl.fifo_rd = 0; + s_mipc_tx_ctrl.fifo_wr = 0; +} + +#define MUX_IPC_DISABLE 0 +#define MUX_IPC_ENABLE 1 +void sdio_ipc_enable(u8 is_enable) +{ + if(!s_mux_ipc_module_inited) { + IPC_SDIO_ERR("Error: mux ipc module is not initialized!\r\n"); + return; + } + + + if(is_enable) { + //msleep(1000); + kfifo_reset(&s_mipc_rx_cache_kfifo); + _FlushTxTransfer(); + _FreeAllTxTransferFrame(); + s_tx_flow_info = 0xFF; + s_acked_tx_frame = 0; + s_last_tx_frame = 0; + s_tx_frame_counter = 1; //tx frame number start from 1 + s_mipc_next_rx_len = DEFAULT_RX_FRAME_SIZE; + //sdhci_resetconnect(MUX_IPC_DISABLE); + mutex_lock(&ipc_mutex); + s_mux_ipc_event_flags = 0; + mutex_unlock(&ipc_mutex); + s_mipc_enable_change_flag = 0; + s_mux_ipc_enable = MUX_IPC_ENABLE; + wake_up(&s_modem_ready); + } else { + s_mux_ipc_enable = MUX_IPC_DISABLE; + //wakeup sdio thread to release sdio_gpio_res + s_mipc_enable_change_flag = 1; + wake_up(&s_mux_ipc_sdio_wq); + //wait all resources freed + wait_event(s_sdio_gpio_res_free, + (s_sdio_gpio_res_status == SDIO_GPIO_RES_FREE)); + } + + IPC_SDIO_WARN("[mipc]mux ipc enable:0x%x, status:0x%x\r\n", is_enable, s_mux_ipc_enable); +} + +int mux_ipc_sdio_stop(int mode) +{ + mutex_lock(&ipc_mutex); + if(mode & SPRDMUX_READ) { + s_mux_ipc_event_flags |= MUX_IPC_READ_DISABLE; + } + + if(mode & SPRDMUX_WRITE) { + s_mux_ipc_event_flags |= MUX_IPC_WRITE_DISABLE; + } + mutex_unlock(&ipc_mutex); + wake_up(&s_mux_read_rts); + _WakeTxTransfer(); + return 0; +} + +static char s_dbg_buf[MAX_MIPC_TX_FRAME_SIZE]; + +#ifdef SDIO_LOOP_TEST + +unsigned char s_dbg_tx_seed_v0 = 0; +unsigned char s_dbg_tx_seed_v1 = 0; + + +void set_tx_buf_dbg_data(unsigned char* payload, unsigned long length) +{ + int i = 0; + unsigned char v0 = s_dbg_tx_seed_v0; + unsigned char v1 = s_dbg_tx_seed_v1; + unsigned char val; + + for(i = 0; i < length; i++) { + + val = v0 + v1 + 1; + v0 = v1; + v1 = val; + + payload[i] = val; + } + s_dbg_tx_seed_v0 = v0; + s_dbg_tx_seed_v1 = v1; +} + +unsigned char s_dbg_tx_chk_seed_v0 = 0; +unsigned char s_dbg_tx_chk_seed_v1 = 0; +unsigned long s_dbg_rx_data_cnt = 0; + + + +void chk_tx_buf_dbg_data(unsigned char* payload, unsigned long length) +{ + int i = 0; + unsigned char v0 = s_dbg_tx_chk_seed_v0; + unsigned char v1 = s_dbg_tx_chk_seed_v1; + unsigned char val; + + for(i = 0; i < length; i++) { + + /*if(s_dbg_rx_data_cnt == ((64 * 1024 - 32) * 8)) { + s_dbg_tx_chk_seed_v0 = 0; + s_dbg_tx_chk_seed_v1 = 0; + v0 = s_dbg_tx_chk_seed_v0; + v1 = s_dbg_tx_chk_seed_v1; + s_dbg_rx_data_cnt = 0; + }*/ + val = v0 + v1 + 1; + v0 = v1; + v1 = val; + + if(payload[i] != val) { + while(1) { + IPC_SDIO_ERR("[mipc]chk_tx_buf_dbg_data error, rx_cnt:%d!\r\n", s_dbg_rx_data_cnt); + msleep(200); + } + } + ++s_dbg_rx_data_cnt; + } + s_dbg_tx_chk_seed_v0 = v0; + s_dbg_tx_chk_seed_v1 = v1; +} + +int mux_ipc_sdio_write_test(const char *buf, size_t count) +{ + static int first = 1; + IPC_SDIO_INFO("[mipc]mux_ipc_sdio_write write len:%d\r\n", count); + if(first) { + int cnt = 0; + first = 0; + wait_cp_bootup(); + while(1) { + set_tx_buf_dbg_data(s_dbg_buf, MAX_MIPC_TX_FRAME_SIZE - sizeof(struct packet_header)); + mux_ipc_xmit_buf(s_dbg_buf, MAX_MIPC_TX_FRAME_SIZE - sizeof(struct packet_header)); + + cnt++; + if(cnt == MAX_MIPC_TX_FRAME_SIZE) + //if(cnt == 4) + { + msleep(100); + cnt = 0; + } + } + } + + return count; + + //return mux_ipc_xmit_buf(buf, count); +} +#endif + +#ifndef SDIO_LOOP_TEST + +int mux_ipc_sdio_write(const char *buf, size_t count) +{ + if(s_mux_ipc_event_flags & MUX_IPC_WRITE_DISABLE) { + IPC_SDIO_ERR("[mipc]mux_ipc_sdio_write write disabled!\r\n"); + return -1; + } + wait_cp_bootup(); + IPC_SDIO_INFO("[mipc]mux_ipc_sdio_write write len:%d\r\n", count); + return mux_ipc_xmit_buf(buf, count); +} + +#else + +int mux_ipc_sdio_write(const char *buf, size_t count) +{ + wait_cp_bootup(); + IPC_SDIO_INFO("[mipc]mux_ipc_sdio_write write len:%d\r\n", count); + return count; +} +#endif + +#ifdef SDIO_LOOP_TEST + +int do_mux_ipc_sdio_read(char *buf, size_t count) +{ + int ret = 0; + + wait_event(s_mux_read_rts, !kfifo_is_empty(&s_mipc_rx_cache_kfifo) || s_mux_ipc_event_flags); + + if(s_mux_ipc_event_flags & MUX_IPC_READ_DISABLE) { + IPC_SDIO_ERR("[mipc] mux ipc read disable!\r\n"); + return -1; + } + + ret = kfifo_out(&s_mipc_rx_cache_kfifo,buf,count); + IPC_SDIO_INFO("[mipc]mux_ipc_sdio_read read len:%d\r\n", ret); + + ipc_info_mux_read(ret); + + ipc_info_sdio_read_saved_count(kfifo_len(&s_mipc_rx_cache_kfifo)); + + //wake up rx thread + wake_up(&s_rx_fifo_avail); + return ret; +} + +#define DBG_RX_BUF_LEN (16 * 1024) + +unsigned char s_dbg_rx_buf[DBG_RX_BUF_LEN]; + +int mux_ipc_sdio_test_read(char *buf, size_t c) +{ + int ret = 0; + + do { + + ret = do_mux_ipc_sdio_read(s_dbg_rx_buf, DBG_RX_BUF_LEN); + if(ret >= 0) { + chk_tx_buf_dbg_data(s_dbg_rx_buf, ret); + } + //printk("[mipc]mux_ipc_sdio_test_read sleep 10ms\r\n"); + //msleep(10); + } while(ret >= 0); + + return 0; +} + + +#endif + +#ifdef SDIO_LOOP_TEST +int mux_ipc_sdio_read(char *buf, size_t count) +{ + int ret = 0; + + return ret; +} + +#else + +int mux_ipc_sdio_read(char *buf, size_t count) +{ + int ret = 0; + + //incrase read count + //atomic_inc(&s_mipc_read_pending); + + wait_event(s_mux_read_rts, !kfifo_is_empty(&s_mipc_rx_cache_kfifo) || s_mux_ipc_event_flags); + + //decrase read count + //atomic_dec(&s_mipc_read_pending); + if(s_mux_ipc_event_flags & MUX_IPC_READ_DISABLE) { + IPC_SDIO_ERR("[mipc] mux ipc read disable!\r\n"); + //if(atomic_read(&s_mipc_read_pending) == 0) { + mutex_lock(&ipc_mutex); + s_mux_ipc_event_flags = 0; + mutex_unlock(&ipc_mutex); + //} + return -1; + } + + + ret = kfifo_out(&s_mipc_rx_cache_kfifo,buf,count); + + IPC_SDIO_INFO("[mipc]mux_ipc_sdio_read read len:%d\r\n", ret); + + ipc_info_mux_read(ret); + + ipc_info_sdio_read_saved_count(kfifo_len(&s_mipc_rx_cache_kfifo)); + + //wake up rx thread + wake_up(&s_rx_fifo_avail); + + return ret; +} +#endif + +static void _FrameList_Init(MIPC_FRAME_LIST_T* frame_list_ptr) +{ + if(!frame_list_ptr) { + panic("_Init_Frame_List: frame_list_ptr=NULL!\r\n"); + return; + } + + INIT_LIST_HEAD(&frame_list_ptr->frame_list_head); + mutex_init(&frame_list_ptr->list_mutex); + frame_list_ptr->counter = 0; +} + +static void _TxFreeFrameList_Init(MIPC_FRAME_LIST_T* frame_list_ptr) +{ + u32 t = 0; + MIPC_TRANSF_FRAME_T* frame_ptr = NULL; + + _FrameList_Init(frame_list_ptr); + + for(t = 0; t < MAX_MIPC_TX_FRAME_NUM; t++) { + frame_ptr = &s_tx_transfer_frame[t]; + frame_ptr->buf_size = MAX_MIPC_TX_FRAME_SIZE; + frame_ptr->buf_ptr = (u8*)kmalloc(frame_ptr->buf_size , GFP_KERNEL); + frame_ptr->pos = 0; + frame_ptr->flag = 0; + list_add_tail(&frame_ptr->link, &frame_list_ptr->frame_list_head); + frame_list_ptr->counter++; + } + +} + +static void _TransferInit(MIPC_TRANSFER_T* transfer_ptr) +{ + if(!transfer_ptr) { + panic("_Init_Frame_List: frame_list_ptr=NULL!\r\n"); + return; + } + + INIT_LIST_HEAD(&transfer_ptr->frame_fifo); + mutex_init(&transfer_ptr->transfer_mutex); + transfer_ptr->counter = 0; + transfer_ptr->cur_frame_ptr = NULL; + transfer_ptr->frame_count = 0; + init_waitqueue_head(&transfer_ptr->frame_free_wq); +} + +static void _transfer_frame_init(void) +{ + s_mipc_rx_buf = (u8*) __get_free_pages(GFP_KERNEL, get_order(MAX_MIPC_RX_FRAME_SIZE)); + + WARN_ON(NULL == s_mipc_rx_buf); + + if(kfifo_alloc(&s_mipc_rx_cache_kfifo,MAX_MIPC_RX_CACHE_SIZE, GFP_KERNEL)) { + IPC_SDIO_ERR("_transfer_frame_init: kfifo rx cache no memory!\r\n"); + panic("%s[%d]kfifo rx cache no memory", __FILE__, __LINE__); + } + _TxFreeFrameList_Init(&s_mipc_tx_free_frame_list); + _TransferInit(&s_mipc_tx_tansfer); + +} + +MIPC_TRANSF_FRAME_T* _AllocFrame(MIPC_FRAME_LIST_T* frame_list_ptr) +{ + MIPC_TRANSF_FRAME_T* frame_ptr = NULL; + + if(!frame_list_ptr) { + panic("%s[%d] frame_list_ptr = NULL!\r\n", __FILE__, __LINE__); + } + + mutex_lock(&frame_list_ptr->list_mutex);/*get lock */ + if(!list_empty(&frame_list_ptr->frame_list_head)) { + frame_ptr = list_entry(frame_list_ptr->frame_list_head.next, MIPC_TRANSF_FRAME_T, link); + list_del(&frame_ptr->link); + frame_ptr->pos = sizeof(struct packet_header); + frame_ptr->flag = 1; + frame_list_ptr->counter--; + } + mutex_unlock(&frame_list_ptr->list_mutex);/*set lock */ + IPC_SDIO_DEBUG("_AllocFrame:0x%X\r\n", (u32)frame_ptr); + return frame_ptr; +} + +static void _FreeFrame(MIPC_TRANSF_FRAME_T* frame_ptr, MIPC_FRAME_LIST_T* frame_list_ptr) +{ + if(!frame_list_ptr || !frame_ptr) { + panic("%s[%d] frame_list_ptr = NULL!\r\n", __FILE__, __LINE__); + } + IPC_SDIO_DEBUG("_FreeFrame:0x%x\r\n", (u32)frame_ptr); + mutex_lock(&frame_list_ptr->list_mutex);/*get lock */ + frame_ptr->pos = 0; + frame_ptr->flag = 0; + list_add_tail(&frame_ptr->link, &frame_list_ptr->frame_list_head); + frame_list_ptr->counter++; + mutex_unlock(&frame_list_ptr->list_mutex);/*set lock */ + _WakeTxTransfer(); +} + +static void _AddFrameToTxTransferFifo(MIPC_TRANSF_FRAME_T* frame_ptr, MIPC_TRANSFER_T* transfer_ptr) +{ + struct packet_header* header_ptr = ( struct packet_header* )frame_ptr->buf_ptr; + header_ptr->tag = 0x7e7f; + header_ptr->flag = 0xaa55; + header_ptr->length = frame_ptr->pos - sizeof(struct packet_header); + header_ptr->frame_num = s_tx_frame_counter++; //sending_cnt; + header_ptr->u.emergency_cmd = 0xabcdef00; + header_ptr->next_length = 0xFFFFEEEE; + header_ptr->reserved[0] = 0x11112222; + header_ptr->reserved[1] = 0x33334444; + header_ptr->reserved[2] = 0x55556666; + list_add_tail(&frame_ptr->link, &transfer_ptr->frame_fifo); + transfer_ptr->frame_count++; +} + +static u32 _GetFrameFromTxTransfer(MIPC_TRANSF_FRAME_T* * out_frame_ptr) +{ + MIPC_TRANSF_FRAME_T* frame_ptr = NULL; + MIPC_TRANSFER_T* transfer_ptr = &s_mipc_tx_tansfer; + mutex_lock(&transfer_ptr->transfer_mutex);/*get lock */ + if(!list_empty(&transfer_ptr->frame_fifo)) { + frame_ptr = list_entry(transfer_ptr->frame_fifo.next, MIPC_TRANSF_FRAME_T, link); + list_del(&frame_ptr->link); + transfer_ptr->frame_count--; + } + mutex_unlock(&transfer_ptr->transfer_mutex);/*set lock */ + + if(!frame_ptr) { + return 1; + } + + *out_frame_ptr = frame_ptr; + return 0; +} + +static u32 _IsTransferFifoEmpty(MIPC_TRANSFER_T* transfer_ptr) +{ + u32 ret = 0; + mutex_lock(&transfer_ptr->transfer_mutex);/*get lock */ + ret = list_empty(&transfer_ptr->frame_fifo); + mutex_unlock(&transfer_ptr->transfer_mutex);/*set lock */ + return ret; +} + +u32 s_need_wake_up_tx_thread = 0; + +static u32 _AddDataToTxTransfer(u8* data_ptr, u32 len) +{ + u32 ret = 0; + MIPC_TRANSF_FRAME_T* frame_ptr = NULL; + MIPC_TRANSF_FRAME_T* new_frame_ptr = NULL; + MIPC_TRANSFER_T* transfer_ptr = &s_mipc_tx_tansfer; + mutex_lock(&transfer_ptr->transfer_mutex);/*get lock */ + + if(0 == transfer_ptr->counter) { + s_need_wake_up_tx_thread = 1; + } + do { + frame_ptr = transfer_ptr->cur_frame_ptr; + if(!frame_ptr) { + frame_ptr = _AllocFrame(&s_mipc_tx_free_frame_list); + } + if(!frame_ptr) { + // printk("%s[%d]_AddDataToFrame No Empty Frame!\r\n", __FILE__, __LINE__); + ret = 0; + break; + } + if(len > (frame_ptr->buf_size - sizeof( struct packet_header))) { + IPC_SDIO_ERR("[mipc] _AddDataToFrame data too long!\r\n"); + ret = 0; + break; + } + if((len + frame_ptr->pos) > frame_ptr->buf_size) { + new_frame_ptr = _AllocFrame(&s_mipc_tx_free_frame_list); + if(!new_frame_ptr) { + // printk("%s[%d]_AddDataToFrame No Empty Frame : pos:%d, data len:%d\r\n", "ipc_sdio.c", __LINE__, frame_ptr->pos, len); + ret = 0; + break; + } + _AddFrameToTxTransferFifo(frame_ptr, transfer_ptr); + frame_ptr = new_frame_ptr; + s_need_wake_up_tx_thread = 1; + } + memcpy(&frame_ptr->buf_ptr[frame_ptr->pos], data_ptr, len); + frame_ptr->pos += len; + transfer_ptr->counter += len; + transfer_ptr->cur_frame_ptr = frame_ptr; + ret = len; + } while(0); + + mutex_unlock(&transfer_ptr->transfer_mutex);/*set lock */ + + return ret; +} + +static void _WaitTxTransferFree(void) +{ + wait_event(s_mipc_tx_tansfer.frame_free_wq, (s_mipc_tx_free_frame_list.counter || + (s_mux_ipc_event_flags & MUX_IPC_WRITE_DISABLE))); +} + +static void _WakeTxTransfer(void) +{ + wake_up(&s_mipc_tx_tansfer.frame_free_wq); +} + +static u32 _DelDataFromTxTransfer(MIPC_TRANSF_FRAME_T* frame_ptr) +{ + MIPC_TRANSFER_T* transfer_ptr = &s_mipc_tx_tansfer; + mutex_lock(&transfer_ptr->transfer_mutex); + transfer_ptr->counter -= frame_ptr->pos - sizeof(struct packet_header); + IPC_SDIO_DEBUG("[mipc] _DelDataFromTxTransfer transfer_ptr->counter = %d\n", transfer_ptr->counter); + mutex_unlock(&transfer_ptr->transfer_mutex); + //_FreeFrame(frame_ptr, &s_mipc_tx_free_frame_list); //sdio opt 1, do not free here + return 0; +} + + +static u32 _FreeAllTxTransferFrame(void) +{ + MIPC_TRANSF_FRAME_T* frame_ptr = NULL; + free_all_tx_frame_in_fifo(); + while(!_GetFrameFromTxTransfer(&frame_ptr)) { + _DelDataFromTxTransfer(frame_ptr); + _FreeFrame(frame_ptr, &s_mipc_tx_free_frame_list); + } + return 0; +} + + +static u32 _FlushTxTransfer(void) +{ + u32 ret = 0; + MIPC_TRANSFER_T* transfer_ptr = &s_mipc_tx_tansfer; + mutex_lock(&transfer_ptr->transfer_mutex);/*get lock */ + do { + if((transfer_ptr->counter == 0) || !transfer_ptr->cur_frame_ptr || + (transfer_ptr->cur_frame_ptr->pos == sizeof( struct packet_header))) { + ret = 1; + break; + } + _AddFrameToTxTransferFifo(transfer_ptr->cur_frame_ptr, transfer_ptr); + transfer_ptr->cur_frame_ptr = NULL; + } while(0); + mutex_unlock(&transfer_ptr->transfer_mutex);/*set lock */ + return ret; +} + +u32 mux_ipc_GetTxTransferSavedCount(void) +{ + u32 count = 0; + MIPC_TRANSFER_T* transfer_ptr = &s_mipc_tx_tansfer; + mutex_lock(&transfer_ptr->transfer_mutex); + count = transfer_ptr->counter; + mutex_unlock(&transfer_ptr->transfer_mutex); + return count; +} +static size_t sdio_write_modem_data(const u8 * buf, u32 len) +{ + size_t ret = 0; + u32 result = SDHCI_TRANSFER_OK; + u32 resend_count = 0; + + wake_lock(&s_ipc_sdio_wake_lock); + + do { + IPC_SDIO_INFO("SDIO WRITE START\n"); + + ipc_info_change_status(IPC_TX_CHANNEL, IPC_STATUS_CONNECTED); + ret = sprd_sdio_channel_tx(buf, len, (resend_count & ((1 << 17) - 1))); + if(!ret) { + set_cp_awake(true); + IPC_SDIO_INFO("SDIO WRITE SUCESS\n"); + result = SDHCI_TRANSFER_OK; + ret = len; + } else { + ipc_info_error_status(IPC_TX_CHANNEL, IPC_STATUS_CRC_ERROR); + result = SDHCI_TRANSFER_ERROR; + IPC_SDIO_ERR("SDIO WRITE FAIL ret= %d\n", ret); + ret = 0; + } + + + ipc_info_change_status(IPC_TX_CHANNEL, IPC_STATUS_DISCONNECTED); + + if(!_is_mux_ipc_enable()) { + IPC_SDIO_ERR("[mipc] Found mux ipc disabled in do_sdio_tx\r \n", ret); + break; + } + if(result) { + resend_count++; + msleep(200); + } + } while(result && (resend_count < MAX_SDIO_TX_RETRY)); + + wake_unlock(&s_ipc_sdio_wake_lock); + + ipc_info_change_status(IPC_TX_CHANNEL, IPC_STATUS_IDLE); + if(resend_count >= MAX_SDIO_TX_RETRY) { +#if 0 + while(1) { + msleep(2000); + printk("sdio_write_modem_data write fail 3 times \n"); + } +#else + IPC_SDIO_ERR("sdio_write_modem_data write fail 3 times \n"); +#endif + } + + return ret; + +} + +static int sdio_read_modem_data(u8 *buf, int len, int addr) +{ + int ret = 0; + IPC_SDIO_INFO("[mipc] sdio_read_modem_data entery:buf:0x%X, len:%x\r\n", (u32)buf, len); + ret = sprd_sdio_channel_rx(buf, len, addr); + return ret; +} + +extern __u8 mux_calc_crc(__u8 * data, __u32 length); + +unsigned long s_dbg_dl_frame_num = 0; +static bool VerifyPacketHeader(struct packet_header *header, u32 *tx_flow_info, + u32 *acked_tx_frame) +{ + if(0) { //(sdio_transfer_crc_check_enable) + __u8* data_ptr = (__u8*)(header + 1); + __u8 crc_value = mux_calc_crc(data_ptr, header->length ); + if ( (header->tag != HEADER_TAG) || ((header->flag & 0xFF00) != HEADER_TYPE) + || (header->length > MAX_MIPC_RX_FRAME_SIZE) + ||(header->frame_num != crc_value)) { + IPC_SDIO_ERR("[mipc]:%s error, tag:0x%X, type:0x%X, len:0x%X, crc:0x%X, calc_crc:0x%X\r\n", + __func__, header->tag , header->flag, header->length,header->frame_num, crc_value); + return false; + } + } else { + if ( (header->tag != HEADER_TAG) || ((header->flag & 0xFF00) != HEADER_TYPE) + || (header->length > MAX_MIPC_RX_FRAME_SIZE)) { + IPC_SDIO_ERR("[mipc]:%s error, tag:0x%X, type:0x%X, len:0x%X, frame_num:0x%X\r\n", + __func__, header->tag , header->flag, header->length,header->frame_num); + return false; + } else { + *tx_flow_info = header->flag & 0xFF; + *acked_tx_frame = header->u.ul_frame_num; + if((s_dbg_dl_frame_num + 1) != header->frame_num) { + IPC_SDIO_ERR("[mipc]:(s_dbg_dl_frame_num + 1) != header->frame_num!!\r\n"); + } + s_dbg_dl_frame_num = header->frame_num; + IPC_SDIO_DEBUG("[mipc]:s_dbg_dl_frame_num:%d!\r\n", s_dbg_dl_frame_num); + s_mipc_next_rx_len = header->next_length; + } + } + + return true; +} + +static inline bool have_buffer_to_read(void) +{ + return (kfifo_avail(&s_mipc_rx_cache_kfifo) >= MAX_MIPC_RX_FRAME_SIZE); +} +u32 process_modem_packet(unsigned long data) +{ + if(!have_buffer_to_read()) { + IPC_SDIO_ERR("[MIPC] MIPC Rx Cache Full!\r\n"); + } + wait_event(s_rx_fifo_avail, have_buffer_to_read()); + + //send sdio read request + IPC_SDIO_DEBUG("[MIPC] MIPC Rx Cache Free!\r\n"); + s_mipc_rx_event_flags = 0; + s_mipc_rx_req_flag = 1; + wake_up(&s_mux_ipc_sdio_wq); + + return 0; +} + +#ifdef SDIO_GPIO_TEST + +unsigned long s_dbg_cp2ap_cnt = 0; +int s_dbg_cp2ap_last_val; + +static int mux_test_cp2ap_rdy(void) +{ + int val; + val = cp2ap_req(); + + IPC_SDIO_DEBUG("[mipc_test] cp2ap_req value after write data:%d\r\n", val); + if(s_dbg_cp2ap_cnt > 10) { + if(s_dbg_cp2ap_last_val == val) { + while(1) { + IPC_SDIO_ERR("[mipc_test] s_dbg_cp2ap_last_val == val\r\n"); + msleep(20); + } + } + } + s_dbg_cp2ap_cnt++; + s_dbg_cp2ap_last_val = val; + + return 0; +} + +#endif + +#define TX_THRD_GATHER_COND (s_mipc_tx_tansfer.frame_count || ((s_mipc_tx_ctrl.fifo_wr == s_mipc_tx_ctrl.fifo_rd)&& s_tx_flow_info)) +static int mux_ipc_tx_thread(void *data) +{ + MIPC_TRANSF_FRAME_T* frame_ptr = NULL; + struct sched_param param = {.sched_priority = 10}; + + IPC_SDIO_INFO("mux_ipc_tx_thread"); + sched_setscheduler(current, SCHED_FIFO, ¶m); + + + while (!kthread_should_stop()) { + wait_event(s_mux_ipc_tx_wq, s_mipc_tx_tansfer.counter); + + if(!s_mipc_tx_tansfer.counter || (s_mipc_tx_tansfer.counter > MAX_MIPC_TX_FRAME_SIZE)) { + IPC_SDIO_DEBUG("[mipc] s_mipc_tx_tansfer.counter:%d\r\n", s_mipc_tx_tansfer.counter); + } + + while(s_mipc_tx_tansfer.counter) { + if(_IsTransferFifoEmpty(&s_mipc_tx_tansfer)) { + //do not send right now, because now sdio is pending + wait_event(s_mux_ipc_tx_wq, TX_THRD_GATHER_COND); + IPC_SDIO_DEBUG("[mipc] tx thread wait TX_THRD_GATHER_COND OK!\r\n"); + } + do { + + if(_IsTransferFifoEmpty(&s_mipc_tx_tansfer)) { + if(_FlushTxTransfer()) { + IPC_SDIO_DEBUG("[mipc] s_mipc_tx_tansfer.counter: %d\n", s_mipc_tx_tansfer.counter); + IPC_SDIO_DEBUG("[mipc] transfer_ptr->cur_frame_ptr: 0x%X\n", s_mipc_tx_tansfer.cur_frame_ptr); + if(s_mipc_tx_tansfer.cur_frame_ptr) { + IPC_SDIO_DEBUG("[mipc] cur_frame_ptr->pos: %d\n", s_mipc_tx_tansfer.cur_frame_ptr->pos); + } + IPC_SDIO_WARN("[mipc] No Data To Send\n"); + break; + } + } + + if(_GetFrameFromTxTransfer(&frame_ptr)) { + IPC_SDIO_ERR("[mipc] Error: Flush Empty Frame \n"); + break; + } + + _DelDataFromTxTransfer(frame_ptr); + + IPC_SDIO_DEBUG("[mipc] sending frame count: %d\n", (MAX_MIPC_TX_FRAME_NUM - s_mipc_tx_free_frame_list.counter)); + put_to_sdio_tx_fifo(frame_ptr); + } while(0); + + } + } + + sprd_sdio_channel_close(); + sdhci_hal_gpio_exit(); + + return 0; +} + + + +static int mux_ipc_xmit_buf(const char *buf, ssize_t len) +{ + ssize_t ret = -1; + + mutex_lock(&sdio_tx_lock); + do { + ret = _AddDataToTxTransfer((u8*)buf, len); + if(!ret) { + ipc_info_sdio_write_overflow(1); + IPC_SDIO_WARN("[mipc] mux_ipc_xmit_buf begin _WaitTxTransferFree\n"); + _WaitTxTransferFree(); + IPC_SDIO_WARN("[mipc] mux_ipc_xmit_buf end _WaitTxTransferFree\n"); + if(s_mux_ipc_event_flags & MUX_IPC_WRITE_DISABLE) { + break; + } + } + } while(!ret); + + ipc_info_mux_write(len); + + if(s_need_wake_up_tx_thread) { + s_need_wake_up_tx_thread = 0; + wake_up(&s_mux_ipc_tx_wq); + } + mutex_unlock(&sdio_tx_lock); + return ret; +} + +static int mux_ipc_create_tx_thread(void) +{ + IPC_SDIO_INFO("mux_ipc_create_tx_thread enter.\n"); + init_waitqueue_head(&s_mux_ipc_tx_wq); + s_mux_ipc_tx_thread = kthread_create(mux_ipc_tx_thread, NULL, "mipc_tx_thread"); + + if (IS_ERR(s_mux_ipc_tx_thread)) { + IPC_SDIO_ERR("mux_ipc_tx_thread error!.\n"); + return -1; + } + + wake_up_process(s_mux_ipc_tx_thread); + + return 0; +} + + +u32 wake_up_mipc_rx_thread(u32 even_flag) +{ + u32 status = 0; + u32 ipc_status = _is_mux_ipc_enable(); + + wake_lock_timeout(&s_cp_req_wake_lock, HZ / 2); + if(ipc_status && even_flag) { + s_mipc_rx_event_flags = even_flag; + wake_up(&s_mux_ipc_rx_wq); + status = 0; + } else { + IPC_SDIO_ERR("mux ipc rx invaild wakeup, ipc status:%u, flag:%d\r\n", ipc_status, even_flag); + status = -1; + } + return status; +} + +static int mux_ipc_rx_thread(void *data) +{ + struct sched_param param = {.sched_priority = 25}; + IPC_SDIO_INFO("mux_ipc_rx_thread enter\r\n"); + sched_setscheduler(current, SCHED_FIFO, ¶m); + + wait_cp_bootup(); + msleep(500); + + IPC_SDIO_INFO("mux_ipc_rx_thread start----\r\n"); + //check CP status when start up + if(cp2ap_req()) { + s_mipc_rx_event_flags = 1; + } + //working loop + while (!kthread_should_stop()) { + wait_event(s_mux_ipc_rx_wq, s_mipc_rx_event_flags); + process_modem_packet(0); + } + + return 0; +} + +static int mux_ipc_create_rx_thread(void) +{ + IPC_SDIO_INFO("mux_ipc_rx_create_thread enter.\n"); + init_waitqueue_head(&s_mux_ipc_rx_wq); + s_mux_ipc_rx_thread = kthread_create(mux_ipc_rx_thread, NULL, "mipc_rx_thread"); + + if (IS_ERR(s_mux_ipc_rx_thread)) { + IPC_SDIO_ERR("ipc_sdio.c:mux_ipc_rx_thread error!.\n"); + return -1; + } + + wake_up_process(s_mux_ipc_rx_thread); + + return 0; +} + +static int do_sdio_rx(u32 *tx_flow_info, u32 *acked_tx_frame) +{ + u32 receve_len = 0; + int ret = 0; + int result = SDHCI_TRANSFER_OK; + int resend_count = 0; + struct packet_header *packet = NULL; + u32 rx_buf_len; + + rx_buf_len = s_mipc_next_rx_len; + wake_lock(&s_ipc_sdio_wake_lock); + do { + IPC_SDIO_DEBUG(" sdio_read_modem_data xxxx................\r\n"); + + ipc_info_change_status(IPC_RX_CHANNEL, IPC_STATUS_CONNECTED); + + memset(s_mipc_rx_buf, 0xaa, MAX_MIPC_RX_FRAME_SIZE); + + if(!have_buffer_to_read()) { + panic("do_sdio_rx have no buffer to read!\r\n"); + } + +#ifdef SDIO_VARIABLE_DL_LEN + ret = sdio_read_modem_data(s_mipc_rx_buf, rx_buf_len, (resend_count != 0)); +#else + ret = sdio_read_modem_data(s_mipc_rx_buf, MAX_MIPC_RX_FRAME_SIZE, (resend_count != 0)); +#endif + if (!ret) { + set_cp_awake(true); + //calc phy read speed +#ifdef SDIO_VARIABLE_DL_LEN + ipc_info_sdio_read(rx_buf_len); +#else + ipc_info_sdio_read(MAX_MIPC_RX_FRAME_SIZE); +#endif + + + + packet=(struct packet_header *)s_mipc_rx_buf; + if(VerifyPacketHeader(packet, tx_flow_info, acked_tx_frame)) { + result = SDHCI_TRANSFER_OK; + } else { + ipc_info_error_status(IPC_RX_CHANNEL, IPC_STATUS_PACKET_ERROR); + result = SDHCI_TRANSFER_ERROR; + IPC_SDIO_ERR("[mipc] Sdio Rx Packet check error tag:0x%X, len:%d, ul_frame_num:0x%x\n", packet->tag, packet->length, packet->u.ul_frame_num); + } +#ifdef SDIO_VARIABLE_DL_LEN + if (packet->length > rx_buf_len) { + result = SDHCI_TRANSFER_ERROR; + IPC_SDIO_ERR("[mipc]SDIO READ FAIL, packet->length(%d) > rx_buf_len(%d)\r \n", packet->length, rx_buf_len); + rx_buf_len = MAX_MIPC_RX_FRAME_SIZE; + } +#endif + } else { + ipc_info_error_status(IPC_RX_CHANNEL, IPC_STATUS_CRC_ERROR); + result = SDHCI_TRANSFER_ERROR; + IPC_SDIO_ERR("[mipc]SDIO READ FAIL, ret:%d\r \n", ret); + } + + + ipc_info_change_status(IPC_RX_CHANNEL, IPC_STATUS_DISCONNECTED); + + if(!_is_mux_ipc_enable()) { + IPC_SDIO_ERR("[mipc] Found mux ipc disabled in do_sdio_rx\r \n", ret); + break; + } + if(result) { + resend_count++; + msleep(2); + } + + } while(result && (resend_count < MAX_SDIO_RX_RETRY)); + + ipc_info_change_status(IPC_RX_CHANNEL, IPC_STATUS_IDLE); + + wake_unlock(&s_ipc_sdio_wake_lock); + + if(!result) { + + ipc_info_rate(IPC_RX_CHANNEL, packet->length*1000/MAX_MIPC_RX_FRAME_SIZE); + ipc_info_sdio_read_payload(packet->length); + receve_len = packet->length; + + IPC_SDIO_INFO("[mipc] read data len:%d\r\n", receve_len); + + if(sdio_transfer_frame_check_enable && sdio_frame_check(&s_mipc_rx_buf[sizeof(struct packet_header )], packet->length)) { + IPC_SDIO_ERR("[mipc]:sdio receved data frame error!\r\n"); + } + + if(packet->length) { + kfifo_in(&s_mipc_rx_cache_kfifo,&s_mipc_rx_buf[sizeof(struct packet_header )], packet->length); + + wake_up(&s_mux_read_rts); + } else { + ipc_info_error_status(IPC_RX_CHANNEL, IPC_STATUS_INVALID_PACKET); + } + } else { + receve_len = 0; + IPC_SDIO_ERR("[mipc] receive data fail! result:%d\r\n", result); + } + return result; +} + + +static int do_sdio_tx(MIPC_TRANSF_FRAME_T* frame_ptr) +{ + u32 write_len = 0; + struct packet_header* header_ptr = ( struct packet_header* )frame_ptr->buf_ptr; + + //write len shall multi - 512 bytes,and min len = 4096 + write_len = (frame_ptr->pos <= MIN_MIPC_TX_FRAME_SIZE) ? + MIN_MIPC_TX_FRAME_SIZE : ((frame_ptr->pos + 0x1FF) & (~0x1FF)); + + s_last_tx_frame = header_ptr->frame_num; + IPC_SDIO_INFO("[mipc] write data ptr: 0x%x, len:%d\r\n", frame_ptr->buf_ptr, frame_ptr->pos); + + IPC_SDIO_DEBUG("[mipc] write data: 0x%x, 0x%x, 0x%x, 0x%x,\r\n", frame_ptr->buf_ptr[8], + frame_ptr->buf_ptr[9], + frame_ptr->buf_ptr[10], + frame_ptr->buf_ptr[11]); + write_len = sdio_write_modem_data(frame_ptr->buf_ptr, write_len); + + if(write_len) { + ipc_info_rate(IPC_TX_CHANNEL, (frame_ptr->pos - sizeof(struct packet_header))*1000/write_len); + ipc_info_sdio_write(write_len); + } + + return write_len; +} + +static void init_sdio_gpio_res() +{ + int rval; + + if(s_sdio_gpio_res_status != SDIO_GPIO_RES_USING) { + sdhci_hal_gpio_init(); + do { + rval = sprd_sdio_channel_open(); + IPC_SDIO_INFO("%s() sdio channel opened %d\n", __func__, rval); + } while(rval); + + sdhci_hal_gpio_irq_init(); + s_sdio_gpio_res_status = SDIO_GPIO_RES_USING; + } + +} + +static void free_sdio_gpio_res() +{ + if(s_sdio_gpio_res_status != SDIO_GPIO_RES_FREE) { + sdhci_hal_gpio_exit(); + sprd_sdio_channel_close(); + s_sdio_gpio_res_status = SDIO_GPIO_RES_FREE; + wake_up(&s_sdio_gpio_res_free); + } +} + +static int mux_ipc_sdio_thread(void *data) +{ + struct sched_param param = {.sched_priority = 35}; + u32 continue_tx_cnt = 0; + IPC_SDIO_INFO("mux_ipc_sdio_thread enter\r\n"); + sched_setscheduler(current, SCHED_FIFO, ¶m); + + IPC_SDIO_INFO("mux_ipc_sdio_thread start----\r\n"); + while (!kthread_should_stop()) { + bool force_tx = false; + + //check mux_ipc driver is enabled + if(!_is_mux_ipc_enable()) { + + IPC_SDIO_WARN("[mipc] found s_mux_ipc_enable == false\r\n"); + + free_sdio_gpio_res(); + + IPC_SDIO_WARN("[mipc] start to wait bootup in mux_ipc_sdio_thread\r\n"); + + wait_cp_bootup(); + + IPC_SDIO_WARN("[mipc] wait bootup success in mux_ipc_sdio_thread\r\n"); + + init_sdio_gpio_res(); + } + + wait_event_timeout(s_mux_ipc_sdio_wq, (s_mipc_enable_change_flag || s_mipc_rx_req_flag || MIPC_TX_REQ_FLAG), + msecs_to_jiffies(MAX_SDIO_SLEEP_TIMEOUT)); + if(s_mipc_enable_change_flag) { + s_mipc_enable_change_flag = 0; + continue; + } + + IPC_SDIO_DEBUG("[mipc] wait_event_timeout rx_req tx_req\r\n"); + if(!(s_mipc_rx_req_flag || MIPC_TX_REQ_FLAG)) { + //set ap inactive to let cp sleep + ap2cp_req_disable(); + set_cp_awake(false); + + //wait for requests + IPC_SDIO_INFO("[mipc] start wait_event rx_req tx_req forever\r\n"); + wait_event(s_mux_ipc_sdio_wq, (s_mipc_enable_change_flag || s_mipc_rx_req_flag || MIPC_TX_REQ_FLAG)); + if(s_mipc_enable_change_flag) { + s_mipc_enable_change_flag = 0; + continue; + } + } + + //check modem status + if(!_is_mux_ipc_enable()) { + continue; + } + + //set ap2cp active before transfer + ap2cp_req_enable(); + //make sure cp is awake + if(!get_cp_awake()) { + + //wait cp ack + if(!wait_cp_awake(msecs_to_jiffies(MAX_SDIO_CP_ACK_TIMEOUT))) { + IPC_SDIO_ERR("[MIPC] SDIO wait cp awake fail!\r\n"); + + //cp no ack, just discard a request + ap2cp_req_disable(); + msleep(2); + + continue; + + } + set_cp_awake(true); + if(MIPC_TX_REQ_FLAG) { + force_tx = true; + } + } + + //do tx + if(MIPC_TX_REQ_FLAG) { + + MIPC_TRANSF_FRAME_T* frame = get_from_sdio_tx_fifo(); + + if(frame) { + do_sdio_tx(frame); + continue_tx_cnt++; + } + + force_tx = false; + } + //check modem status + if(!_is_mux_ipc_enable()) { + continue; + } + + //do tx,check again + if(MIPC_TX_REQ_FLAG) { + + MIPC_TRANSF_FRAME_T* frame = get_from_sdio_tx_fifo(); + + if(frame) { + do_sdio_tx(frame); + continue_tx_cnt++; + } + + force_tx = false; + } + //check modem status + if(!_is_mux_ipc_enable()) { + continue; + } + + //do rx + if(s_mipc_rx_req_flag && !force_tx) { + int result; + u32 last_tx_flow_info = s_tx_flow_info; + + result = do_sdio_rx(&s_tx_flow_info, &s_acked_tx_frame); + s_mipc_rx_req_flag = 0; + continue_tx_cnt = 0; + + if(SDHCI_TRANSFER_OK == result) { + //out of flow ctrl, wake up tx thread + if(!last_tx_flow_info && s_tx_flow_info) { + wake_up(&s_mux_ipc_tx_wq); + printk("[mipc] exit ul flow ctrl, s_tx_flow_info:%d\n", s_tx_flow_info); + } else if(!s_tx_flow_info && last_tx_flow_info) { + printk("[mipc] enter ul flow ctrl,s_tx_flow_info:%d\n", s_tx_flow_info); + } + + IPC_SDIO_DEBUG("[mipc] last_ack_frame:%d, s_acked_tx_frame:%d, s_last_tx_frame:%d\n", + s_mipc_tx_ctrl.last_ack_frame, + s_acked_tx_frame, + s_last_tx_frame); + + if(s_mipc_tx_ctrl.last_ack_frame != s_acked_tx_frame) { + + ack_tx_frame_num(s_acked_tx_frame); + } + + if(s_acked_tx_frame != s_last_tx_frame) { + + nack_tx_frame_num(); + } + + if(s_tx_flow_info == 0) { + ipc_info_error_status(IPC_TX_CHANNEL, IPC_STATUS_FLOW_STOP); + } + } + + } + + //check modem status + if(!_is_mux_ipc_enable()) { + continue; + } + + + + //check cp read request after one cmd53 + if(cp2ap_req()) { + continue_tx_cnt = 0; + if(have_buffer_to_read()) { + s_mipc_rx_req_flag = 1; + } else { + wake_up_mipc_rx_thread(1); + } + } + + if(continue_tx_cnt >= 2) { + continue_tx_cnt--; + //implicit_ack_tx_frame(); + } + + //check need to wake tx_thread, nothing to send, wake up tx thread + if((s_mipc_tx_ctrl.fifo_wr == s_mipc_tx_ctrl.fifo_rd) + && s_tx_flow_info + && s_mipc_tx_tansfer.counter) { + wake_up(&s_mux_ipc_tx_wq); + } + + } + + return 0; +} + +static int mux_ipc_create_sdio_thread(void) +{ + IPC_SDIO_INFO("mux_ipc_create_sdio_thread enter.\n"); + init_waitqueue_head(&s_mux_ipc_sdio_wq); + s_mux_ipc_sdio_thread = kthread_create(mux_ipc_sdio_thread, NULL, "mipc_sdio_thread"); + + if (IS_ERR(s_mux_ipc_sdio_thread)) { + IPC_SDIO_ERR("ipc_sdio.c:s_mux_ipc_sdio_thread error!.\n"); + return -1; + } + + wake_up_process(s_mux_ipc_sdio_thread); + + return 0; +} + +#ifdef SDIO_LOOP_TEST + +static struct task_struct *s_mux_ipc_test_rx_thread; +static struct task_struct *s_mux_ipc_test_tx_thread; + + +static int mux_ipc_test_rx_thread(void *data) +{ + struct sched_param param = {.sched_priority = 15}; + IPC_SDIO_INFO("mux_ipc_test_rx_thread enter\r\n"); + sched_setscheduler(current, SCHED_FIFO, ¶m); + + + + IPC_SDIO_INFO("mux_ipc_test_rx_thread start----\r\n"); + while (!kthread_should_stop()) { + mux_ipc_sdio_test_read(NULL, 0); + } + return 0; +} + +static int mux_ipc_test_tx_thread(void *data) +{ + struct sched_param param = {.sched_priority = 15}; + IPC_SDIO_INFO("mux_ipc_test_tx_thread enter\r\n"); + sched_setscheduler(current, SCHED_FIFO, ¶m); + + + + IPC_SDIO_INFO("mux_ipc_test_tx_thread start----\r\n"); + + msleep(100 * 1000); + while (!kthread_should_stop()) { + mux_ipc_sdio_write_test(NULL, 0); + msleep(200); + } + + return 0; +} + +static int mux_ipc_create_test_tx_thread(void) +{ + IPC_SDIO_INFO("mux_ipc_create_test_tx_thread enter.\n"); + s_mux_ipc_test_tx_thread = kthread_create(mux_ipc_test_tx_thread, NULL, "mipc_test_tx_thrd"); + + if (IS_ERR(s_mux_ipc_test_tx_thread)) { + IPC_SDIO_ERR("ipc_sdio.c:s_mux_ipc_sdio_thread error!.\n"); + return -1; + } + + wake_up_process(s_mux_ipc_test_tx_thread); + + return 0; +} + +static int mux_ipc_create_test_rx_thread(void) +{ + IPC_SDIO_INFO("mux_ipc_create_test_rx_thread enter.\n"); + s_mux_ipc_test_rx_thread = kthread_create(mux_ipc_test_rx_thread, NULL, "mipc_test_rx_thrd"); + + if (IS_ERR(s_mux_ipc_test_rx_thread)) { + IPC_SDIO_ERR("ipc_sdio.c:s_mux_ipc_sdio_thread error!.\n"); + return -1; + } + + wake_up_process(s_mux_ipc_test_rx_thread); + + return 0; +} + +#endif + + +static int modem_sdio_probe(struct platform_device *pdev) +{ + int retval = 0; + + IPC_SDIO_INFO("modem_sdio_probe\n"); + //debug{ + ipc_info_error_status(IPC_RX_CHANNEL, IPC_STATUS_FLOW_STOP); + //}debug + + wake_lock_init(&s_ipc_sdio_wake_lock, WAKE_LOCK_SUSPEND, "ipc_sdio_lock"); + wake_lock_init(&s_cp_req_wake_lock, WAKE_LOCK_SUSPEND, "sdio_cp_req_lock"); + mutex_init(&ipc_mutex); + //atomic_set(&s_mipc_read_pending, 0); + init_cp_awake_status(); + init_mipc_tx_ctrl(); + mux_ipc_create_sdio_thread(); + mux_ipc_create_tx_thread(); + mux_ipc_create_rx_thread(); + +#ifdef SDIO_LOOP_TEST + mux_ipc_create_test_rx_thread(); + mux_ipc_create_test_tx_thread(); +#endif + return retval; +} + +static int modem_sdio_remove(struct platform_device *pdev) +{ + return 0; +} + + +static struct platform_driver modem_sdio_driver = { + .probe = modem_sdio_probe, + .remove = modem_sdio_remove, + .driver = { + .name = "ipc_sdio", + } +}; + +static struct sprdmux sprd_iomux = { + .id = 1, + .io_read = mux_ipc_sdio_read, + .io_write = mux_ipc_sdio_write, + .io_stop = mux_ipc_sdio_stop, +}; + +static int __init mux_ipc_sdio_init(void) +{ + + int retval; + IPC_SDIO_INFO("mux_ipc_sdio_init\n"); + + _transfer_frame_init(); + init_waitqueue_head(&s_mux_read_rts); + init_waitqueue_head(&s_rx_fifo_avail); + init_waitqueue_head(&s_modem_ready); + init_waitqueue_head(&s_sdio_gpio_res_free); + sprdmux_register(&sprd_iomux); + retval = platform_driver_register(&modem_sdio_driver); + if (retval) { + IPC_SDIO_ERR("[sdio]: register modem_sdio_driver error:%d\r\n", retval); + } + + s_mux_ipc_module_inited = 1; + + return retval; +} + +static void __exit mux_ipc_sdio_exit(void) +{ + platform_driver_unregister(&modem_sdio_driver); +} + + +module_param_named(sdio_transfer_crc_check_enable, sdio_transfer_crc_check_enable, int, S_IRUGO | S_IWUSR); +module_param_named(sdio_transfer_frame_check_enable, sdio_transfer_frame_check_enable, int, S_IRUGO | S_IWUSR); +module_param_named(sdio_tx_wait_time, sdio_tx_wait_time, int, S_IRUGO | S_IWUSR); + + + +module_init(mux_ipc_sdio_init); +module_exit(mux_ipc_sdio_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Bin.Xu<bin.xu@spreadtrum.com>"); +MODULE_DESCRIPTION("MUX SDIO Driver"); |
