diff options
Diffstat (limited to 'drivers/char/mux_spi_hal')
| -rw-r--r-- | drivers/char/mux_spi_hal/Makefile | 1 | ||||
| -rw-r--r-- | drivers/char/mux_spi_hal/ipc_spi.c | 1405 | ||||
| -rw-r--r-- | drivers/char/mux_spi_hal/ipc_spi.h | 147 | ||||
| -rw-r--r-- | drivers/char/mux_spi_hal/spi_gpio_ctrl.c | 205 | ||||
| -rw-r--r-- | drivers/char/mux_spi_hal/spi_gpio_ctrl.h | 40 |
5 files changed, 1798 insertions, 0 deletions
diff --git a/drivers/char/mux_spi_hal/Makefile b/drivers/char/mux_spi_hal/Makefile new file mode 100644 index 00000000..41721465 --- /dev/null +++ b/drivers/char/mux_spi_hal/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_MUX_SPI_HAL) += spi_gpio_ctrl.o ipc_spi.o diff --git a/drivers/char/mux_spi_hal/ipc_spi.c b/drivers/char/mux_spi_hal/ipc_spi.c new file mode 100644 index 00000000..c88b9117 --- /dev/null +++ b/drivers/char/mux_spi_hal/ipc_spi.c @@ -0,0 +1,1405 @@ +/* + * 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/semaphore.h> +#include <linux/wait.h> +#include <linux/sched.h> +#include <linux/kthread.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/sprdmux.h> +#include <linux/kfifo.h> +#include <linux/spi/spi.h> +#include <linux/fs.h> +#include <asm/uaccess.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> + + +#include <spi_gpio_ctrl.h> +#include <ipc_spi.h> + +#define DRV_NAME "ipc-spi" + +#define IPC_READ_DISABLE 0x01 +#define IPC_WRITE_DISABLE 0x02 + +#define MAX_MIPC_RX_FRAME_NUM 8 +#define MAX_MIPC_TX_FRAME_NUM 5 +#define MAX_MIPC_TX_FRAME_SIZE (8*1024) +#define MAX_MIPC_RX_FRAME_SIZE (8*1024) + +#define IPC_DRIVER_NAME "IPC_SPI" +#define IPC_DBG(f, x...) pr_debug(IPC_DRIVER_NAME " [%s()]: " f, __func__,## x) + +static struct ipc_spi_dev *ipc_dev; + +static u8 send_buf[128]; +static u8 dummy_buf[4] = {0x5a, 0x5a, 0x0,0x0}; +static struct ipc_transfer_frame tx_transfer_frame[MAX_MIPC_TX_FRAME_NUM]; +static struct ipc_transfer_frame rx_transfer_frame[MAX_MIPC_RX_FRAME_NUM]; + + +static u8 read_buf[MAX_RECEIVER_SIZE]; +static u8 write_buf[MAX_MIPC_TX_FRAME_SIZE]; + +struct ipc_spi_dev *g_ipc_spi_dev = NULL; + +extern int modemsts_notifier_register(struct modemsts_chg *handler); + +static int ipc_freelist_Init(struct frame_list* tx_ptr, struct frame_list* rx_ptr) +{ + u32 t = 0; + struct ipc_transfer_frame* frame_ptr = NULL; + + INIT_LIST_HEAD(&tx_ptr->frame_list_head); + mutex_init(&tx_ptr->list_mutex); + tx_ptr->counter = 0; + + memset(tx_transfer_frame, 0x0, sizeof(tx_transfer_frame)); + for(t = 0; t < MAX_MIPC_TX_FRAME_NUM; t++) { + frame_ptr = &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); + if(!frame_ptr->buf_ptr) + return -ENOMEM; + frame_ptr->pos = 0; + frame_ptr->flag = 0; + list_add_tail(&frame_ptr->link, &tx_ptr->frame_list_head); + tx_ptr->counter++; + } + + INIT_LIST_HEAD(&rx_ptr->frame_list_head); + mutex_init(&rx_ptr->list_mutex); + rx_ptr->counter = 0; + + memset(rx_transfer_frame, 0x0, sizeof(rx_transfer_frame)); + for(t = 0; t < MAX_MIPC_RX_FRAME_NUM; t++) { + frame_ptr = &rx_transfer_frame[t]; + frame_ptr->buf_size = MAX_MIPC_TX_FRAME_SIZE; + frame_ptr->buf_ptr = (u8*)kmalloc(frame_ptr->buf_size , GFP_KERNEL); + if(!frame_ptr->buf_ptr) + return -ENOMEM; + frame_ptr->pos = 0; + frame_ptr->flag = 0; + list_add_tail(&frame_ptr->link, &rx_ptr->frame_list_head); + rx_ptr->counter++; + } + return 0; + +} + +static void ipc_transferinit(struct ipc_transfer* transfer_ptr) +{ + INIT_LIST_HEAD(&transfer_ptr->frame_fifo); + mutex_init(&transfer_ptr->transfer_mutex); + transfer_ptr->counter = 0; + transfer_ptr->cur_frame_ptr = NULL; +} + +static void ipc_rvclist_init(struct frame_list* rx_ptr) +{ + INIT_LIST_HEAD(&rx_ptr->frame_list_head); + mutex_init(&rx_ptr->list_mutex); + rx_ptr->counter = 0; +} + +static int ipc_frame_init(struct ipc_spi_dev *dev) +{ + int ret; + ret = ipc_freelist_Init(&dev->tx_free_lst, &dev->rx_free_lst); + if(ret) { + printk(KERN_ERR "ipc free list init failed \n"); + return ret; + } + ipc_rvclist_init(&dev->rx_recv_lst); + ipc_transferinit(&dev->tx_transfer); + dev->rx_curframe = NULL; + dev->tx_curframe = NULL; + return 0; +} + +/*static*/ int ipc_IsTransferFifoEmpty(struct ipc_transfer* 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; +} + +static void ipc_AddFrameToTxTransferFifo(struct ipc_transfer_frame* frame_ptr, struct ipc_transfer* transfer_ptr) +{ + list_add_tail(&frame_ptr->link, &transfer_ptr->frame_fifo); +} + + +/*static*/ u32 ipc_FlushTxTransfer(struct ipc_transfer* transfer_ptr) +{ + u32 ret = 0; + 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 data_packet_header))) { + ret = 1; + printk("ipc_FlushTxTransfer %d, %p \n", transfer_ptr->counter, + transfer_ptr->cur_frame_ptr); + break; + } + ipc_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; +} + +/*static*/ u32 ipc_GetFrameFromTxTransfer(struct ipc_transfer* transfer_ptr, struct ipc_transfer_frame* *out_frame_ptr) +{ + struct ipc_transfer_frame* frame_ptr = NULL; + mutex_lock(&transfer_ptr->transfer_mutex);/*get lock */ + if(!list_empty(&transfer_ptr->frame_fifo)) { + frame_ptr = list_entry(transfer_ptr->frame_fifo.next, struct ipc_transfer_frame, link); + list_del(&frame_ptr->link); + } + mutex_unlock(&transfer_ptr->transfer_mutex);/*set lock */ + + if(!frame_ptr) { + return 1; + } + + *out_frame_ptr = frame_ptr; + return 0; +} + +static void ipc_FreeFrame(struct ipc_transfer_frame *frame_ptr, struct frame_list* frame_list_ptr) +{ + if(!frame_list_ptr || !frame_ptr) { + panic("%s[%d] frame_list_ptr = NULL!\r\n", __FILE__, __LINE__); + } + pr_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; + frame_ptr->seq = 0; + frame_ptr->status = IDLE_STATUS; + 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 */ +} + +static int spi_read_write(struct spi_device *spi, void *rbuf, + const void *wbuf, size_t len) +{ + struct spi_transfer t = { + .rx_buf = rbuf, + .tx_buf = wbuf, + .len = len, + }; + struct spi_message m; + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + return spi_sync(spi, &m); +} + + +static u16 ipc_calculate_len(u16 length) +{ + if(length > MAX_RECEIVER_SIZE) + BUG_ON(1); + if(length % 32) + return (length/32 + 1) * 32; + else + return length; +} + +/*just use for calulate time when debug */ +static void ipc_recordaptime(struct ipc_spi_dev* dev) +{ + dev->ap_info[dev->ap_infocnt%100].time = cpu_clock(0); + dev->ap_info[dev->ap_infocnt%100].status = dev->task_status; + dev->ap_infocnt++; +} + +static size_t ipc_read_write_data(struct ipc_spi_dev* dev, u8* rbuf, u32 rlen, u8* wbuf, u32 wlen) +{ + size_t ret = 0; + rlen = ipc_calculate_len(rlen); + wlen = ipc_calculate_len(wlen); + ap2cp_wakeup(); + ap2cp_enable(); + dev->tx_irqennum++; + dev->task_status = 5; + ipc_recordaptime(dev); + wait_event(dev->wait, (dev->rx_ctl == CP2AP_RASING) || !dev->ipc_enable); + if(!dev->ipc_enable) + return -1; + dev->task_status = 6; + dev->rx_ctl = CP2AP_NOIRQ; + if(wlen == 0) + ret = spi_read_write(dev->spi,rbuf, dummy_buf, rlen); + else + ret = spi_read_write(dev->spi,rbuf, wbuf, (rlen > wlen)? rlen : wlen); + ap2cp_disable(); + ap2cp_sleep(); + dev->tx_irqdisnum++; + dev->task_status = 7; + wait_event(dev->wait, dev->rx_ctl || !dev->ipc_enable); + if(!dev->ipc_enable) + return -1; + dev->task_status = 8; + dev->rwnum++; + return ret; + +} + +static void ipc_freerxrcvframe(struct ipc_spi_dev* dev, struct ipc_transfer_frame *p_frame) +{ + struct frame_list *pfreelist = &dev->rx_free_lst; + list_del(&p_frame->link); + ipc_FreeFrame(p_frame, pfreelist); +} + +static u16 ipc_checksum(const unsigned char *src, int len) +{ + unsigned long sum =0; + int size1 = len >> 1; + int size2 = len & 0x01; + unsigned short *data = (unsigned short *)src; + + if(len){ + while (size1-- > 0) + { + sum += *data++; + } + + if (size2) + { + sum +=src[len - 1]; + } + } + sum = (sum >> 16)+(sum & 0xFFFF); + sum += (sum >> 16); + return ~sum; +} + + +static void ipc_ack(struct ipc_spi_dev* dev, u8* buf, u16* len) +{ + struct ack_packet *ack = (struct ack_packet*)buf; + bool bfound = false; + struct frame_list *plist = &dev->rx_recv_lst; + struct ipc_transfer_frame *p_frame; + mutex_lock(&plist->list_mutex); + list_for_each_entry(p_frame, &plist->frame_list_head, link) { + if(bfound) { + if(p_frame->status == ack->type) { + if(p_frame->seq == ack->seq_end + 1) { + ack->seq_end = p_frame->seq; + if(ack->type == TYPE_ACK) { + //dev->rx_seqnum = p_frame->seq; + } + ipc_freerxrcvframe(dev, p_frame); + p_frame = list_entry(&plist->frame_list_head, struct ipc_transfer_frame, link); + continue; + } + } + break; + } + if(p_frame->status == ACK_STATUS || p_frame->status == NAK_STATUS) { + bfound = true; + ack->seq_begin = p_frame->seq; + ack->seq_end = p_frame->seq; + ack->magic = ACK_MAGIC; + if(p_frame->status == ACK_STATUS) { + ack->type = TYPE_ACK; + //dev->rx_seqnum = p_frame->seq; + } + else + ack->type = TYPE_NAK; + *len = sizeof(struct ack_packet); + ipc_freerxrcvframe(dev, p_frame); + p_frame = list_entry(&plist->frame_list_head, struct ipc_transfer_frame, link); + } + } + ack->checksum = ipc_checksum((const unsigned char*)&ack->type, sizeof(struct ack_packet) - 4); + mutex_unlock(&plist->list_mutex); +} + +/*static*/ void ipc_setup(struct ipc_spi_dev* dev, u8* buf, u16* len) +{ + bool bfound = false; + u32 offset = sizeof(struct data_packet_header); + struct data_packet_header *header; + struct ipc_transfer *ptransfer = &dev->tx_transfer; + struct ipc_transfer_frame* frame_ptr = NULL; + struct ack_packet *ack = (struct ack_packet*)buf; + + if(dev->rx_status == NAKALL_STATUS) { + ack->seq_begin = 0; + ack->seq_end = 0; + ack->magic = ACK_MAGIC; + ack->type = TYPE_NAKALL; + *len = sizeof(struct ack_packet); + ack->checksum = ipc_checksum((const unsigned char*)&ack->type, sizeof(struct ack_packet) - 4); + //dev->rx_status = IDLE_STATUS; + return; + } + + if(ptransfer->counter) { + /*if(ipc_IsTransferFifoEmpty(ptransfer)) { + if(ipc_FlushTxTransfer(ptransfer)) { + BUG_ON(1); + } + }*/ + mutex_lock(&ptransfer->transfer_mutex); + /* + if(dev->remote_status == MODEM_STATUS_ASSERT) { + if(ptransfer->cur_frame_ptr && (ptransfer->cur_frame_ptr->pos > sizeof( struct data_packet_header))) { + ipc_AddFrameToTxTransferFifo(ptransfer->cur_frame_ptr, ptransfer); + ptransfer->cur_frame_ptr = NULL; + } + } */ + list_for_each_entry(frame_ptr, &ptransfer->frame_fifo, link) { + if(frame_ptr->status == IDLE_STATUS) { + bfound = true; + break; + }/* else { + if(dev->remote_status == MODEM_STATUS_ASSERT) + break; + }*/ + } + if(!bfound/* && (dev->remote_status != MODEM_STATUS_ASSERT)*/) { + if(ptransfer->cur_frame_ptr && (ptransfer->cur_frame_ptr->pos > sizeof( struct data_packet_header))) { + ipc_AddFrameToTxTransferFifo(ptransfer->cur_frame_ptr, ptransfer); + frame_ptr = ptransfer->cur_frame_ptr; + ptransfer->cur_frame_ptr = NULL; + bfound = true; + } + } + mutex_unlock(&ptransfer->transfer_mutex); + if(bfound) { + dev->tx_curframe = frame_ptr; + header = (struct data_packet_header*)frame_ptr->buf_ptr; + ipc_ack(dev, (u8*)(&header->ack), len); + if(*len > 0) + header->magic = DATA_ACK_MAGIC; + else + header->magic = DATA_MAGIC; + header->len = frame_ptr->pos - offset; + header->seqnum = frame_ptr->seq; + header->checksum = ipc_checksum(&frame_ptr->buf_ptr[4], header->len + offset - 4); + *len = MAX_MIPC_TX_FRAME_SIZE; + frame_ptr->status = ACK_STATUS; + IPC_DBG("--seq %d \n", header->seqnum); + return; + } + + } + ipc_ack(dev, buf, len); +} + +/*static*/ void ipc_prepare_send(struct ipc_spi_dev* dev, u8* buf, + u16* len) +{ + + ipc_setup(dev, buf, len); +} + +struct ipc_transfer_frame* ipc_AllocFrame(struct frame_list* frame_list_ptr) +{ + struct ipc_transfer_frame *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, struct ipc_transfer_frame, link); + list_del(&frame_ptr->link); + frame_ptr->pos = sizeof(struct data_packet_header); + frame_ptr->flag = 1; + frame_list_ptr->counter--; + } + mutex_unlock(&frame_list_ptr->list_mutex);/*set lock */ + pr_debug("_AllocFrame:0x%X\r\n", (u32)frame_ptr); + return frame_ptr; +} + + +static void ipc_prepare_read(struct ipc_spi_dev* dev, u16* len) +{ + if(dev->rx_curframe == NULL) { + dev->rx_curframe = ipc_AllocFrame(&dev->rx_free_lst); + if(!dev->rx_curframe) + BUG_ON(1); + } + dev->rx_curframe->status = IDLE_STATUS; + *len = MAX_MIPC_RX_FRAME_SIZE; +} + +static inline int ipc_txfreelist_empty(struct ipc_spi_dev *dev) +{ + struct frame_list *frame_lst = &dev->tx_free_lst; + return list_empty(&frame_lst->frame_list_head); +} + +static u32 ipc_AddDataToTxTransfer(struct ipc_spi_dev *dev, u8* data_ptr, u32 len) +{ + u32 ret = 0; + u32 index = 0; + u32 remain_size = 0; + struct ipc_transfer_frame *frame_ptr = NULL; + struct ipc_transfer_frame *new_frame_ptr = NULL; + struct ipc_transfer *transfer_ptr = &dev->tx_transfer; + + IPC_DBG("Enter ipc_AddDataToTxTransfer %d \n", len); + mutex_lock(&transfer_ptr->transfer_mutex);/*get lock */ + + do { + frame_ptr = transfer_ptr->cur_frame_ptr; + if(!frame_ptr) { + frame_ptr = ipc_AllocFrame(&dev->tx_free_lst); + if(!frame_ptr) { + break; + } + frame_ptr->seq = dev->tx_seqnum++; + } + remain_size = frame_ptr->buf_size - frame_ptr->pos; + if(len > remain_size) { + //memcpy(&frame_ptr->buf_ptr[frame_ptr->pos], data_ptr + index, remain_size); + //frame_ptr->pos = frame_ptr->buf_size; + //transfer_ptr->counter += remain_size; + transfer_ptr->cur_frame_ptr = NULL; + ipc_AddFrameToTxTransferFifo(frame_ptr, transfer_ptr); + //ret += remain_size; + //index += remain_size; + //len -= remain_size; + new_frame_ptr = ipc_AllocFrame(&dev->tx_free_lst); + if(!new_frame_ptr) { + break; + } + new_frame_ptr->seq = dev->tx_seqnum++; + frame_ptr = new_frame_ptr; + transfer_ptr->cur_frame_ptr = frame_ptr; + } else { + memcpy(&frame_ptr->buf_ptr[frame_ptr->pos], data_ptr + index, len); + frame_ptr->pos += len; + transfer_ptr->counter += len; + transfer_ptr->cur_frame_ptr = frame_ptr; + ret += len; + break; + } + }while(len > 0); + if(ret > 0) + dev->bneedsend = true; + mutex_unlock(&transfer_ptr->transfer_mutex);/*set lock */ + IPC_DBG("Enter ipc_AddDataToTxTransfer %d - \n", ret); + return ret; +} + +static u32 ipc_DelDataFromTxTransfer(struct ipc_spi_dev *dev, + struct ipc_transfer_frame *frame_ptr) +{ + struct ipc_transfer* transfer_ptr = &dev->tx_transfer; + mutex_lock(&transfer_ptr->transfer_mutex); + list_del(&frame_ptr->link); + transfer_ptr->counter -= frame_ptr->pos - sizeof(struct data_packet_header); + mutex_unlock(&transfer_ptr->transfer_mutex); + ipc_FreeFrame(frame_ptr, &dev->tx_free_lst); + wake_up(&dev->tx_frame_wait); + return 0; +} + +/*static */void ipc_FreeAllTxTransferFrame(struct ipc_spi_dev *dev) +{ + struct ipc_transfer_frame *frame_ptr = NULL; + struct ipc_transfer *p_transfer = &dev->tx_transfer; + mutex_lock(&p_transfer->transfer_mutex); + list_for_each_entry(frame_ptr, &p_transfer->frame_fifo, link) { + printk("+-+- %d, %d \n", frame_ptr->pos, frame_ptr->seq); + mutex_unlock(&p_transfer->transfer_mutex); + ipc_DelDataFromTxTransfer(dev, frame_ptr); + mutex_lock(&p_transfer->transfer_mutex); + frame_ptr = list_entry(&p_transfer->frame_fifo, struct ipc_transfer_frame, link); + } + mutex_unlock(&p_transfer->transfer_mutex); +} + +static void ipc_freeallrxframe(struct ipc_spi_dev *dev) +{ + struct ipc_transfer_frame *frame_ptr; + struct frame_list *plist = &dev->rx_recv_lst; + mutex_lock(&plist->list_mutex); + list_for_each_entry(frame_ptr, &plist->frame_list_head, link) { + list_del(&frame_ptr->link); + ipc_FreeFrame(frame_ptr, &dev->rx_free_lst); + frame_ptr = list_entry(&plist->frame_list_head, struct ipc_transfer_frame, link); + } + mutex_unlock(&plist->list_mutex); +} + +static void ipc_print_errdata(u8 *buf, u16 len) +{ + int i; + printk("++++++++++++++++++++++++\n"); + for(i = 0; i < 128;) { + + printk("data: 0x%x, 0x%x, 0x%x, 0x%x \n", buf[i], buf[i+1], buf[i+2], buf[i+3]); + i+=4; + } + printk("error data \n"); + printk("++++++++++++++++++++++++\n"); +} + +static void ipc_AddFrameToRxrcvlist(struct ipc_spi_dev* dev, struct ipc_transfer_frame *frame, + struct frame_list *plist) +{ + struct ipc_transfer_frame* tframe; + bool binsert = false; + mutex_lock(&plist->list_mutex); + list_for_each_entry(tframe, &plist->frame_list_head, link) { + if(tframe->seq > frame->seq) { + list_add_tail(&frame->link, &tframe->link); + binsert = true; + break; + } else if (tframe->seq == frame->seq) { + if(frame->status == ACK_STATUS) { + tframe->status = ACK_STATUS; + mutex_unlock(&plist->list_mutex); + return; + } + if(tframe->status == NAK_STATUS && frame->status == IDLE_STATUS) { + list_add_tail(&frame->link, &tframe->link); + ipc_freerxrcvframe(dev, tframe); + binsert = true; + } else if(tframe->status == NAKALL_STATUS) { + tframe->status = IDLE_STATUS; + dev->bneedread = true; + mutex_unlock(&plist->list_mutex); + return; + } else { + mutex_unlock(&plist->list_mutex); + return; + } + break; + } + } + if(!binsert) { + list_add_tail(&frame->link, &plist->frame_list_head); + } + dev->rx_curframe = NULL; + dev->bneedread = true; + mutex_unlock(&plist->list_mutex); +} + +static void ipc_rcv_data(struct ipc_spi_dev* dev, u8 *buf, u16 len) +{ + u32 offset = sizeof(struct data_packet_header); + struct data_packet_header *header = (struct data_packet_header*)buf; + //u16 cs = ipc_checksum(buf + 4, header->len); + if((header->len <= MAX_MIPC_RX_FRAME_SIZE - offset) && (header->checksum == ipc_checksum(buf + 4, header->len + offset - 4))) { + if(dev->rx_seqnum >= header->seqnum) { + dev->rx_curframe->status = ACK_STATUS; + } + } else { + printk("ipc_spi error data data checksum error \n"); + ipc_print_errdata(buf, len); + if(dev->rx_seqnum >= header->seqnum) + dev->rx_curframe->status = ACK_STATUS; + else + dev->rx_curframe->status = NAK_STATUS; + } + IPC_DBG("++seq %d \n", header->seqnum); + dev->rx_curframe->seq = header->seqnum; + dev->rx_curframe->pos = offset; + dev->rx_curframe->buf_size = header->len + offset; + ipc_AddFrameToRxrcvlist(dev,dev->rx_curframe, &dev->rx_recv_lst); + //dev->rx_curframe = NULL; + wake_up(&dev->rx_read_wait); +} + +static void ipc_process_errdata(struct ipc_spi_dev* dev) +{ + struct ipc_transfer_frame *frame; + struct frame_list *plist = &dev->rx_recv_lst; + struct ipc_transfer *p_transfer = &dev->tx_transfer; + mutex_lock(&p_transfer->transfer_mutex); + list_for_each_entry(frame, &p_transfer->frame_fifo, link) { + frame->status = IDLE_STATUS; + } + mutex_unlock(&p_transfer->transfer_mutex); + + mutex_lock(&plist->list_mutex); + list_for_each_entry(frame, &plist->frame_list_head, link) { + if(frame->status == IDLE_STATUS || frame->status == ACK_STATUS) { + frame->status = NAKALL_STATUS; + } else if(frame->status == NAK_STATUS) { + ipc_freerxrcvframe(dev, frame); + frame = list_entry(&plist->frame_list_head, struct ipc_transfer_frame, link); + } + } + mutex_unlock(&plist->list_mutex); + dev->rx_status = NAKALL_STATUS; +} + +static void ipc_rcv_ack(struct ipc_spi_dev* dev, u8 *buf, bool bcheck) +{ + u32 seqnum; + struct ipc_transfer_frame *frame; + struct ipc_transfer *p_transfer = &dev->tx_transfer; + struct ack_packet *header = (struct ack_packet*)buf; + IPC_DBG("ipc_rcv_ack %d, %d, %d \n", header->type, header->seq_begin, header->seq_end); + if(bcheck + && (header->checksum != ipc_checksum((const unsigned char*)&header->type, sizeof(struct ack_packet) - 4))) { + printk(KERN_ERR "ipc_spi wrong ack packet1 \n"); + ipc_print_errdata(buf, sizeof(struct ack_packet)); + ipc_process_errdata(dev); + return; + } + if(header->type == TYPE_ACK) { + mutex_lock(&p_transfer->transfer_mutex); + list_for_each_entry(frame, &p_transfer->frame_fifo, link) { + for(seqnum = header->seq_begin; seqnum < header->seq_end + 1; seqnum++) { + if(frame->seq == seqnum) { + mutex_unlock(&p_transfer->transfer_mutex); + ipc_DelDataFromTxTransfer(dev, frame); + mutex_lock(&p_transfer->transfer_mutex); + frame = list_entry(&p_transfer->frame_fifo, struct ipc_transfer_frame, link); + } + } + } + mutex_unlock(&p_transfer->transfer_mutex); + } else if(header->type == TYPE_NAK) { + printk(KERN_WARNING "ipc receive nak status %d, %d \n", header->seq_begin, header->seq_end); + mutex_lock(&p_transfer->transfer_mutex); + list_for_each_entry(frame, &p_transfer->frame_fifo, link) { + for(seqnum = header->seq_begin; seqnum < header->seq_end + 1; seqnum++) { + if(frame->seq == seqnum) + frame->status = IDLE_STATUS; + } + } + mutex_unlock(&p_transfer->transfer_mutex); + } else if(header->type == TYPE_NAKALL) { + struct frame_list *plist = &dev->rx_recv_lst; + printk(KERN_WARNING "ipc receive nakall status \n"); + mutex_lock(&p_transfer->transfer_mutex); + list_for_each_entry(frame, &p_transfer->frame_fifo, link) { + frame->status = IDLE_STATUS; + } + mutex_unlock(&p_transfer->transfer_mutex); + + mutex_lock(&plist->list_mutex); + list_for_each_entry(frame, &plist->frame_list_head, link) { + if(frame->status == IDLE_STATUS || frame->status == ACK_STATUS) { + frame->status = NAKALL_STATUS; + } else if(frame->status == NAK_STATUS) { + ipc_freerxrcvframe(dev, frame); + frame = list_entry(&plist->frame_list_head, struct ipc_transfer_frame, link); + } + } + mutex_unlock(&plist->list_mutex); + } else { + ipc_print_errdata(buf, sizeof(struct ack_packet)); + ipc_process_errdata(dev); + printk(KERN_ERR "ipc_spi wrong ack packet \n"); + } +} + +static void ipc_rcv_dataack(struct ipc_spi_dev* dev, u8 *buf, u16 len) +{ + u32 offset = sizeof(struct data_packet_header); + struct data_packet_header *header = (struct data_packet_header*)buf; + if((header->len <= MAX_MIPC_RX_FRAME_SIZE - offset) && (header->checksum == ipc_checksum(buf + 4, header->len + offset - 4))) { + struct data_packet_header *data_header; + data_header = (struct data_packet_header*)buf; + ipc_rcv_ack(dev, (u8*)&(data_header->ack), false); + ipc_rcv_data(dev, buf, len); + } else { + printk(KERN_ERR "ipc_spi dataack checksum error \n"); + ipc_print_errdata(buf, len); + ipc_process_errdata(dev); + return; + } +} + +static void ipc_process_rcvdata(struct ipc_spi_dev* dev, u8 *buf, u16 len) +{ + u16 *magic = (u16*)buf; + #if 0 + static int i = 0; + i++; + if((i % 100) == 0) { + struct data_packet_header *data_header; + data_header = (struct data_packet_header*)buf; + printk("** %d, %d \n", data_header->magic, data_header->seqnum); + ipc_process_errdata(dev); + return; + } + #endif + IPC_DBG("ipc_process_rcvdata 0x%x \n", *magic); + if(dev->rx_status == NAKALL_STATUS) { + dev->rx_status = IDLE_STATUS; + return; + } + if(DATA_MAGIC == *magic) + ipc_rcv_data(dev, buf, len); + else if(ACK_MAGIC == *magic) + ipc_rcv_ack(dev, buf, true); + else if (DATA_ACK_MAGIC == *magic) { + ipc_rcv_dataack(dev, buf, len); + } else if(DUMMY_MAGIC == *magic) { + IPC_DBG("ipc dump data just discard \n"); + } else { + printk(KERN_ERR "ipc wrong data need to tell remote peer \n"); + ipc_print_errdata(buf, len); + ipc_process_errdata(dev); + } +} + +static void ipc_process_send(struct ipc_spi_dev* dev) +{ +} + +static inline void ipc_hasdata(struct ipc_spi_dev* dev) +{ + bool bread = false; + struct ipc_transfer_frame *frame; + struct frame_list *plist = &dev->rx_recv_lst; + mutex_lock(&plist->list_mutex); + list_for_each_entry(frame, &plist->frame_list_head, link) { + if((frame->status == IDLE_STATUS) + && (frame->seq == dev->rx_seqnum + 1) + && (frame->buf_size - frame->pos > 0)) { + bread= true; + break; + } + } + dev->bneedread = bread; + mutex_unlock(&plist->list_mutex); +} + +static int ipc_readdatafromrvclist(struct ipc_spi_dev* dev, + char *buf, size_t count) +{ + u32 index = 0; + struct ipc_transfer_frame *frame; + struct frame_list *plist = &dev->rx_recv_lst; + mutex_lock(&plist->list_mutex); + list_for_each_entry(frame, &plist->frame_list_head, link) { + if((frame->status == IDLE_STATUS) + && (frame->seq == dev->rx_seqnum + 1) + && (frame->buf_size - frame->pos > 0)) { + if(frame->buf_size - frame->pos > count) { + memcpy(&buf[index], &frame->buf_ptr[frame->pos], count); + frame->pos += count; + index += count; + break; + } else if(frame->buf_size - frame->pos == count) { + memcpy(&buf[index], &frame->buf_ptr[frame->pos], count); + frame->pos += count; + frame->status = ACK_STATUS; + index += count; + dev->rx_seqnum++; + dev->bneedsend = true; + break; + } else { + memcpy(&buf[index], &frame->buf_ptr[frame->pos], frame->buf_size - frame->pos); + index += frame->buf_size - frame->pos; + count -= frame->buf_size - frame->pos; + frame->pos = frame->buf_size; + frame->status = ACK_STATUS; + dev->rx_seqnum++; + dev->bneedsend = true; + } + } + } + mutex_unlock(&plist->list_mutex); + return index; +} + +static int mux_ipc_spi_read(char *buf, size_t count) +{ + int ret = 0; + struct ipc_spi_dev *dev = ipc_dev; + + ipc_hasdata(dev); + wait_event(dev->rx_read_wait, dev->bneedread + || (dev->rwctrl & IPC_READ_DISABLE)); + + if(dev->rwctrl & IPC_READ_DISABLE) { + printk(KERN_ERR "read was disabled \n"); + return -1; + } + IPC_DBG("[mipc]mux_ipc_spi_read read len:%d\r\n", count); + ret = ipc_readdatafromrvclist(dev, buf, count); + if(ret) + wake_up(&dev->wait); + return ret; +} + +static int mux_ipc_spi_write(const char *buf, size_t count) +{ + ssize_t ret = 0; + struct ipc_spi_dev *dev = ipc_dev; + + if(dev->rwctrl & IPC_WRITE_DISABLE) { + printk(KERN_ERR "write was disabled \n"); + return -1; + } + if(count == 0) { + printk("send count equal zero ? \n"); + return ret; + } + IPC_DBG("Enter mux_ipc_spi_write %d \n", count); + do { + ret = ipc_AddDataToTxTransfer(dev, (u8*)buf, count); + if(!ret) { + wait_event(dev->tx_frame_wait, !ipc_txfreelist_empty(dev)); + } + } while(!ret); + + /* if tx transfer is empty so need to wake up thread */ + if(ret) { + IPC_DBG("ipc write wake up \n"); + wake_up(&dev->wait); + } + IPC_DBG("Enter mux_ipc_spi_write end %d \n", ret); + return ret; +} + +int mux_ipc_spi_stop(int mode) +{ + struct ipc_spi_dev *dev = ipc_dev; + if(mode & SPRDMUX_READ) + { + dev->rwctrl |= IPC_READ_DISABLE; + } + + if(mode & SPRDMUX_WRITE) + { + dev->rwctrl |= IPC_WRITE_DISABLE; + } + + wake_up(&dev->rx_read_wait); + + return 0; +} + + +static struct sprdmux sprd_iomux = { + .id = SPRDMUX_ID_SPI, + .io_read = mux_ipc_spi_read, + .io_write = mux_ipc_spi_write, + .io_stop = mux_ipc_spi_stop, +}; + +static void spi_ipc_enable(struct modemsts_chg *s, unsigned int is_enable) +{ + struct ipc_spi_dev *dev = ipc_dev; + printk("spi_ipc_enable %d, %d \n", is_enable, dev->task_status); + if(is_enable == MODEM_STATUS_POWERON) { + spi_hal_gpio_irq_init(dev); + return ; + } + if(is_enable == MODEM_STATUS_ALVIE) { + dev->rwctrl = 0; + //dev->ipc_enable = true; + //dev->remote_status = REMOTE_ALIVE_STATUS; + } else if(is_enable == MODEM_STATUS_REBOOT){ + ap2cp_disable(); + dev->ipc_enable = false; + } + dev->remote_status = is_enable; + wake_up(&dev->wait); + IPC_DBG("[mipc]mux ipc enable:0x%x, status:0x%x\r\n", is_enable, dev->ipc_enable); +} + +static void ipc_reset(struct ipc_spi_dev *dev) +{ + dev->rx_ctl = false; + dev->bneedrcv = false; + dev->bneedread = false; + dev->bneedsend = false; + dev->rx_status = IDLE_STATUS; + dev->tx_seqnum = 1; + dev->rx_seqnum = 0; + dev->irq_num = 0; + dev->rwnum = 0; + dev->tx_irqennum = 0; + dev->tx_irqdisnum = 0; + dev->ap_infocnt = 0; +} + +static inline bool ipc_cansend(struct ipc_spi_dev *dev) +{ + bool bsend = false; + struct ipc_transfer_frame* frame_ptr = NULL; + struct ipc_transfer *ptransfer = &dev->tx_transfer; + struct frame_list *plist = &dev->rx_recv_lst; + if(dev->rx_status == NAKALL_STATUS) + return true; + mutex_lock(&ptransfer->transfer_mutex); + if(ptransfer->cur_frame_ptr) { + if(ptransfer->cur_frame_ptr->pos > sizeof(struct data_packet_header)) + bsend = true; + } + if(!bsend) { + list_for_each_entry(frame_ptr, &ptransfer->frame_fifo, link) { + if(frame_ptr->status == IDLE_STATUS) { + bsend = true; + break; + } + } + } + dev->bneedsend = bsend; + mutex_unlock(&ptransfer->transfer_mutex); + if(bsend) + return bsend; + + mutex_lock(&plist->list_mutex); + list_for_each_entry(frame_ptr, &plist->frame_list_head, link) { + if(frame_ptr->status == ACK_STATUS || frame_ptr->status == NAK_STATUS) { + bsend = true; + break; + } + } + dev->bneedsend = bsend; + mutex_unlock(&plist->list_mutex); + return bsend; +} + +static int mux_ipc_thread(void *data) +{ + int rval = 0; + struct ipc_spi_dev *dev = (struct ipc_spi_dev*)data; + struct sched_param param = {.sched_priority = 10}; + + IPC_DBG("mux_ipc_tx_thread"); + sched_setscheduler(current, SCHED_FIFO, ¶m); + + spi_hal_gpio_init(); + + IPC_DBG("%s() spi channel opened %d\n", __func__, rval); + //spi_hal_gpio_irq_init(dev); + dev->task_count = 0; + dev->task_status = 0; + + while (!kthread_should_stop()) { + dev->task_count++; + dev->task_status = 1; + wait_event(dev->wait, dev->remote_status || !dev->ipc_enable); + dev->task_status = 2; + wait_event(dev->wait, dev->bneedsend || dev->bneedrcv || !dev->ipc_enable); + dev->task_status = 3; + ipc_recordaptime(dev); + + IPC_DBG("Enter mux_ipc_thread %d, %d \n", + dev->tx_transfer.counter, dev->bneedrcv); + if(!dev->ipc_enable) { + printk(KERN_ERR "ipc was disabled , there must something wrong! \n"); + ipc_reset(dev); + ipc_FlushTxTransfer(&dev->tx_transfer); + ipc_FreeAllTxTransferFrame(dev); + ipc_freeallrxframe(dev); + ap2cp_disable(); + //dev->rwctrl = 0; + dev->ipc_enable = true; + continue; + } +#ifdef CONFIG_PM + wait_event(dev->wait, !dev->bsuspend); +#endif + wake_lock(&dev->wake_lock); + while(ipc_cansend(dev) || dev->bneedrcv) { + u16 tx_len = 0; + u16 rx_len = 0; + ipc_prepare_send(dev, send_buf, &tx_len); + ipc_prepare_read(dev, &rx_len); + IPC_DBG("ipc done prepare %d, %d \n", tx_len, rx_len); + dev->task_status = 4; + if(tx_len == MAX_MIPC_TX_FRAME_SIZE) { + rval = ipc_read_write_data(dev, dev->rx_curframe->buf_ptr, rx_len, dev->tx_curframe->buf_ptr, tx_len); + } else + rval = ipc_read_write_data(dev, dev->rx_curframe->buf_ptr, rx_len, send_buf, tx_len); + IPC_DBG("ipc_read_write_data rval %d \n", rval); + if(rval < 0) + break; + dev->task_status = 9; + ipc_process_rcvdata(dev, dev->rx_curframe->buf_ptr, rx_len); + dev->task_status = 10; + ipc_process_send(dev); + dev->task_status = 11; + } + wake_unlock(&dev->wake_lock); + } + spi_hal_gpio_exit(); + dev->task_status = 12; + + return 0; +} + +static int ipc_fopen(struct inode *inode, struct file *filp) +{ + struct ipc_spi_dev *dev = container_of(filp->private_data, + struct ipc_spi_dev, miscdev); + IPC_DBG("enter ipc_fopen \n"); + filp->private_data = dev; + return 0; +} + +static ssize_t ipc_fread(struct file *filp, char __user *buf, size_t count, loff_t *ppos) +{ + ssize_t ret; + IPC_DBG("enter ipc_fread \n"); + ret = mux_ipc_spi_read(read_buf, count); + if(ret > 0) { + if (copy_to_user(buf, read_buf, ret)) { + printk(KERN_ERR "ipc_fread copy data to user error !\n"); + return -EFAULT; + } + } + *ppos += ret; + return ret; +} + +static ssize_t ipc_fwrite(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) +{ + ssize_t ret; + IPC_DBG("enter ipc_fwrite \n"); + if (copy_from_user(write_buf, buf, count)) { + return -EFAULT; + } + ret = mux_ipc_spi_write(write_buf, count); + if(ret > 0) + *ppos += ret; + return ret; +} + + +static const struct file_operations ipc_fops = { + .owner = THIS_MODULE, + .open = ipc_fopen, + .read = ipc_fread, + .write = ipc_fwrite, +}; + + +#if defined(CONFIG_DEBUG_FS) +static int ipc_debug_show(struct seq_file *m, void *private) +{ + u32 *sdata = NULL; + struct ipc_spi_dev *dev = ipc_dev; + struct ipc_transfer *p_transfer = &dev->tx_transfer; + struct ipc_transfer_frame *frame_ptr = NULL; + struct frame_list *frame_lst = NULL; + seq_printf(m, "======================================================================\n"); + seq_printf(m, "ipc_enable: %d: rwctrl: %d\n", dev->ipc_enable, dev->rwctrl); + seq_printf(m, "bneedrcv: %d\n", dev->bneedrcv); + seq_printf(m, "rx_ctl: %d\n", dev->rx_ctl); + seq_printf(m, "rx status: %d rx_seqnum: %d\n", + dev->rx_status, dev->rx_seqnum); + seq_printf(m, "tx_seqnum: %d, remote_status: %d \n", dev->tx_seqnum, dev->remote_status); + seq_printf(m, "tx_transfer.counter: %d: \n", dev->tx_transfer.counter); + seq_printf(m, "tx_free_lst.counter: %d: \n", dev->tx_free_lst.counter); + seq_printf(m, "ap2cp_sts: %d: cp2ap_sts: %d\n", ap2cp_sts(), cp2ap_sts()); + seq_printf(m, "task_count: %d: task_status: %d\n", dev->task_count, dev->task_status); + seq_printf(m, "irq_num: %d rwnum: %d\n", dev->irq_num, dev->rwnum); + seq_printf(m, "tx_irqennum: %d tx_irqdisnum: %d\n", dev->tx_irqennum, dev->tx_irqdisnum); + + seq_printf(m, "======================================================================\n"); + + + seq_printf(m, "*******send data: *********\n"); + sdata = (u32*)send_buf; + seq_printf(m, "0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n", + sdata[0], sdata[1], sdata[2], sdata[3], sdata[4], sdata[5], sdata[6], sdata[7]); + seq_printf(m, "0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n", + sdata[8], sdata[9], sdata[10], sdata[11], sdata[12], sdata[13], sdata[14], sdata[15]); + seq_printf(m, "0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n", + sdata[16], sdata[17], sdata[18], sdata[19], sdata[20], sdata[21], sdata[22], sdata[23]); + seq_printf(m, "0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n", + sdata[24], sdata[25], sdata[26], sdata[27], sdata[28], sdata[29], sdata[30], sdata[31]); + seq_printf(m, "*******receive data: *********\n"); + + seq_printf(m, "======================================================================\n"); + seq_printf(m, "show tx transfer list\n"); + mutex_lock(&p_transfer->transfer_mutex); + list_for_each_entry(frame_ptr, &p_transfer->frame_fifo, link) { + seq_printf(m, "frameptr: %p, pos: %d, seq: %d status: %d \n", frame_ptr, frame_ptr->pos, frame_ptr->seq, frame_ptr->status); + } + mutex_unlock(&p_transfer->transfer_mutex); + seq_printf(m, "======================================================================\n"); + seq_printf(m, "show tx free list\n"); + frame_lst = &dev->tx_free_lst; + mutex_lock(&frame_lst->list_mutex); + list_for_each_entry(frame_ptr, &frame_lst->frame_list_head, link) { + seq_printf(m, "frameptr: %p, pos: %d, seq: %d status: %d \n", frame_ptr, frame_ptr->pos, frame_ptr->seq, frame_ptr->status); + } + mutex_unlock(&frame_lst->list_mutex); + seq_printf(m, "======================================================================\n"); + seq_printf(m, "show rx receive list\n"); + frame_lst = &dev->rx_recv_lst; + mutex_lock(&frame_lst->list_mutex); + list_for_each_entry(frame_ptr, &frame_lst->frame_list_head, link) { + seq_printf(m, "frameptr: %p, pos: %d, seq: %d status: %d \n", frame_ptr, frame_ptr->pos, frame_ptr->seq, frame_ptr->status); + } + mutex_unlock(&frame_lst->list_mutex); + seq_printf(m, "======================================================================\n"); + seq_printf(m, "show rx free list\n"); + frame_lst = &dev->rx_free_lst; + mutex_lock(&frame_lst->list_mutex); + list_for_each_entry(frame_ptr, &frame_lst->frame_list_head, link) { + seq_printf(m, "frameptr: %p, pos: %d, seq: %d status: %d \n", frame_ptr, frame_ptr->pos, frame_ptr->seq, frame_ptr->status); + } + mutex_unlock(&frame_lst->list_mutex); + seq_printf(m, "will show mipc_thread stack in kernel log \n"); + show_stack(dev->task,NULL); + + return 0; +} + +static int ipc_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, ipc_debug_show, inode->i_private); +} + +static const struct file_operations ipc_debug_fops = { + .open = ipc_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int ipc_debug_show1(struct seq_file *m, void *private) +{ + u32 max ; + u32 i; + struct ipc_spi_dev *dev = ipc_dev; + + dev->rx_status = NAKALL_STATUS; + seq_printf(m, "**********************************************************************\n"); + seq_printf(m, "show cp2ap irq time:\n"); + max = (dev->irq_num > 100) ? 100: dev->irq_num; + for(i = 0; i < max; i++) { + unsigned long long t; + unsigned long nanosec_rem; + + t = dev->cp2ap[i].time; + nanosec_rem = do_div(t, 1000000000); + seq_printf(m, "[%5lu.%06lu], status: %d\n", (unsigned long) t, nanosec_rem / 1000, + dev->cp2ap[i].status); + } + + seq_printf(m, "**********************************************************************\n"); + seq_printf(m, "show ap2cp irq time:\n"); + max = (dev->ap_infocnt > 100) ? 100: dev->ap_infocnt; + for(i = 0; i < max; i++) { + unsigned long long t; + unsigned long nanosec_rem; + + t = dev->ap_info[i].time; + nanosec_rem = do_div(t, 1000000000); + seq_printf(m, "[%5lu.%06lu], status: %d\n", (unsigned long) t, nanosec_rem / 1000, + dev->ap_info[i].status); + } + + seq_printf(m, "**********************************************************************\n"); + + return 0; +} + + +static int ipc_debug_open1(struct inode *inode, struct file *file) +{ + return single_open(file, ipc_debug_show1, inode->i_private); +} + +static const struct file_operations ipc_debug_fops1 = { + .open = ipc_debug_open1, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int ipc_init_debugfs(void) +{ + struct dentry *root = debugfs_create_dir("ipc-spi", NULL); + if (!root) + return -ENXIO; + debugfs_create_file("status", S_IRUGO, (struct dentry *)root, NULL, &ipc_debug_fops); + debugfs_create_file("timeinfo", S_IRUGO, (struct dentry *)root, NULL, &ipc_debug_fops1); + return 0; +} + +#endif /* CONFIG_DEBUG_FS */ + +static int __init ipc_spi_probe(struct spi_device *spi) +{ + struct ipc_spi_dev *dev; + int ret = 0; + int i; + printk("Enter ipc_spi_probe ..\n"); + dev = kzalloc(sizeof(struct ipc_spi_dev), GFP_KERNEL); + if (!dev) { + ret = -ENOMEM; + goto error_ret; + } + spi_set_drvdata(spi, dev); + dev->spi = spi; + + ret = ipc_frame_init(dev); + if(ret) { + printk(KERN_ERR "frame init failed \n"); + goto error_free_dev; + } + init_waitqueue_head(&dev->wait); + init_waitqueue_head(&dev->rx_read_wait); + init_waitqueue_head(&dev->tx_frame_wait); + ipc_reset(dev); + + dev->ipc_enable = true; + dev->remote_status = 0; +#ifdef CONFIG_PM + dev->bsuspend = false; +#endif + wake_lock_init(&dev->wake_lock, WAKE_LOCK_SUSPEND, "ipc_spi"); + /* register misc devices */ + dev->miscdev.minor = MISC_DYNAMIC_MINOR; + dev->miscdev.name = "ipc_spi"; + dev->miscdev.fops = &ipc_fops; + dev->miscdev.parent = NULL; + ret = misc_register(&dev->miscdev); + if(ret) { + printk(KERN_ERR "failed to register spi ipc miscdev!\n"); + goto error_free_dev; + } + + #ifdef CONFIG_DEBUG_FS + ret = ipc_init_debugfs(); + if(ret) { + printk(KERN_ERR "init debugfs error %d !.\n", ret); + goto error_unregister_dev; + } + #endif + + spi->max_speed_hz = 19200000; + spi->mode = SPI_MODE_3; + spi->bits_per_word = 32; + spi_setup(spi); + + ipc_dev = dev; + dev->task = kthread_create(mux_ipc_thread, (void*)dev, "mipc_thread"); + if (IS_ERR(dev->task)) { + printk(KERN_ERR "mux_ipc_thread error!.\n"); + goto error_unregister_dev; + } + + wake_up_process(dev->task); + + sprdmux_register(&sprd_iomux); + dev->modemsts.level = MODEMSTS_LEVEL_SPI; + dev->modemsts.data = &dev->modemsts; + dev->modemsts.modemsts_notifier = spi_ipc_enable; + modemsts_notifier_register(&dev->modemsts); + + return 0; +error_unregister_dev: + misc_deregister(&dev->miscdev); + +error_free_dev: + for(i = 0; i < MAX_MIPC_TX_FRAME_NUM; i++) { + if(tx_transfer_frame[i].buf_ptr) + kfree(tx_transfer_frame[i].buf_ptr); + } + for(i = 0; i < MAX_MIPC_RX_FRAME_NUM; i++) { + if(rx_transfer_frame[i].buf_ptr) + kfree(rx_transfer_frame[i].buf_ptr); + } + kfree(dev); + +error_ret: + return ret; +} + +static int __exit ipc_spi_remove(struct spi_device *spi) +{ + int i; + struct ipc_spi_dev *dev = dev_get_drvdata(&spi->dev); + dev->rwctrl |= IPC_WRITE_DISABLE | IPC_READ_DISABLE; + dev->ipc_enable = false; + sprdmux_unregister(SPRDMUX_ID_SPI); + for(i = 0; i < MAX_MIPC_TX_FRAME_NUM; i++) { + if(tx_transfer_frame[i].buf_ptr) + kfree(tx_transfer_frame[i].buf_ptr); + } + for(i = 0; i < MAX_MIPC_RX_FRAME_NUM; i++) { + if(rx_transfer_frame[i].buf_ptr) + kfree(rx_transfer_frame[i].buf_ptr); + } + kfree(dev); + return 0; +} + +#ifdef CONFIG_PM +static int ipc_spi_suspend(struct device *dev) +{ + struct ipc_spi_dev *ipc_dev = dev_get_drvdata(dev); + IPC_DBG("ipc_spi_suspend\n"); + ipc_dev->bsuspend = true; + return 0; +} + +static int ipc_spi_resume(struct device *dev) +{ + struct ipc_spi_dev *ipc_dev = dev_get_drvdata(dev); + IPC_DBG("ipc_spi_resume\n"); + ipc_dev->bsuspend = false; + wake_up(&ipc_dev->wait); + return 0; +} + +static const struct dev_pm_ops ipc_spi_pm_ops = { + .suspend = ipc_spi_suspend, + .resume = ipc_spi_resume, +}; +#define IPC_SPI_PM_OPS (&ipc_spi_pm_ops) + +#else +#define IPC_SPI_PM_OPS NULL +#endif + + +static struct spi_driver ipc_spi_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = IPC_SPI_PM_OPS, + }, + .probe = ipc_spi_probe, + .remove = __exit_p(ipc_spi_remove), +}; +module_spi_driver(ipc_spi_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("zhongping.tan<zhongping.tan@spreadtrum.com>"); +MODULE_DESCRIPTION("MUX spi Driver"); diff --git a/drivers/char/mux_spi_hal/ipc_spi.h b/drivers/char/mux_spi_hal/ipc_spi.h new file mode 100644 index 00000000..99672618 --- /dev/null +++ b/drivers/char/mux_spi_hal/ipc_spi.h @@ -0,0 +1,147 @@ +/* + * 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 __IPC_SPI_H +#define __IPC_SPI_H + +#include <linux/kfifo.h> +#include <linux/miscdevice.h> +#include <linux/wakelock.h> +#include <mach/modem_interface.h> + + +//#define SETUP_PACKET_SIZE (128) +#define MAX_RECEIVER_SIZE (8*1024) + +//#define SETUP_MAGIC 0x1a2b +#define DATA_MAGIC 0x3c4d +#define ACK_MAGIC 0x5e6f +//#define SETUP_ACK_MAGIC 0x789a +#define DATA_ACK_MAGIC 0x9abc +#define DUMMY_MAGIC 0x5a5a + +#define CP2AP_NOIRQ 0x0 +#define CP2AP_FALLING 0x1 +#define CP2AP_RASING 0x2 + +//#define REMOTE_ALIVE_STATUS 0x1 +//#define REMOTE_ASSERT_STATUS 0x2 + +typedef enum { + TYPE_ACK = 0x1, + TYPE_NAK, + TYPE_NAKALL, //if receive this ack , cancel the data you want to resend + TYPE_DATA, + TYPE_MAX +}packet_type; + +struct ack_packet { + u16 magic; + u16 checksum; + u16 type; + u16 dummy; + u32 seq_begin; + u32 seq_end; +}; + +// data packet type like below + +struct data_packet_header { + u16 magic; + u16 checksum; + union { + struct ack_packet ack; + }; + u32 seqnum; + u32 len; +}; + + +enum ipc_status { + IDLE_STATUS, + ACK_STATUS, + NAK_STATUS, + NAKALL_STATUS, + MAX_STATUS +}; + +struct frame_list { + struct list_head frame_list_head; + struct mutex list_mutex; + u32 counter; +}; + +struct ipc_transfer_frame { + u8* buf_ptr; + u32 buf_size; + u16 pos; + u16 flag; + u32 status; + u32 seq; + struct list_head link; +}; + + +struct ipc_transfer +{ + struct mutex transfer_mutex; + struct list_head frame_fifo; + struct ipc_transfer_frame* cur_frame_ptr; + u32 counter; +}; + +struct timer_info { + unsigned long long time; + u32 status; +}; + +struct ipc_spi_dev { + bool bneedrcv; + bool ipc_enable; + bool bneedread; + bool bneedsend; + u16 rx_ctl; // gpio control no irq: 0 , falling: 1, rasing: 2 + u16 rwctrl; /* read write controlled by mux */ + u16 rx_status; // rx status + u32 rx_seqnum; /* received num */ + u32 tx_seqnum; + u32 remote_status; + struct frame_list tx_free_lst; + struct ipc_transfer tx_transfer; + struct frame_list rx_free_lst; + struct frame_list rx_recv_lst; + struct ipc_transfer_frame* rx_curframe; + struct ipc_transfer_frame *tx_curframe; + wait_queue_head_t wait; + wait_queue_head_t rx_read_wait; + wait_queue_head_t tx_frame_wait; // tx frame wait + struct task_struct *task; + struct spi_device *spi; + struct miscdevice miscdev; + struct wake_lock wake_lock; + struct modemsts_chg modemsts; +#ifdef CONFIG_PM + bool bsuspend; +#endif + u32 irq_num; /* use for debug cp side irq num */ + u32 task_count; /* use for debug task run count*/ + u32 task_status; /* use for debug record the status of task */ + u32 rwnum; + u32 tx_irqennum; /* use for debug enable gpio num */ + u32 tx_irqdisnum; /* usefor debug disable gpio num */ + u32 ap_infocnt; + struct timer_info cp2ap[100]; + struct timer_info ap_info[100]; +}; + + + + +#endif /* __SDHCI_H */ diff --git a/drivers/char/mux_spi_hal/spi_gpio_ctrl.c b/drivers/char/mux_spi_hal/spi_gpio_ctrl.c new file mode 100644 index 00000000..281f018c --- /dev/null +++ b/drivers/char/mux_spi_hal/spi_gpio_ctrl.c @@ -0,0 +1,205 @@ +/* + * 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/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 <linux/interrupt.h> +#include <linux/dma-mapping.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/syscore_ops.h> + +#include "spi_gpio_ctrl.h" + + +#define GPIO_AP_TO_CP 239 /* cp side 71 */ +#define GPIO_CP_TO_AP 238 /* cp side 70*/ +#define GPIO_AP_WAKEUP_CP 150 + +#define DRIVER_NAME "spi_gpio" + +#define DBG(f, x...) pr_debug(DRIVER_NAME " [%s()]: " f, __func__,## x) + +static unsigned int cp_to_ap_irq; +static bool irq_need_change = true; + +int cp2ap_sts(void) +{ + DBG("spi :cp2ap_sts=%d\r\n",gpio_get_value(GPIO_CP_TO_AP)); + return gpio_get_value(GPIO_CP_TO_AP); +} + +int ap2cp_sts(void) +{ + DBG("spi :ap2cp_sts=%d\r\n",gpio_get_value(GPIO_AP_TO_CP)); + return gpio_get_value(GPIO_AP_TO_CP); +} + +void ap2cp_enable(void) +{ + DBG("spi ap2cp_enable AP_TO_CP_sts is 1\r\n"); + gpio_set_value(GPIO_AP_TO_CP, 1); +} + +void ap2cp_disable(void) +{ + DBG("spi ap2cp_disable AP_TO_CP is 0\r\n"); + gpio_set_value(GPIO_AP_TO_CP, 0); +} + +void ap2cp_wakeup(void) +{ + gpio_set_value(GPIO_AP_WAKEUP_CP, 0); +} + +void ap2cp_sleep(void) +{ + gpio_set_value(GPIO_AP_WAKEUP_CP, 1); +} + + +static irqreturn_t cp_to_ap_irq_handle(int irq, void *handle) +{ + struct ipc_spi_dev *dev = (struct ipc_spi_dev*)handle; + if(!irq_need_change) { + irq_set_irq_type(cp_to_ap_irq, IRQ_TYPE_EDGE_BOTH); + irq_need_change = true; + } + if(cp2ap_sts()) + { + //irq_set_irq_type( irq, IRQF_TRIGGER_LOW); + dev->rx_ctl = CP2AP_RASING; + dev->bneedrcv = true; + /* just for debug */ + dev->cp2ap[dev->irq_num % 100].status = 1; + dev->cp2ap[dev->irq_num % 100].time = cpu_clock(0); + } + else + { + //irq_set_irq_type( irq, IRQF_TRIGGER_HIGH); + dev->rx_ctl = CP2AP_FALLING; + dev->bneedrcv = false; + /* just for debug */ + dev->cp2ap[dev->irq_num % 100].status = 0; + dev->cp2ap[dev->irq_num % 100].time = cpu_clock(0); + } + dev->irq_num++; + wake_up(&(dev->wait)); + return IRQ_HANDLED; +} + +static int spi_syscore_suspend(void) +{ + if(irq_need_change) { + irq_set_irq_type(cp_to_ap_irq, IRQF_TRIGGER_HIGH); + irq_need_change = false; + } + return 0; +} + + +static struct syscore_ops spi_syscore_ops = { + .suspend = spi_syscore_suspend, +}; + + +/*****************************************************************************\ + * * + * Driver init/exit * + * * +\*****************************************************************************/ + +int spi_hal_gpio_init(void) +{ + int ret; + + DBG("spi_hal_gpio init \n"); + + + //config ap_to_cp_rts + ret = gpio_request(GPIO_AP_TO_CP, "ap_to_cp_rts"); + if (ret) { + DBG("Cannot request GPIO %d\r\n", GPIO_AP_TO_CP); + gpio_free(GPIO_AP_TO_CP); + return ret; + } + gpio_direction_output(GPIO_AP_TO_CP, 0); + gpio_export(GPIO_AP_TO_CP, 1); + + //config GPIO_AP_WAKEUP_CP + ret = gpio_request(GPIO_AP_WAKEUP_CP, "ap_wakeup_cp"); + if (ret) { + DBG("Cannot request GPIO %d\r\n", GPIO_AP_WAKEUP_CP); + gpio_free(GPIO_AP_WAKEUP_CP); + return ret; + } + gpio_direction_output(GPIO_AP_WAKEUP_CP, 1); + gpio_export(GPIO_AP_WAKEUP_CP, 1); + + //config cp_out_rdy + ret = gpio_request(GPIO_CP_TO_AP, "cp_out_rdy"); + if (ret) { + DBG("Cannot request GPIO %d\r\n",GPIO_CP_TO_AP); + gpio_free(GPIO_CP_TO_AP); + return ret; + } + gpio_direction_input(GPIO_CP_TO_AP); + gpio_export(GPIO_CP_TO_AP, 1); + + return 0; +} + +int spi_hal_gpio_irq_init(struct ipc_spi_dev *dev) +{ + int ret; + + DBG("spi_hal_gpio_irq_init \n"); + + cp_to_ap_irq = gpio_to_irq(GPIO_CP_TO_AP); + if (cp_to_ap_irq < 0) + return -1; + ret = request_irq(cp_to_ap_irq, cp_to_ap_irq_handle, + IRQF_NO_SUSPEND | IRQF_DISABLED|IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "cp_to_ap_irq", (void*)dev); + if (ret) { + DBG("lee :cannot alloc cp_to_ap_rdy_irq, err %d\r\r\n", ret); + return ret; + } + enable_irq_wake(cp_to_ap_irq); + register_syscore_ops(&spi_syscore_ops); + + return 0; + +} + +void spi_hal_gpio_exit(void) +{ + DBG("spi_hal_gpio_exit gpio exit\r\n"); + + gpio_free(GPIO_AP_TO_CP); + gpio_free(GPIO_CP_TO_AP); + + free_irq(cp_to_ap_irq, NULL); +} + +MODULE_AUTHOR("zhongping.tan<zhongping.tan@spreadtrum.com>"); +MODULE_DESCRIPTION("GPIO driver about mux spi"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/mux_spi_hal/spi_gpio_ctrl.h b/drivers/char/mux_spi_hal/spi_gpio_ctrl.h new file mode 100644 index 00000000..8bf9d589 --- /dev/null +++ b/drivers/char/mux_spi_hal/spi_gpio_ctrl.h @@ -0,0 +1,40 @@ +/* + * + * + * 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 __SPI_GPIO_CTRL_H +#define __SPI_GPIO_CTRL_H + +#include <linux/scatterlist.h> +#include <linux/compiler.h> +#include <linux/types.h> +#include <linux/io.h> +#include <linux/bitops.h> + +#include "ipc_spi.h" + +extern int cp2ap_sts(void); + +extern int ap2cp_sts(void); + +extern void ap2cp_enable(void); + +extern void ap2cp_disable(void); + +extern void ap2cp_wakeup(void); + +extern void ap2cp_sleep(void); + +extern int spi_hal_gpio_init(void); + +extern int spi_hal_gpio_irq_init(struct ipc_spi_dev *dev); + +extern void spi_hal_gpio_exit(void); + +#endif /* __SPI_GPIO_CTRL_H */ |
