diff options
author | codeworkx <daniel.hillenbrand@codeworkx.de> | 2012-06-02 13:09:29 +0200 |
---|---|---|
committer | codeworkx <daniel.hillenbrand@codeworkx.de> | 2012-06-02 13:09:29 +0200 |
commit | c6da2cfeb05178a11c6d062a06f8078150ee492f (patch) | |
tree | f3b4021d252c52d6463a9b3c1bb7245e399b009c /drivers/phone_svn | |
parent | c6d7c4dbff353eac7919342ae6b3299a378160a6 (diff) | |
download | kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.zip kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.gz kernel_samsung_smdk4412-c6da2cfeb05178a11c6d062a06f8078150ee492f.tar.bz2 |
samsung update 1
Diffstat (limited to 'drivers/phone_svn')
25 files changed, 10816 insertions, 0 deletions
diff --git a/drivers/phone_svn/Kconfig b/drivers/phone_svn/Kconfig new file mode 100644 index 0000000..9146f77 --- /dev/null +++ b/drivers/phone_svn/Kconfig @@ -0,0 +1,71 @@ +# +# +# + +menuconfig SAMSUNG_PHONE_SVNET + tristate "Samsung Phone Interface - SVN" + default n + ---help--- + +if SAMSUNG_PHONE_SVNET + +menuconfig PHONE_ONEDRAM + tristate "OneDRAM" + depends on SAMSUNG_PHONE_SVNET + default n + help + Say Y to enable OneDRAM Interface support. + +config PHONE_IPC_SPI + bool "IPC SPI" + depends on SAMSUNG_PHONE_SVNET + default n + help + Say Y to enable IPC-SPI Interface support. + +menuconfig PHONE_IPC_HSI + tristate "IPC HSI" + depends on SAMSUNG_PHONE_SVNET + default n + help + Say Y to enable MIPI-HSI Interface support. + +config PHONE_SVNET + tristate "Samsung Virtual Network" + depends on SAMSUNG_PHONE_SVNET + default n + help + Say Y to enable Samsung Virtual Network support + +menuconfig PHONE_LOOPBACK_TEST + tristate "SPI LoopBack Test" + depends on SAMSUNG_PHONE_SVNET + default n + +menuconfig PHONE_STORAGE + tristate "DGS Storage" + depends on SAMSUNG_PHONE_SVNET + default n + +config SVNET_WHITELIST + bool "svnet uses whitelist via onedram" + default n + ---help--- + By default, n + +config CHN_CMCC_SPI_SPRD + tristate "Samsung M0-TD CP SPRD" + depends on SAMSUNG_PHONE_SVNET + default n + +config SEC_MODEM_M0_TD + tristate "Samsung M0-TD modemctl driver" + depends on SAMSUNG_PHONE_SVNET + default n + +config WORKQUEUE_FRONT + tristate "IPC_SPI workqueue front" + depends on SAMSUNG_PHONE_SVNET + default n + +endif # SAMSUNG_PHONE_SVNET diff --git a/drivers/phone_svn/Makefile b/drivers/phone_svn/Makefile new file mode 100644 index 0000000..38664e0 --- /dev/null +++ b/drivers/phone_svn/Makefile @@ -0,0 +1,4 @@ +# IPC_SPI kernel driver + +obj-$(CONFIG_PHONE_IPC_SPI) += ipc_spi/ +obj-$(CONFIG_PHONE_SVNET) += svnet/ diff --git a/drivers/phone_svn/ipc_spi/Kconfig b/drivers/phone_svn/ipc_spi/Kconfig new file mode 100644 index 0000000..fb1386c --- /dev/null +++ b/drivers/phone_svn/ipc_spi/Kconfig @@ -0,0 +1,9 @@ +# +# OneDRAM configuration +# +config DPRAM_BUILT_IN + tristate "onedram driver built-in" + +config SVNET_WHITELIST + bool "svnet uses whitelist via onedram" + default n diff --git a/drivers/phone_svn/ipc_spi/Makefile b/drivers/phone_svn/ipc_spi/Makefile new file mode 100644 index 0000000..28a66e6 --- /dev/null +++ b/drivers/phone_svn/ipc_spi/Makefile @@ -0,0 +1,2 @@ +ipc_spi-objs := ipc_spis.o spi_app.o spi_data.o spi_dev.o spi_main.o spi_os.o +obj-$(CONFIG_PHONE_IPC_SPI) += ipc_spi.o diff --git a/drivers/phone_svn/ipc_spi/ipc_spis.c b/drivers/phone_svn/ipc_spi/ipc_spis.c new file mode 100644 index 0000000..331a238 --- /dev/null +++ b/drivers/phone_svn/ipc_spi/ipc_spis.c @@ -0,0 +1,3923 @@ +#define FEATURE_SAMSUNG_SPI + +/** + * Samsung Virtual Network driver using IpcSpi device + * + * Copyright (C) 2010 Samsung Electronics. 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 + * version 2 as published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#define DEBUG + +/* +#define FORMAT_TX_DUMP +#define RAW_TX_DUMP +#define RFS_TX_DUMP +#define FORMAT_RX_DUMP +#define RAW_RX_DUMP +#define RFS_RX_DUMP +#define RAW_TX_RX_LENGTH_DUMP +#define RFS_TX_RX_LENGTH_DUMP +*/ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> + +#include <linux/mm.h> +#include <linux/fs.h> +#include <linux/cdev.h> +#include <linux/phone_svn/ipc_spi.h> +#include <linux/spinlock.h> +#include <linux/ioport.h> +#include <linux/io.h> +#include <linux/wait.h> +#include <linux/fs.h> +#include <linux/poll.h> +#include <linux/sched.h> + +#include <linux/vmalloc.h> +#include <linux/spi/spi.h> +#include <linux/gpio.h> +#include <linux/delay.h> +#include <linux/in.h> +#include <mach/regs-pmu.h> +#include <linux/kthread.h> +#include <linux/err.h> + +#include <linux/workqueue.h> +#ifdef FEATURE_SAMSUNG_SPI +#include "spi_main.h" +#endif + +#define DRVNAME "onedram" + +#define ONEDRAM_REG_OFFSET 0xFFF800 +#define ONEDRAM_REG_SIZE 0x800 + +#define MB_VALID 0x0080 +#define MB_COMMAND 0x0040 + +#define MB_CMD(x) (MB_VALID | MB_COMMAND | x) +#define MB_DATA(x) (MB_VALID | x) + +#define MBD_SEND_FMT 0x0002 +#define MBD_SEND_RAW 0x0001 +#define MBD_SEND_RFS 0x0100 +#define MBD_REQ_ACK_FMT 0x0020 +#define MBD_REQ_ACK_RAW 0x0010 +#define MBD_REQ_ACK_RFS 0x0400 +#define MBD_RES_ACK_FMT 0x0008 +#define MBD_RES_ACK_RAW 0x0004 +#define MBD_RES_ACK_RFS 0x0200 + + +#define FMT_OUT 0x0FE000 +#define FMT_IN 0x10E000 +#define FMT_SZ 0x10000 /* 65536 bytes */ + +#define RAW_OUT 0x11E000 +#define RAW_IN 0x21E000 +#define RAW_SZ 0x100000 /* 1 MB */ + +#define RFS_OUT 0x31E000 +#define RFS_IN 0x41E000 +#define RFS_SZ 0x100000 /* 1 MB */ + +static u32 ipc_spi_get_send_vbuff_command(void); +static void ipc_spi_set_send_vbuff_command_clear(void); +static inline void ipc_spi_get_pointer_of_vbuff_format_tx(u32 *head, u32 *tail); +static inline void ipc_spi_get_pointer_of_vbuff_format_rx(u32 *head, u32 *tail); +static inline void ipc_spi_get_pointer_of_vbuff_raw_tx(u32 *head, u32 *tail); +static inline void ipc_spi_get_pointer_of_vbuff_raw_rx(u32 *head, u32 *tail); +static inline void ipc_spi_get_pointer_of_vbuff_rfs_tx(u32 *head, u32 *tail); +static inline void ipc_spi_get_pointer_of_vbuff_rfs_rx(u32 *head, u32 *tail); +static inline void ipc_spi_update_tail_of_vbuff_format_tx(u32 u_tail); +static inline void ipc_spi_update_head_of_vbuff_format_rx(u32 u_head); +static inline void ipc_spi_update_tail_of_vbuff_raw_tx(u32 u_tail); +static inline void ipc_spi_update_head_of_vbuff_raw_rx(u32 u_head); +static inline void ipc_spi_update_tail_of_vbuff_rfs_tx(u32 u_tail); +static inline void ipc_spi_update_head_of_vbuff_rfs_rx(u32 u_head); +/* +#define LOOPBACK_TEST +*/ +#ifdef LOOPBACK_TEST +int loop_back_test = 1; +#define LOOP_SIZE 2048 +#else +int loop_back_test; +#endif + +enum { + MBC_NONE = 0, + MBC_INIT_START, + MBC_INIT_END, + MBC_REQ_ACTIVE, + MBC_RES_ACTIVE, + MBC_TIME_SYNC, + MBC_POWER_OFF, + MBC_RESET, + MBC_PHONE_START, + MBC_ERR_DISPLAY, + MBC_POWER_SAVE, + MBC_NV_REBUILD, + MBC_EMER_DOWN, + MBC_REQ_SEM, + MBC_RES_SEM, + MBC_MAX +}; +#define MBC_MASK 0xFF + +#define MAX_BUF_SIZE 2044 +#define DEF_BUF_SIZE MAX_BUF_SIZE + +static struct spi_device *p_ipc_spi; + +#ifdef LOOPBACK_TEST +struct spi_protocol_header { + unsigned long current_data_size:12; + unsigned long more:1; + unsigned long rx_error:1; + unsigned long packet_id:2; + unsigned long reserved:2; + unsigned long next_data_size:10; + unsigned long RI:1; + unsigned long DCD:1; + unsigned long RTSCTS:1; + unsigned long DSRDTR:1; +}; +#else +struct spi_protocol_header { + unsigned long current_data_size:31; + unsigned long more:1; +}; +#endif + +struct onedram_reg_mapped { + u32 sem; + u32 reserved1[7]; + u32 mailbox_AB; /* CP write, AP read */ + u32 reserved2[7]; + u32 mailbox_BA; /*AP write, CP read */ + u32 reserved3[23]; + u32 check_AB; /* can't read */ + u32 reserved4[7]; + u32 check_BA; /* 0: CP read, 1: CP don't read */ +}; + +struct ipc_spi_handler { + struct list_head list; + void *data; + void (*handler)(u32, void *); +}; + +struct ipc_spi_handler_head { + struct list_head list; + u32 len; + spinlock_t lock; +}; +static struct ipc_spi_handler_head h_list; + +static struct resource ipc_spi_resource = { + .name = DRVNAME, + .start = 0, + .end = -1, + .flags = IORESOURCE_MEM, +}; + +struct ipc_spi { + struct class *class; + struct device *dev; + struct cdev cdev; + dev_t devid; + + wait_queue_head_t waitq; + struct fasync_struct *async_queue; + u32 mailbox; + + unsigned long base; + unsigned long size; + void __iomem *mmio; + + int irq; + + struct completion comp; + atomic_t ref_sem; + unsigned long flags; + + const struct attribute_group *group; + + struct onedram_reg_mapped *reg; +}; +struct ipc_spi *ipc_spi; + +struct workqueue_struct *ipc_spi_wq; +struct ipc_spi_send_modem_bin_workq_data { + struct ipc_spi *od; + + struct work_struct send_modem_w; +}; +struct ipc_spi_send_modem_bin_workq_data *ipc_spi_send_modem_work_data; + +static DEFINE_SPINLOCK(ipc_spi_lock); + +static unsigned long hw_tmp; /* for hardware */ +static inline int _read_sem(struct ipc_spi *od); +static inline void _write_sem(struct ipc_spi *od, int v); +static int ipc_spi_thread_restart(void); +static inline void ipc_spi_get_pointer_of_vbuff_format_tx(u32 *head, u32 *tail); + +struct completion ril_init; +static unsigned gpio_mrdy; +static unsigned gpio_srdy; +struct semaphore srdy_sem; +struct semaphore transfer_event_sem; +int send_modem_spi = 1; +int is_cp_reset; +int boot_done; + +#ifndef FEATURE_SAMSUNG_SPI +static +#endif +int transfer_thread_waiting; + +static void __iomem *p_virtual_buff; + +static unsigned long recv_cnt; +static unsigned long send_cnt; +static ssize_t show_debug(struct device *d, + struct device_attribute *attr, char *buf) +{ + char *p = buf; + struct ipc_spi *od = dev_get_drvdata(d); + + if (!od) + return 0; + + p += sprintf(p, "Semaphore: %d (%d)\n", _read_sem(od), (char)hw_tmp); + p += sprintf(p, "Mailbox: %x\n", od->reg->mailbox_AB); + p += sprintf(p, "Reference count: %d\n", atomic_read(&od->ref_sem)); + p += sprintf(p, "Mailbox send: %lu\n", send_cnt); + p += sprintf(p, "Mailbox recv: %lu\n", recv_cnt); + + return p - buf; +} + + +#ifdef FEATURE_SAMSUNG_SPI +void *ipc_spi_get_queue_buff(void) +{ + return (void *)p_virtual_buff; +} +#endif + + +static DEVICE_ATTR(debug, S_IRUGO, show_debug, NULL); + +static struct attribute *ipc_spi_attributes[] = { + &dev_attr_debug.attr, + NULL +}; + +static const struct attribute_group ipc_spi_group = { + .attrs = ipc_spi_attributes, +}; + +static inline void _write_sem(struct ipc_spi *od, int v) +{ + od->reg->sem = 1; + + hw_tmp = od->reg->sem; /* for hardware */ +} + +static inline int _read_sem(struct ipc_spi *od) +{ + od->reg->sem = 1; + + return od->reg->sem; +} + +static inline int _send_cmd(struct ipc_spi *od, u32 cmd) +{ + u32 tmp_cmd; + + if (!od) { + printk(KERN_ERR "[%s]ipcspi: Dev is NULL, but try to access\n", + __func__); + return -EFAULT; + } + + if (!od->reg) { + dev_err(od->dev, "(%d) Failed to send cmd, not initialized\n", + __LINE__); + + return -EFAULT; + } + + if (cmd & MB_VALID) { + if (cmd & MB_COMMAND) { + tmp_cmd = (cmd & MBC_MASK) & ~(MB_CMD(0)); + + switch (tmp_cmd) { + case MBC_INIT_END: /* 0x0002 */ + dev_info(od->dev, "(%d) send ril init cmd : %x\n", + __LINE__, cmd); + + send_cnt++; + od->reg->mailbox_BA = cmd; + +#ifdef FEATURE_SAMSUNG_SPI + spi_main_send_signal(SPI_MAIN_MSG_IPC_SEND); +#else + if (transfer_thread_waiting) { + transfer_thread_waiting = 0; + up(&transfer_event_sem); + } +#endif + + complete_all(&ril_init); + + break; + + default: + break; + } + } else { + dev_dbg(od->dev, "(%d) =>send data (0x%x)\n", + __LINE__, cmd); + +#ifdef FEATURE_SAMSUNG_SPI + spi_main_send_signal(SPI_MAIN_MSG_IPC_SEND); +#else + if (transfer_thread_waiting) { + transfer_thread_waiting = 0; + up(&transfer_event_sem); + } +#endif + } + } else { + if (cmd == 0x45674567) { + dev_dbg(od->dev, "(%d) modem image loaded.\n", + __LINE__); + dev_dbg(od->dev, "(%d) start to send modem binary to cp.\n", + __LINE__); + + ipc_spi_send_modem_work_data->od = od; + if (!queue_work(ipc_spi_wq, + &ipc_spi_send_modem_work_data->send_modem_w)) + dev_err(od->dev, "(%d) already exist w-q\n", __LINE__); + + } else { + dev_err(od->dev, "(%d) send invalid cmd : 0x%x\n", + __LINE__, cmd); + } + } + + return 0; +} + +static inline int _recv_cmd(struct ipc_spi *od, u32 *cmd) +{ + if (!cmd) + return -EINVAL; + + if (!od) { + printk(KERN_ERR "[%s]ipcspi: Dev is NULL, but try to access\n", + __func__); + return -EFAULT; + } + + if (!od->reg) { + dev_err(od->dev, "(%d) Failed to read cmd, not initialized\n", + __LINE__); + + return -EFAULT; + } + + recv_cnt++; + *cmd = od->reg->mailbox_AB; + return 0; +} + +static inline int _get_auth(struct ipc_spi *od, u32 cmd) +{ + unsigned long timeleft; + int retry = 0; + + /* send cmd every 20m seconds */ + while (1) { + _send_cmd(od, cmd); + + timeleft = wait_for_completion_timeout(&od->comp, HZ/50); +#if 0 + if (timeleft) + break; +#endif + if (_read_sem(od)) + break; + + retry++; + if (retry > 50) { /* time out after 1 seconds */ + dev_err(od->dev, "(%d) get authority time out\n", __LINE__); + + return -ETIMEDOUT; + } + } + + return 0; +} + +static int get_auth(struct ipc_spi *od, u32 cmd) +{ + int r; + + if (!od) { + printk(KERN_ERR "[%s]ipcspi: Dev is NULL, but try to access\n", __func__); + return -EFAULT; + } + + if (!od->reg) { + dev_err(od->dev, "(%d) Failed to get authority\n", __LINE__); + + return -EFAULT; + } + + if (_read_sem(od)) + return 0; + + if (cmd) + r = _get_auth(od, cmd); + else + r = -EACCES; + + return r; +} + +static int put_auth(struct ipc_spi *od, int release) +{ + if (!od) { + printk(KERN_ERR "[%s]onedram: Dev is NULL, but try to access\n", __func__); + return -EFAULT; + } + + if (!od->reg) { + dev_err(od->dev, "(%d) Failed to put authority\n", __LINE__); + + return -EFAULT; + } + + return 0; +} + +static int rel_sem(struct ipc_spi *od) +{ + if (!od) { + printk(KERN_ERR "[%s]onedram: Dev is NULL, but try to access\n", __func__); + return -EFAULT; + } + + if (!od->reg) { + dev_err(od->dev, "(%d) Failed to put authority\n", __LINE__); + + return -EFAULT; + } + + return 0; +} + +int onedram_read_mailbox(u32 *mb) +{ + return _recv_cmd(ipc_spi, mb); +} +EXPORT_SYMBOL(onedram_read_mailbox); + +int onedram_write_mailbox(u32 mb) +{ + return _send_cmd(ipc_spi, mb); +} +EXPORT_SYMBOL(onedram_write_mailbox); + +void onedram_init_mailbox(void) +{ + int r = 0; + /* flush mailbox before registering onedram irq */ + r = ipc_spi->reg->mailbox_AB; + + /* Set nINT_ONEDRAM_CP to low */ + ipc_spi->reg->mailbox_BA = 0x0; +} +EXPORT_SYMBOL(onedram_init_mailbox); + +int onedram_get_auth(u32 cmd) +{ + return get_auth(ipc_spi, cmd); +} +EXPORT_SYMBOL(onedram_get_auth); + +int onedram_put_auth(int release) +{ + return put_auth(ipc_spi, release); +} +EXPORT_SYMBOL(onedram_put_auth); + +int onedram_rel_sem(void) +{ + return rel_sem(ipc_spi); +} +EXPORT_SYMBOL(onedram_rel_sem); + +int onedram_read_sem(void) +{ + return _read_sem(ipc_spi); +} +EXPORT_SYMBOL(onedram_read_sem); + +void onedram_get_vbase(void **vbase) +{ + *vbase = (void *)ipc_spi->mmio; +} +EXPORT_SYMBOL(onedram_get_vbase); + + +static int ipc_spi_irq_log_flag; +static irqreturn_t ipc_spi_irq_handler(int irq, void *data) /* SRDY Rising EDGE ISR */ +{ + struct ipc_spi *od = (struct ipc_spi *)data; + int srdy_pin = 0; + + srdy_pin = gpio_get_value(gpio_srdy); + + if (!boot_done) + return IRQ_HANDLED; + + /* + if (ipc_spi_irq_log_flag) + dev_dbg(od->dev, "(%d) isr SRDY : %d\n", __LINE__, srdy_pin); + */ + + if (!srdy_pin) { + printk(KERN_ERR "SRDY LOW.\n"); + return IRQ_HANDLED; + } + + up(&srdy_sem); + + if (transfer_thread_waiting) { + transfer_thread_waiting = 0; + + if (ipc_spi_irq_log_flag) + dev_dbg(od->dev, "(%d) signal transfer event.\n", __LINE__); + + up(&transfer_event_sem); + } + + return IRQ_HANDLED; +} + +static int ipc_spi_vm_pagefault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + struct ipc_spi *od = vma->vm_private_data; + + vmf->page = vmalloc_to_page(od->mmio + (vmf->pgoff << PAGE_SHIFT)); + get_page(vmf->page); + + return 0; +} + +static struct vm_operations_struct ipc_spi_vm_ops = { + .fault = ipc_spi_vm_pagefault, +}; + +static int ipc_spi_open(struct inode *inode, struct file *filp) +{ + struct cdev *cdev = inode->i_cdev; + struct ipc_spi *od = container_of(cdev, struct ipc_spi, cdev); + + filp->private_data = od; + return 0; +} + +static int ipc_spi_release(struct inode *inode, struct file *filp) +{ + filp->private_data = NULL; + return 0; +} + +static unsigned int ipc_spi_poll(struct file *filp, poll_table *wait) +{ + struct ipc_spi *od; + u32 data; + + od = filp->private_data; + + poll_wait(filp, &od->waitq, wait); + + spin_lock_irq(&ipc_spi_lock); + data = od->mailbox; + spin_unlock_irq(&ipc_spi_lock); + + if (data) + return POLLIN | POLLRDNORM; + + return 0; +} + +static ssize_t ipc_spi_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + DECLARE_WAITQUEUE(wait, current); + u32 data; + ssize_t retval; + struct ipc_spi *od; + + od = filp->private_data; + + if (count < sizeof(u32)) + return -EINVAL; + + add_wait_queue(&od->waitq, &wait); + + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + + spin_lock_irq(&ipc_spi_lock); + data = od->mailbox; + od->mailbox = 0; + spin_unlock_irq(&ipc_spi_lock); + + if (data) + break; + else if (filp->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + goto out; + } else if (signal_pending(current)) { + retval = -ERESTARTSYS; + goto out; + } + schedule(); + } + + retval = put_user(data, (u32 __user *)buf); + if (!retval) + retval = sizeof(u32); +out: + __set_current_state(TASK_RUNNING); + remove_wait_queue(&od->waitq, &wait); + + return retval; +} + +static ssize_t ipc_spi_write(struct file *filp, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct ipc_spi *od; + + od = filp->private_data; + + if (count) { + u32 data; + + if (get_user(data, (u32 __user *)buf)) + return -EFAULT; + + _send_cmd(od, data); + } + + return count; +} + +static int ipc_spi_fasync(int fd, struct file *filp, int on) +{ + struct ipc_spi *od; + + od = filp->private_data; + + return fasync_helper(fd, filp, on, &od->async_queue); +} + +static int ipc_spi_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct ipc_spi *od; + unsigned long size; + unsigned long offset; + + od = filp->private_data; + if (!od || !vma) + return -EFAULT; + + size = vma->vm_end - vma->vm_start; + offset = vma->vm_pgoff << PAGE_SHIFT; + + dev_dbg(od->dev, "(%d) Req region: 0x%08lx 0x%08lx\n", __LINE__, offset, size); + + vma->vm_flags |= VM_RESERVED; + vma->vm_ops = &ipc_spi_vm_ops; + vma->vm_private_data = od; + + dev_dbg(od->dev, "(%d) ipc_spi_mmap Done.\n", __LINE__); + + return 0; +} + +int ipc_spi_sema_init(void) +{ + printk(KERN_ERR "[SPI] Srdy sema init\n"); + sema_init(&srdy_sem, 0); + return 0; +} + +static long ipc_spi_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + + int r = 0; + + printk(KERN_ERR "[%s]\n", __func__); + + switch (cmd) { + case ONEDRAM_CP_CRASH: + panic("CP Crash"); + break; + case ONEDRAM_REL_SEM: + r = ipc_spi_thread_restart(); + break; + case ONEDRAM_SEMA_INIT: + r = ipc_spi_sema_init(); + break; + default: + r = -ENOIOCTLCMD; + break; + } + + return r; +} + +static const struct file_operations ipc_spi_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = ipc_spi_read, + .write = ipc_spi_write, + .poll = ipc_spi_poll, + .fasync = ipc_spi_fasync, + .open = ipc_spi_open, + .release = ipc_spi_release, + .mmap = ipc_spi_mmap, + .unlocked_ioctl = ipc_spi_ioctl, +}; + +static int _register_chrdev(struct ipc_spi *od) +{ + int r; + dev_t devid; + + od->class = class_create(THIS_MODULE, DRVNAME); + if (IS_ERR(od->class)) { + r = PTR_ERR(od->class); + od->class = NULL; + return r; + } + + r = alloc_chrdev_region(&devid, 0, 1, DRVNAME); + if (r) + return r; + + cdev_init(&od->cdev, &ipc_spi_fops); + + r = cdev_add(&od->cdev, devid, 1); + if (r) { + unregister_chrdev_region(devid, 1); + return r; + } + od->devid = devid; + + od->dev = device_create(od->class, NULL, od->devid, od, DRVNAME); + if (IS_ERR(od->dev)) { + r = PTR_ERR(od->dev); + od->dev = NULL; + return r; + } + dev_set_drvdata(od->dev, od); + + return 0; +} + +static inline int _request_mem(struct ipc_spi *od, struct platform_device *pdev) +{ + od->mmio = vmalloc(od->size); + if (!od->mmio) { + dev_err(&pdev->dev, "(%d) Failed to vmalloc size : %lu\n", __LINE__, od->size); + + return -EBUSY; + } else { + dev_dbg(&pdev->dev, "(%d) vmalloc Done. mmio : 0x%08x\n", __LINE__, (u32)od->mmio); + } + + memset((void *)od->mmio, 0, od->size); + + od->reg = (struct onedram_reg_mapped *)( + (char *)od->mmio + ONEDRAM_REG_OFFSET); + dev_dbg(&pdev->dev, "(%d) Onedram semaphore: %d\n", __LINE__, _read_sem(od)); + + od->reg->sem = 1; + dev_dbg(&pdev->dev, "(%d) force set sem to 1 : %d\n", __LINE__, od->reg->sem); + + od->reg->mailbox_AB = 0x000000C8; + od->reg->mailbox_BA = 0; + dev_dbg(&pdev->dev, "(%d) force set mailbox to 0 : AB=0x%x, BA=0x%x\n", __LINE__, od->reg->mailbox_AB, od->reg->mailbox_BA); + + ipc_spi_resource.start = (resource_size_t)od->mmio; + ipc_spi_resource.end = (resource_size_t)od->mmio + od->size - 1; + + p_virtual_buff = od->mmio; + + return 0; +} + +static void _release(struct ipc_spi *od) +{ + if (!od) + return; + + if (od->irq) + free_irq(od->irq, od); + + if (od->group) + sysfs_remove_group(&od->dev->kobj, od->group); + + if (od->dev) + device_destroy(od->class, od->devid); + + if (od->devid) { + cdev_del(&od->cdev); + unregister_chrdev_region(od->devid, 1); + } + + if (od->mmio) { + od->reg = NULL; + + vfree((void *)od->mmio); + + ipc_spi_resource.start = 0; + ipc_spi_resource.end = -1; + } + + if (od->class) + class_destroy(od->class); + + kfree(od); +} + +struct resource *onedram_request_region(resource_size_t start, + resource_size_t n, const char *name) +{ + struct resource *res; + + start += ipc_spi_resource.start; + res = __request_region(&ipc_spi_resource, start, n, name, 0); + if (!res) + return NULL; + + return res; +} +EXPORT_SYMBOL(onedram_request_region); + +void onedram_release_region(resource_size_t start, resource_size_t n) +{ + start += ipc_spi_resource.start; + __release_region(&ipc_spi_resource, start, n); +} +EXPORT_SYMBOL(onedram_release_region); + +int onedram_register_handler(void (*handler)(u32, void *), void *data) +{ + unsigned long flags; + struct ipc_spi_handler *hd; + + if (!handler) + return -EINVAL; + + hd = kzalloc(sizeof(struct ipc_spi_handler), GFP_KERNEL); + if (!hd) + return -ENOMEM; + + hd->data = data; + hd->handler = handler; + + spin_lock_irqsave(&h_list.lock, flags); + list_add_tail(&hd->list, &h_list.list); + h_list.len++; + spin_unlock_irqrestore(&h_list.lock, flags); + + return 0; +} +EXPORT_SYMBOL(onedram_register_handler); + +int onedram_unregister_handler(void (*handler)(u32, void *)) +{ + unsigned long flags; + struct list_head *l, *tmp; + + if (!handler) + return -EINVAL; + + spin_lock_irqsave(&h_list.lock, flags); + list_for_each_safe(l, tmp, &h_list.list) { + struct ipc_spi_handler *hd = list_entry(l, struct ipc_spi_handler, list); + + if (hd->handler == handler) { + list_del(&hd->list); + h_list.len--; + kfree(hd); + } + } + spin_unlock_irqrestore(&h_list.lock, flags); + + return 0; +} +EXPORT_SYMBOL(onedram_unregister_handler); + +static void _unregister_all_handlers(void) +{ + unsigned long flags; + struct list_head *l, *tmp; + + spin_lock_irqsave(&h_list.lock, flags); + list_for_each_safe(l, tmp, &h_list.list) { + struct ipc_spi_handler *hd = list_entry(l, struct ipc_spi_handler, list); + + list_del(&hd->list); + h_list.len--; + kfree(hd); + } + spin_unlock_irqrestore(&h_list.lock, flags); +} + +static void _init_data(struct ipc_spi *od) +{ + init_completion(&od->comp); + atomic_set(&od->ref_sem, 0); + INIT_LIST_HEAD(&h_list.list); + spin_lock_init(&h_list.lock); + h_list.len = 0; + init_waitqueue_head(&od->waitq); + + init_completion(&ril_init); + sema_init(&srdy_sem, 0); + sema_init(&transfer_event_sem, 0); +} + +static u32 ipc_spi_get_send_vbuff_command(void) +{ + u32 cmd = 0; + + memcpy((void *)&cmd, (void *)(p_virtual_buff + ONEDRAM_REG_OFFSET + 64), sizeof(cmd)); + /* dev_dbg(&p_ipc_spi->dev, "(%d) get mailbox_BA cmd : 0x%x\n", __LINE__, cmd); */ + + return cmd; +} + +static void ipc_spi_set_send_vbuff_command_clear(void) +{ + u32 cmd = 0; + + memcpy((void *)(p_virtual_buff + ONEDRAM_REG_OFFSET + 64), (void *)&cmd, sizeof(cmd)); + /* dev_dbg(&p_ipc_spi->dev, "(%d) clear mailbox_BA cmd : 0x%x\n", __LINE__, cmd); */ +} + +static inline void ipc_spi_get_pointer_of_vbuff_format_tx(u32 *head, u32 *tail) +{ + memcpy((void *)head, (void *)(p_virtual_buff + 16), sizeof(*head)); + memcpy((void *)tail, (void *)(p_virtual_buff + 20), sizeof(*tail)); + /* dev_dbg(&p_ipc_spi->dev, "(%d) get FMT tx_head : %d, tx_tail : %d\n", __LINE__, *head, *tail); */ +} + +static inline void ipc_spi_get_pointer_of_vbuff_format_rx(u32 *head, u32 *tail) +{ + memcpy((void *)head, (void *)(p_virtual_buff + 16 + 8), sizeof(*head)); + memcpy((void *)tail, (void *)(p_virtual_buff + 20 + 8), sizeof(*tail)); + /* dev_dbg(&p_ipc_spi->dev, "(%d) get FMT rx_head : %d, rx_tail : %d\n", __LINE__, *head, *tail); */ +} + +static inline void ipc_spi_get_pointer_of_vbuff_raw_tx(u32 *head, u32 *tail) +{ + memcpy((void *)head, (void *)(p_virtual_buff + 32), sizeof(*head)); + memcpy((void *)tail, (void *)(p_virtual_buff + 36), sizeof(*tail)); + /* dev_dbg(&p_ipc_spi->dev, "(%d) get RAW tx_head : %d, tx_tail : %d\n", __LINE__, *head, *tail); */ +} + +static inline void ipc_spi_get_pointer_of_vbuff_raw_rx(u32 *head, u32 *tail) +{ + memcpy((void *)head, (void *)(p_virtual_buff + 32 + 8), sizeof(*head)); + memcpy((void *)tail, (void *)(p_virtual_buff + 36 + 8), sizeof(*tail)); + /* dev_dbg(&p_ipc_spi->dev, "(%d) get RAW rx_head : %d, rx_tail : %d\n", __LINE__, *head, *tail); */ +} + +static inline void ipc_spi_get_pointer_of_vbuff_rfs_tx(u32 *head, u32 *tail) +{ + memcpy((void *)head, (void *)(p_virtual_buff + 48), sizeof(*head)); + memcpy((void *)tail, (void *)(p_virtual_buff + 52), sizeof(*tail)); + /* dev_dbg(&p_ipc_spi->dev, "(%d) get RFS tx_head : %d, tx_tail : %d\n", __LINE__, *head, *tail); */ +} + +static inline void ipc_spi_get_pointer_of_vbuff_rfs_rx(u32 *head, u32 *tail) +{ + memcpy((void *)head, (void *)(p_virtual_buff + 48 + 8), sizeof(*head)); + memcpy((void *)tail, (void *)(p_virtual_buff + 52 + 8), sizeof(*tail)); + /* dev_dbg(&p_ipc_spi->dev, "(%d) get RFS rx_head : %d, rx_tail : %d\n", __LINE__, *head, *tail); */ +} + +static inline void ipc_spi_update_tail_of_vbuff_format_tx(u32 u_tail) +{ + memcpy((void *)(p_virtual_buff + 20), (void *)&u_tail, sizeof(u_tail)); + /* dev_dbg(&p_ipc_spi->dev, "(%d) update FMT tx_tail : %d\n", __LINE__, u_tail); */ +} + +static inline void ipc_spi_update_head_of_vbuff_format_rx(u32 u_head) +{ + memcpy((void *)(p_virtual_buff + 20 + 4), (void *)&u_head, sizeof(u_head)); + /* dev_dbg(&p_ipc_spi->dev, "(%d) update FMT rx_head : %d\n", __LINE__, u_head); */ +} + +static inline void ipc_spi_update_tail_of_vbuff_raw_tx(u32 u_tail) +{ + memcpy((void *)(p_virtual_buff + 36), (void *)&u_tail, sizeof(u_tail)); + /* dev_dbg(&p_ipc_spi->dev, "(%d) update RAW tx_tail : %d\n", __LINE__, u_tail); */ +} + +static inline void ipc_spi_update_head_of_vbuff_raw_rx(u32 u_head) +{ + memcpy((void *)(p_virtual_buff + 36 + 4), (void *)&u_head, sizeof(u_head)); + /* dev_dbg(&p_ipc_spi->dev, "(%d) update RAW rx_head : %d\n", __LINE__, u_head); */ +} + +static inline void ipc_spi_update_tail_of_vbuff_rfs_tx(u32 u_tail) +{ + memcpy((void *)(p_virtual_buff + 52), (void *)&u_tail, sizeof(u_tail)); + /* dev_dbg(&p_ipc_spi->dev, "(%d) update RFS tx_tail : %d\n", __LINE__, u_tail); */ +} + +static inline void ipc_spi_update_head_of_vbuff_rfs_rx(u32 u_head) +{ + memcpy((void *)(p_virtual_buff + 52 + 4), (void *)&u_head, sizeof(u_head)); + /* dev_dbg(&p_ipc_spi->dev, "(%d) update RFS rx_head : %d\n", __LINE__, u_head); */ +} + +#ifndef FEATURE_SAMSUNG_SPI +static +#endif +u32 ipc_spi_get_length_vbuff_format_tx(u32 *out_head, u32 *out_tail) +{ + u32 len = 0; + + ipc_spi_get_pointer_of_vbuff_format_tx(out_head, out_tail); + + if (*out_head >= *out_tail) + len = *out_head - *out_tail; + else + len = FMT_SZ - *out_tail + *out_head; + + /* dev_dbg(&p_ipc_spi->dev, "(%d) get FMT tx_len : %d\n", __LINE__, len); */ + + return len; +} + +static u32 ipc_spi_get_length_vbuff_raw_tx(u32 *out_head, u32 *out_tail) +{ + u32 len = 0; + + ipc_spi_get_pointer_of_vbuff_raw_tx(out_head, out_tail); + + if (*out_head >= *out_tail) + len = *out_head - *out_tail; + else + len = RAW_SZ - *out_tail + *out_head; + + /* dev_dbg(&p_ipc_spi->dev, "(%d) get RAW tx_len : %d\n", __LINE__, len); */ + + return len; +} + +static u32 ipc_spi_get_length_vbuff_rfs_tx(u32 *out_head, u32 *out_tail) +{ + u32 len = 0; + + ipc_spi_get_pointer_of_vbuff_rfs_tx(out_head, out_tail); + + if (*out_head >= *out_tail) + len = *out_head - *out_tail; + else + len = RFS_SZ - *out_tail + *out_head; + + /* dev_dbg(&p_ipc_spi->dev, "(%d) get RFS tx_len : %d\n", __LINE__, len); */ + + return len; +} + +static u32 ipc_spi_get_space_vbuff_format_rx(u32 *in_head, u32 *in_tail) +{ + u32 space = 0; + + ipc_spi_get_pointer_of_vbuff_format_rx(in_head, in_tail); + + /* check the memory space remained */ + if (*in_tail <= *in_head) + space = FMT_SZ - *in_head + *in_tail; + else + space = *in_tail - *in_head; + + /* dev_dbg(&p_ipc_spi->dev, "(%d) get FMT rx space : %d\n", __LINE__, space); */ + + return space; +} + +static u32 ipc_spi_get_space_vbuff_raw_rx(u32 *in_head, u32 *in_tail) +{ + u32 space = 0; + + ipc_spi_get_pointer_of_vbuff_raw_rx(in_head, in_tail); + + /* check the memory space remained */ + if (*in_tail <= *in_head) + space = RAW_SZ - *in_head + *in_tail; + else + space = *in_tail - *in_head; + + /* dev_dbg(&p_ipc_spi->dev, "(%d) get RAW rx space : %d\n", __LINE__, space); */ + + return space; +} + +static u32 ipc_spi_get_space_vbuff_rfs_rx(u32 *in_head, u32 *in_tail) +{ + u32 space = 0; + + ipc_spi_get_pointer_of_vbuff_rfs_rx(in_head, in_tail); + + /* check the memory space remained */ + if (*in_tail <= *in_head) + space = RFS_SZ - *in_head + *in_tail; + else + space = *in_tail - *in_head; + + /* dev_dbg(&p_ipc_spi->dev, "(%d) get RFS rx space : %d\n", __LINE__, space); */ + + return space; +} + +static int ipc_spi_check_send_data(void) +{ + int retval = 0; + u32 out_head = 0; + u32 out_tail = 0; + +#ifdef LOOPBACK_TEST + if (loop_back_test) + retval = 1; +#endif + + if (ipc_spi_get_send_vbuff_command()) + retval = 1; + + ipc_spi_get_pointer_of_vbuff_format_tx(&out_head, &out_tail); + if (out_head != out_tail) + retval = 1; + + ipc_spi_get_pointer_of_vbuff_raw_tx(&out_head, &out_tail); + if (out_head != out_tail) + retval = 1; + + ipc_spi_get_pointer_of_vbuff_rfs_tx(&out_head, &out_tail); + if (out_head != out_tail) + retval = 1; + + return retval; +} + +static void ipc_spi_copy_from_vbuff_format_tx(void *p_des, u32 offset_vbuff, u32 copy_len) +{ + if ((offset_vbuff + copy_len) <= FMT_SZ) { + memcpy((void *)p_des, (void *)(p_virtual_buff + FMT_OUT + offset_vbuff), copy_len); + } else { + memcpy((void *)p_des, (void *)(p_virtual_buff + FMT_OUT + offset_vbuff), FMT_SZ - offset_vbuff); + memcpy((void *)(p_des + (FMT_SZ - offset_vbuff)), (void *)(p_virtual_buff + FMT_OUT), copy_len - (FMT_SZ - offset_vbuff)); + } +} + +static void ipc_spi_copy_from_vbuff_raw_tx(void *p_des, u32 offset_vbuff, u32 copy_len) +{ + if ((offset_vbuff + copy_len) <= RAW_SZ) { + memcpy((void *)p_des, (void *)(p_virtual_buff + RAW_OUT + offset_vbuff), copy_len); + } else { + memcpy((void *)p_des, (void *)(p_virtual_buff + RAW_OUT + offset_vbuff), RAW_SZ - offset_vbuff); + memcpy((void *)(p_des + (RAW_SZ - offset_vbuff)), (void *)(p_virtual_buff + RAW_OUT), copy_len - (RAW_SZ - offset_vbuff)); + } +} + +static void ipc_spi_copy_from_vbuff_rfs_tx(void *p_des, u32 offset_vbuff, u32 copy_len) +{ + if ((offset_vbuff + copy_len) <= RFS_SZ) { + memcpy((void *)p_des, (void *)(p_virtual_buff + RFS_OUT + offset_vbuff), copy_len); + } else { + memcpy((void *)p_des, (void *)(p_virtual_buff + RFS_OUT + offset_vbuff), RFS_SZ - offset_vbuff); + memcpy((void *)(p_des + (RFS_SZ - offset_vbuff)), (void *)(p_virtual_buff + RFS_OUT), copy_len - (RFS_SZ - offset_vbuff)); + } +} + +#ifdef LOOPBACK_TEST +static void ipc_spi_prepare_tx_data(u8 *tx_b) +{ + u32 len = 0, tx_b_remail_len = DEF_BUF_SIZE, read_size = 0; + struct spi_protocol_header *tx_header = (struct spi_protocol_header *)tx_b; + u32 cmd = 0; + u8 cmd_8 = 0; + u16 mux = 0; + u16 p_tx_b = sizeof(struct spi_protocol_header); + u32 p_send_data_h = 0, p_send_data_t = 0; + u16 pkt_fmt_len = 0; + u32 pkt_len = 0; + u8 bof = 0, eof = 0; + int i; + + memset((void *)tx_b, 0, DEF_BUF_SIZE + 4); + + cmd = ipc_spi_get_send_vbuff_command(); /* check mailbox command data */ + if (cmd) { + cmd_8 = cmd & 0xFF; + dev_dbg(&p_ipc_spi->dev, "(%d) =>exist CMD cmd_8 : %x\n", __LINE__, cmd_8); + + mux = 0x0004; + memcpy((void *)(tx_b + sizeof(struct spi_protocol_header)), (void *)&mux, sizeof(mux)); + + memcpy((void *)(tx_b + sizeof(struct spi_protocol_header) + sizeof(mux)), (void *)&cmd_8, sizeof(cmd_8)); + ipc_spi_set_send_vbuff_command_clear(); + + tx_header->current_data_size = sizeof(mux) + sizeof(cmd_8); + tx_header->next_data_size = DEF_BUF_SIZE >> 2; + + if (ipc_spi_check_send_data()) + tx_header->more = 1; + else + tx_header->more = 0; + + } else { /* check format, raw, rfs data */ + len = ipc_spi_get_length_vbuff_format_tx(&p_send_data_h, &p_send_data_t); /* len : vbuff_format_tx length */ + if (len) { + dev_dbg(&p_ipc_spi->dev, "(%d) =>prepare FMT data\n", __LINE__); + + mux = 0x0001; + read_size = 0; + + while (len > read_size) { /* till len is zero */ + /* bof : 0x7F check */ + ipc_spi_copy_from_vbuff_format_tx((void *)&bof, p_send_data_t, sizeof(bof)); + + if (bof != 0x7F) { + dev_err(&p_ipc_spi->dev, "(%d) FMT bof error, remove invalid data. bof : %x\n", __LINE__, bof); + + ipc_spi_update_tail_of_vbuff_format_tx(p_send_data_h); /* remove invalid data */ + memset((void *)tx_b, 0, DEF_BUF_SIZE + 4); + + return; + } + + /* packet length check */ + ipc_spi_copy_from_vbuff_format_tx((void *)&pkt_fmt_len, p_send_data_t + sizeof(bof), sizeof(pkt_fmt_len)); + dev_dbg(&p_ipc_spi->dev, "(%d) =>FMT packet len : %d\n", __LINE__, pkt_fmt_len); + + if ((pkt_fmt_len + sizeof(bof) + sizeof(eof) + sizeof(mux)) > DEF_BUF_SIZE) { + dev_err(&p_ipc_spi->dev, "(%d) FMT wrong packet len, remove invalid data. packet len : %x\n", __LINE__, pkt_fmt_len); + + ipc_spi_update_tail_of_vbuff_format_tx(p_send_data_h); /* remove invalid data */ + memset((void *)tx_b, 0, DEF_BUF_SIZE + 4); + + return; + } else if ((pkt_fmt_len + sizeof(bof) + sizeof(eof) + sizeof(mux)) > tx_b_remail_len) { + dev_dbg(&p_ipc_spi->dev, "(%d) =>FMT tx more set\n", __LINE__); + + tx_header->more = 1; + + break; + } + + /* make spi tx packet (1 packet : vbuff -> tx_b) */ + memcpy((void *)(tx_b + p_tx_b), (void *)&mux, sizeof(mux)); + p_tx_b += sizeof(mux); + tx_b_remail_len -= sizeof(mux); + + ipc_spi_copy_from_vbuff_format_tx((void *)(tx_b + p_tx_b), p_send_data_t, pkt_fmt_len + sizeof(bof) + sizeof(eof)); + +#ifdef FORMAT_TX_DUMP + printk(KERN_ERR "[IPC_SPI => FMT TX :"); + for (i = 0; i < (pkt_fmt_len + sizeof(bof) + sizeof(eof)); i++) + printk(KERN_ERR " %02x", *((u8 *)(tx_b + p_tx_b + i))); + + printk(KERN_ERR "]\n"); +#endif + + p_tx_b += pkt_fmt_len + sizeof(bof) + sizeof(eof); + p_send_data_t += pkt_fmt_len + sizeof(bof) + sizeof(eof); + tx_b_remail_len -= pkt_fmt_len + sizeof(bof) + sizeof(eof); + + p_send_data_t %= FMT_SZ; + ipc_spi_update_tail_of_vbuff_format_tx(p_send_data_t); /* in-pointer update */ + + read_size += pkt_fmt_len + sizeof(bof) + sizeof(eof); + if (len < read_size) + dev_err(&p_ipc_spi->dev, "(%d) FMT tx read error len : %d, read_size : %d\n", __LINE__, len, read_size); + + dev_dbg(&p_ipc_spi->dev, "(%d) =>FMT len : %d\n", __LINE__, len); + } + + tx_header->current_data_size = DEF_BUF_SIZE - tx_b_remail_len; + tx_header->next_data_size = DEF_BUF_SIZE >> 2; + } + + len = ipc_spi_get_length_vbuff_raw_tx(&p_send_data_h, &p_send_data_t); + if (len) { + dev_dbg(&p_ipc_spi->dev, "(%d) =>prepare RAW data\n", __LINE__); + + mux = 0x0002; + read_size = 0; + + while (len > read_size) { /* till len is zero */ + /* bof : 0x7F check */ + ipc_spi_copy_from_vbuff_raw_tx((void *)&bof, p_send_data_t, sizeof(bof)); + + if (bof != 0x7F) { + dev_err(&p_ipc_spi->dev, "(%d) RAW bof error, remove invalid data. bof : %x\n", __LINE__, bof); + + ipc_spi_update_tail_of_vbuff_raw_tx(p_send_data_h); /* remove invalid data */ + memset((void *)tx_b, 0, DEF_BUF_SIZE + 4); + + return; + } + + /* packet length check */ + ipc_spi_copy_from_vbuff_raw_tx((void *)&pkt_len, p_send_data_t + sizeof(bof), sizeof(pkt_len)); + dev_dbg(&p_ipc_spi->dev, "(%d) =>RAW packet len : %d\n", __LINE__, pkt_len); + + if ((pkt_len + sizeof(bof) + sizeof(eof) + sizeof(mux)) > DEF_BUF_SIZE) { + dev_err(&p_ipc_spi->dev, "(%d) RAW wrong packet len, remove invalid data. packet len : %x\n", __LINE__, pkt_len); + + ipc_spi_update_tail_of_vbuff_raw_tx(p_send_data_h); /* remove invalid data */ + memset((void *)tx_b, 0, DEF_BUF_SIZE + 4); + + return; + } else if ((pkt_len + sizeof(bof) + sizeof(eof) + sizeof(mux)) > tx_b_remail_len) { + dev_dbg(&p_ipc_spi->dev, "(%d) =>RAW tx more set\n", __LINE__); + + tx_header->more = 1; + + break; + } + + /* make spi tx packet (1 packet : vbuff -> tx_b) */ + memcpy((void *)(tx_b + p_tx_b), (void *)&mux, sizeof(mux)); + p_tx_b += sizeof(mux); + tx_b_remail_len -= sizeof(mux); + + ipc_spi_copy_from_vbuff_raw_tx((void *)(tx_b + p_tx_b), p_send_data_t, pkt_len + sizeof(bof) + sizeof(eof)); + +#ifdef RAW_TX_DUMP + printk(KERN_ERR "[IPC_SPI => RAW TX :"); + for (i = 0 ; i < (pkt_len + sizeof(bof) + sizeof(eof)) ; i++) + printk(KERN_ERR " %02x", *((u8 *)(tx_b + p_tx_b + i))); + + printk(KERN_ERR "]\n"); +#endif + + p_tx_b += pkt_len + sizeof(bof) + sizeof(eof); + p_send_data_t += pkt_len + sizeof(bof) + sizeof(eof); + tx_b_remail_len -= pkt_len + sizeof(bof) + sizeof(eof); + + p_send_data_t %= RAW_SZ; + ipc_spi_update_tail_of_vbuff_raw_tx(p_send_data_t); /* in-pointer update */ + + read_size += pkt_len + sizeof(bof) + sizeof(eof); + if (len < read_size) + dev_err(&p_ipc_spi->dev, "(%d) RAW tx read error len : %d, read_size : %d\n", __LINE__, len, read_size); + + dev_dbg(&p_ipc_spi->dev, "(%d) =>RAW len : %d\n", __LINE__, len); + } + + tx_header->current_data_size = DEF_BUF_SIZE - tx_b_remail_len; + tx_header->next_data_size = DEF_BUF_SIZE >> 2; + } + + len = ipc_spi_get_length_vbuff_rfs_tx(&p_send_data_h, &p_send_data_t); + if (len) { + dev_dbg(&p_ipc_spi->dev, "(%d) =>prepare RFS data\n", __LINE__); + + mux = 0x0003; + read_size = 0; + + while (len > read_size) { /* till len is zero */ + /* bof : 0x7F check */ + ipc_spi_copy_from_vbuff_rfs_tx((void *)&bof, p_send_data_t, sizeof(bof)); + + if (bof != 0x7F) { + dev_err(&p_ipc_spi->dev, "(%d) RFS bof error, remove invalid data. bof : %x\n", __LINE__, bof); + + ipc_spi_update_tail_of_vbuff_rfs_tx(p_send_data_h); /* remove invalid data */ + memset((void *)tx_b, 0, DEF_BUF_SIZE + 4); + + return; + } + + /* packet length check */ + ipc_spi_copy_from_vbuff_rfs_tx((void *)&pkt_len, p_send_data_t + sizeof(bof), sizeof(pkt_len)); + dev_dbg(&p_ipc_spi->dev, "(%d) =>RFS packet len : %d\n", __LINE__, pkt_len); + + if ((pkt_len + sizeof(bof) + sizeof(eof) + sizeof(mux)) > DEF_BUF_SIZE) { + dev_err(&p_ipc_spi->dev, "(%d) RFS wrong packet len, remove invalid data. packet len : %x\n", __LINE__, pkt_len); + + ipc_spi_update_tail_of_vbuff_rfs_tx(p_send_data_h); /* remove invalid data */ + memset((void *)tx_b, 0, DEF_BUF_SIZE + 4); + + return; + } else if ((pkt_len + sizeof(bof) + sizeof(eof) + sizeof(mux)) > tx_b_remail_len) { + dev_dbg(&p_ipc_spi->dev, "(%d) =>RFS tx more set\n", __LINE__); + + tx_header->more = 1; + + break; + } + + /* make spi tx packet (1 packet : vbuff -> tx_b) */ + memcpy((void *)(tx_b + p_tx_b), (void *)&mux, sizeof(mux)); + p_tx_b += sizeof(mux); + tx_b_remail_len -= sizeof(mux); + + ipc_spi_copy_from_vbuff_rfs_tx((void *)(tx_b + p_tx_b), p_send_data_t, pkt_len + sizeof(bof) + sizeof(eof)); + +#ifdef RFS_TX_DUMP + printk(KERN_ERR "[IPC_SPI => RFS TX :"); + for (i = 0 ; i < (pkt_len + sizeof(bof) + sizeof(eof)) ; i++) + printk(KERN_ERR " %02x", *((u8 *)(tx_b + p_tx_b + i))); + + printk(KERN_ERR "]\n"); +#endif + + p_tx_b += pkt_len + sizeof(bof) + sizeof(eof); + p_send_data_t += pkt_len + sizeof(bof) + sizeof(eof); + tx_b_remail_len -= pkt_len + sizeof(bof) + sizeof(eof); + + p_send_data_t %= RFS_SZ; + ipc_spi_update_tail_of_vbuff_rfs_tx(p_send_data_t); /* in-pointer update */ + + read_size += pkt_len + sizeof(bof) + sizeof(eof); + if (len < read_size) + dev_err(&p_ipc_spi->dev, "(%d) RFS tx read error len : %d, read_size : %d\n", __LINE__, len, read_size); + + dev_dbg(&p_ipc_spi->dev, "(%d) =>RFS len : %d\n", __LINE__, len); + +#ifdef RFS_TX_RX_LENGTH_DUMP + printk(KERN_ERR "[IPC_SPI => RFS TX : %d]\n", pkt_len); +#endif + } + + tx_header->current_data_size = DEF_BUF_SIZE - tx_b_remail_len; + tx_header->next_data_size = DEF_BUF_SIZE >> 2; + } + } + +#ifndef LOOPBACK_TEST + dev_dbg(&p_ipc_spi->dev, "[SPI DUMP] TX : [%02x %02x %02x %02x | %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x]\n", + tx_b[0], tx_b[1], tx_b[2], tx_b[3], tx_b[4], tx_b[5], tx_b[6], tx_b[7], tx_b[8], tx_b[9], tx_b[10], tx_b[11], tx_b[12], tx_b[13], tx_b[14], tx_b[15], tx_b[16], tx_b[17], tx_b[18], tx_b[19], + tx_b[20], tx_b[21], tx_b[22], tx_b[23], tx_b[24], tx_b[25], tx_b[26], tx_b[27], tx_b[28], tx_b[29], tx_b[30], tx_b[31], tx_b[32], tx_b[33], tx_b[34], tx_b[35], tx_b[36], tx_b[37], tx_b[38], tx_b[39]); +#endif +} + +static void ipc_spi_prepare_loopback_tx_data(u8 *tx_b, u8 *rx_b, u8 *test_data) +{ + struct spi_protocol_header *tx_header = (struct spi_protocol_header *)tx_b; + u16 mux = 0; + u8 bof = 0x7F, eof = 0x7E; + u16 pkt_len = LOOP_SIZE - 8; + u16 ipc_len = pkt_len - 3; + u32 ipc_header = 0x010B0A00; + + int i; + + memset((void *)tx_b, 0, DEF_BUF_SIZE + 4); + + mux = 0x0001; + + memcpy((void *)(tx_b + sizeof(struct spi_protocol_header)), (void *)&mux, sizeof(mux)); + memcpy((void *)(tx_b + sizeof(struct spi_protocol_header) + sizeof(mux)), (void *)&bof, sizeof(bof)); + memcpy((void *)(tx_b + sizeof(struct spi_protocol_header) + sizeof(mux) + sizeof(bof)), (void *)&pkt_len, sizeof(pkt_len)); + memcpy((void *)(tx_b + sizeof(struct spi_protocol_header) + sizeof(mux) + sizeof(bof) + sizeof(pkt_len) + 1), (void *)&ipc_len, sizeof(ipc_len)); + memcpy((void *)(tx_b + sizeof(struct spi_protocol_header) + sizeof(mux) + sizeof(bof) + sizeof(pkt_len) + 4), (void *)&ipc_header, sizeof(ipc_header)); + memcpy((void *)(tx_b + sizeof(struct spi_protocol_header) + sizeof(mux) + sizeof(bof) + sizeof(pkt_len) + 4 + 4), (void *)test_data, LOOP_SIZE-18); + memcpy((void *)(tx_b + sizeof(struct spi_protocol_header) + sizeof(mux) + sizeof(bof) + pkt_len), (void *)&eof, sizeof(eof)); + + tx_header->current_data_size = sizeof(mux) + sizeof(bof) + pkt_len + sizeof(eof); + tx_header->next_data_size = DEF_BUF_SIZE >> 2; + + printk(KERN_ERR "[SPI DUMP] LOOP TX :]"); + for (i = 0 ; i < 30 ; i++) + printk(KERN_ERR "%02x ", *((u8 *)(tx_b + i))); + printk(KERN_ERR "| "); + for (i = LOOP_SIZE - 30 ; i < LOOP_SIZE ; i++) + printk(KERN_ERR "%02x ", *((u8 *)(tx_b + i))); + printk(KERN_ERR "]\n"); + +} +#endif + +#if 0 +static void ipc_spi_prepare_loopback_tx_data(u8 *tx_b, u8 *rx_b) +{ + struct spi_protocol_header *tx_header = (struct spi_protocol_header *)tx_b; + u16 mux = 0; + u8 bof = 0x7F, eof = 0x7E; + u32 pkt_len = 16; + u8 test_data[10] = "0123456789"; + u8 ch_id = 31, control_id = 0; + + int i; + + memset((void *)tx_b, 0, DEF_BUF_SIZE + 4); + + mux = 0x0002; + + memcpy((void *)(tx_b + sizeof(struct spi_protocol_header)), (void *)&mux, sizeof(mux)); + memcpy((void *)(tx_b + sizeof(struct spi_protocol_header) + sizeof(mux)), (void *)&bof, sizeof(bof)); + memcpy((void *)(tx_b + sizeof(struct spi_protocol_header) + sizeof(mux) + sizeof(bof)), (void *)&pkt_len, sizeof(pkt_len)); + memcpy((void *)(tx_b + sizeof(struct spi_protocol_header) + sizeof(mux) + sizeof(bof) + sizeof(pkt_len)), + (void *)&ch_id, sizeof(ch_id)); + memcpy((void *)(tx_b + sizeof(struct spi_protocol_header) + sizeof(mux) + sizeof(bof) + sizeof(pkt_len) + sizeof(ch_id)), + (void *)&control_id, sizeof(control_id)); + memcpy((void *)(tx_b + sizeof(struct spi_protocol_header) + sizeof(mux) + sizeof(bof) + sizeof(pkt_len) + + sizeof(ch_id) + sizeof(control_id)), (void *)&test_data, sizeof(test_data)); + memcpy((void *)(tx_b + sizeof(struct spi_protocol_header) + sizeof(mux) + sizeof(bof) + pkt_len), (void *)&eof, sizeof(eof)); + + tx_header->current_data_size = sizeof(mux) + sizeof(bof) + pkt_len + sizeof(eof); + tx_header->next_data_size = DEF_BUF_SIZE >> 2; + + printk(KERN_ERR "[SPI DUMP] LOOP TX : "); + for (i = 0 ; i < 30 ; i++) + printk(KERN_ERR "%02x ", tx_b[i]); + printk(KERN_ERR "\n"); +} +#endif + +static void ipc_spi_set_MRDY_pin(int val) +{ + /* dev_dbg(&p_ipc_spi->dev, "(%d) set MRDY %d\n", __LINE__, val); */ + + gpio_set_value(gpio_mrdy, val); +} + +#ifndef FEATURE_SAMSUNG_SPI +static +#endif +int ipc_spi_tx_rx_sync(u8 *tx_d, u8 *rx_d, unsigned len) +{ + struct spi_transfer t; + struct spi_message msg; + + memset(&t, 0, sizeof t); + + t.len = len; + + t.tx_buf = tx_d; + t.rx_buf = rx_d; + + t.cs_change = 0; + + t.bits_per_word = 32; + t.speed_hz = 12000000; + + spi_message_init(&msg); + spi_message_add_tail(&t, &msg); + + return spi_sync(p_ipc_spi, &msg); +} + +static void ipc_spi_swap_data_htn(u8 *data, int len) +{ + int i; + + for (i = 0 ; i < len ; i += 4) + *(u32 *)(data + i) = htonl(*(u32 *)(data + i)); +} + +static void ipc_spi_swap_data_nth(u8 *data, int len) +{ + int i; + + for (i = 0 ; i < len ; i += 4) + *(u32 *)(data + i) = ntohl(*(u32 *)(data + i)); +} + +static int ipc_spi_copy_to_vbuff_format_rx(void *data, u16 len) +{ + u32 head = 0, tail = 0, new_head = 0, space = 0; + int copy_retry_count = 0; + + dev_dbg(&p_ipc_spi->dev, "(%d) <=copy data to FMT vbuff, len : %d\n", __LINE__, len); + +COPY_TO_VBUFF_FMT_RETRY: + + space = ipc_spi_get_space_vbuff_format_rx(&head, &tail); + if (space < len) { + dev_err(&p_ipc_spi->dev, "(%d) FMT vbuff is full, space : %d, len : %d\n", __LINE__, space, len); + + copy_retry_count++; + if (copy_retry_count > 20) { + dev_err(&p_ipc_spi->dev, "(%d) FMT vbuff is full. copy fail.\n", __LINE__); + + return -ENOMEM; + } + + msleep(20); + goto COPY_TO_VBUFF_FMT_RETRY; + } + + if (head + len <= FMT_SZ) { + memcpy((void *)(p_virtual_buff + FMT_IN + head), data, len); + } else { + memcpy((void *)(p_virtual_buff + FMT_IN + head), data, FMT_SZ - head); + memcpy((void *)(p_virtual_buff + FMT_IN), (void *)(data + (FMT_SZ - head)), len - (FMT_SZ - head)); + } + + new_head = (head + len) % FMT_SZ; + ipc_spi_update_head_of_vbuff_format_rx(new_head); + + dev_dbg(&p_ipc_spi->dev, "(%d) <=copy data to FMT vbuff done.\n", __LINE__); + + return 0; +} + +static int ipc_spi_copy_to_vbuff_raw_rx(void *data, u32 len) +{ + u32 head = 0, tail = 0, new_head = 0, space = 0; + int copy_retry_count = 0; + + dev_dbg(&p_ipc_spi->dev, "(%d) <=copy data to RAW vbuff, len : %d\n", __LINE__, len); + +COPY_TO_VBUFF_RAW_RETRY: + + space = ipc_spi_get_space_vbuff_raw_rx(&head, &tail); + if (space < len) { + dev_err(&p_ipc_spi->dev, "(%d) RAW vbuff is full, space : %d, len : %d\n", __LINE__, space, len); + + copy_retry_count++; + if (copy_retry_count > 20) { + dev_err(&p_ipc_spi->dev, "(%d) RAW vbuff is full. copy fail.\n", __LINE__); + + return -ENOMEM; + } + + msleep(20); + goto COPY_TO_VBUFF_RAW_RETRY; + } + + if (head + len <= RAW_SZ) { + memcpy((void *)(p_virtual_buff + RAW_IN + head), data, len); + } else { + memcpy((void *)(p_virtual_buff + RAW_IN + head), data, RAW_SZ - head); + memcpy((void *)(p_virtual_buff + RAW_IN), (void *)(data + (RAW_SZ - head)), len - (RAW_SZ - head)); + } + + new_head = (head + len) % RAW_SZ; + ipc_spi_update_head_of_vbuff_raw_rx(new_head); + + dev_dbg(&p_ipc_spi->dev, "(%d) <=copy data to RAW vbuff done.\n", __LINE__); + + return 0; +} + +static int ipc_spi_copy_to_vbuff_rfs_rx(void *data, u32 len) +{ + u32 head = 0, tail = 0, new_head = 0, space = 0; + int copy_retry_count = 0; + + dev_dbg(&p_ipc_spi->dev, "(%d) <=copy data to RFS vbuff, len : %d\n", __LINE__, len); + +COPY_TO_VBUFF_RFS_RETRY: + + space = ipc_spi_get_space_vbuff_rfs_rx(&head, &tail); + if (space < len) { + dev_err(&p_ipc_spi->dev, "(%d) RFS vbuff is full, space : %d, len : %d\n", __LINE__, space, len); + + copy_retry_count++; + if (copy_retry_count > 20) { + dev_err(&p_ipc_spi->dev, "(%d) RFS vbuff is full. copy fail.\n", __LINE__); + + return -ENOMEM; + } + + msleep(20); + goto COPY_TO_VBUFF_RFS_RETRY; + } + + if (head + len <= RFS_SZ) { + memcpy((void *)(p_virtual_buff + RFS_IN + head), data, len); + } else { + memcpy((void *)(p_virtual_buff + RFS_IN + head), data, RFS_SZ - head); + memcpy((void *)(p_virtual_buff + RFS_IN), (void *)(data + (RFS_SZ - head)), len - (RFS_SZ - head)); + } + + new_head = (head + len) % RFS_SZ; + ipc_spi_update_head_of_vbuff_rfs_rx(new_head); + + dev_dbg(&p_ipc_spi->dev, "(%d) <=copy data to RFS vbuff done.\n", __LINE__); + + return 0; +} + +#ifndef FEATURE_SAMSUNG_SPI +static +#endif +void ipc_spi_make_data_interrupt(u32 cmd, struct ipc_spi *od) +{ + struct list_head *l; + unsigned long flags; + u32 mailbox; + + mailbox = cmd; + /* dev_dbg(&p_ipc_spi->dev, "(%d) <=make data int : 0x%08x\n", __LINE__, mailbox); */ + + if (h_list.len) { + spin_lock_irqsave(&h_list.lock, flags); + list_for_each(l, &h_list.list) { + struct ipc_spi_handler *h = list_entry(l, struct ipc_spi_handler, list); + + if (h->handler) + h->handler(mailbox, h->data); + } + spin_unlock_irqrestore(&h_list.lock, flags); + + spin_lock(&ipc_spi_lock); + od->mailbox = mailbox; + spin_unlock(&ipc_spi_lock); + } else { + od->mailbox = mailbox; + } + +/* dev_dbg(&p_ipc_spi->dev, "(%d) <=send data int cmd event\n", __LINE__); */ + + wake_up_interruptible(&od->waitq); + kill_fasync(&od->async_queue, SIGIO, POLL_IN); +} + +static int rx_prev_data_saved; +static u16 rx_prev_data_mux; +static u32 rx_prev_data_size; +static u32 rx_prev_data_remain; + +static void ipc_spi_rx_process(u8 *rx_b, u8 *rx_save_b, struct ipc_spi *od) +{ + int retval = 0; + struct spi_protocol_header *rx_header = (struct spi_protocol_header *)rx_b; + u32 total_size = 0, read_size = 0; + u16 packet_fmt_len = 0; + u32 packet_len = 0; + u8 bof = 0, eof = 0; + u16 mux = 0; + u16 p_read = 4; + u32 int_cmd = 0; + int i; + +/* dev_dbg(&p_ipc_spi->dev, "(%d) rx process, more : %d, CTS : %d, current size : %d, next size : %d\n", __LINE__, rx_header->more, rx_header->RTSCTS, rx_header->current_data_size, rx_header->next_data_size); */ + + total_size = rx_header->current_data_size; + + while (total_size > read_size) { + int_cmd = 0; + + if (!rx_prev_data_saved) { + /* check bof : 0x7F */ + memcpy((void *)&bof, (void *)(rx_b + p_read + sizeof(mux)), sizeof(bof)); +/* dev_dbg(&p_ipc_spi->dev, "(%d) rx process, bof : %x\n", __LINE__, bof); */ + if (bof != 0x7F) { + dev_err(&p_ipc_spi->dev, "(%d) rx process, bof error : %x\n", __LINE__, bof); +/* + printk(KERN_ERR "[IPC_SPI <= RX :"); + for (i = 0 ; i < DEF_BUF_SIZE + 4 ; i++) { + printk(KERN_ERR " %02x", *((u8 *)(rx_b + i))); + } + printk(KERN_ERR "]\n"); +*/ + break; + } + } + + if (!rx_prev_data_saved) { + /* read mux */ + memcpy((void *)&mux, (void *)(rx_b + p_read), sizeof(mux)); +/* dev_dbg(&p_ipc_spi->dev, "(%d) rx process, mux : 0x%04x\n", __LINE__, mux); */ + if (mux > 0x4 || mux < 0x0) { + dev_err(&p_ipc_spi->dev, "(%d) rx process, mux error : %x\n", __LINE__, mux); + break; + } + } else { + mux = rx_prev_data_mux; + + dev_dbg(&p_ipc_spi->dev, "(%d) <=rx prev data, prev_mux : %d\n", __LINE__, mux); + } + + switch (mux) { + case 0x0001: +/* dev_dbg(&p_ipc_spi->dev, "(%d) rx process, got FMT : %x\n", __LINE__, mux); */ + + if (rx_prev_data_saved) { + if (rx_prev_data_remain > DEF_BUF_SIZE) { + dev_dbg(&p_ipc_spi->dev, "(%d) <=rx 2nd prev data, FMT saved : %d, remain : %d\n", __LINE__, rx_prev_data_size, rx_prev_data_remain); + + rx_prev_data_mux = 0x0001; + + memcpy((void *)(rx_save_b + rx_prev_data_size), (void *)(rx_b + 4), DEF_BUF_SIZE); + rx_prev_data_size += DEF_BUF_SIZE; + rx_prev_data_remain -= DEF_BUF_SIZE; + dev_dbg(&p_ipc_spi->dev, "(%d) <=rx 2nd prev data save, FMT size : %d, remain size : %d\n", __LINE__, rx_prev_data_size, rx_prev_data_remain); + + rx_prev_data_saved = 1; + + return; + } + + memcpy((void *)(rx_save_b + rx_prev_data_size), (void *)(rx_b + 4), rx_prev_data_remain); + p_read += rx_prev_data_remain; + read_size += rx_prev_data_remain; + + if (*rx_save_b != 0x7F) { + dev_err(&p_ipc_spi->dev, "(%d) <=rx prev, FMT bof error : %x\n", __LINE__, *rx_save_b); + + rx_prev_data_saved = 0; + + return; + } + + if (*(u8 *)(rx_save_b + rx_prev_data_size + rx_prev_data_remain - 1) != 0x7E) { + dev_err(&p_ipc_spi->dev, "(%d) <=rx prev, FMT eof error : %x\n", __LINE__, *(u8 *)(rx_save_b + rx_prev_data_size + rx_prev_data_remain - 1)); + + rx_prev_data_saved = 0; + + return; + } + + /* copy to v_buff */ + retval = ipc_spi_copy_to_vbuff_format_rx((void *)rx_save_b, rx_prev_data_size + rx_prev_data_remain); + if (retval < 0) { + dev_err(&p_ipc_spi->dev, "(%d) rx prev data, cp -> ap FMT memory FULL!!!.\n", __LINE__); + return; + } else { + dev_dbg(&p_ipc_spi->dev, "(%d) rx prev data, <= one packet FMT read Done. size : %d, total_size : %d, read_size : %d, p_read : %d\n", __LINE__, + rx_prev_data_size + rx_prev_data_remain, total_size, read_size, p_read); + } + +#ifdef FORMAT_RX_DUMP + printk(KERN_ERR "[IPC_SPI <= FMT RX :"); + for (i = 0 ; i < (rx_prev_data_size + rx_prev_data_remain) ; i++) + printk(KERN_ERR " %02x", *((u8 *)(rx_save_b + i))); + printk(KERN_ERR "]\n"); +#endif + + rx_prev_data_saved = 0; + } else { + /* read packet len */ + memcpy((void *)&packet_fmt_len, (void *)(rx_b + p_read + sizeof(mux) + sizeof(bof)), sizeof(packet_fmt_len)); + dev_dbg(&p_ipc_spi->dev, "(%d) <=rx process, FMT pkt len : %d\n", __LINE__, packet_fmt_len); +/* if (packet_fmt_len > DEF_BUF_SIZE) { + dev_err(&p_ipc_spi->dev, "(%d) rx process, FMT pkt len error : %d\n", __LINE__, packet_fmt_len); + return; + } */ + + if ((packet_fmt_len + sizeof(mux) + sizeof(bof) + sizeof(eof)) > (total_size - read_size)) { + dev_dbg(&p_ipc_spi->dev, "(%d) <=rx prev data, FMT packet_fmt_len : %d, remain : %d\n", __LINE__, packet_fmt_len, total_size - read_size); + + rx_prev_data_mux = 0x0001; + + memcpy((void *)rx_save_b, (void *)(rx_b + p_read + sizeof(mux)), total_size - read_size - sizeof(mux)); + rx_prev_data_size = total_size - read_size - sizeof(mux); + rx_prev_data_remain = packet_fmt_len + sizeof(bof) + sizeof(eof) - rx_prev_data_size; + dev_dbg(&p_ipc_spi->dev, "(%d) <=rx prev data save, FMT size : %d, remain size : %d\n", __LINE__, rx_prev_data_size, rx_prev_data_remain); + + rx_prev_data_saved = 1; + + return; + } + + /* copy to v_buff */ + retval = ipc_spi_copy_to_vbuff_format_rx((void *)(rx_b + p_read + sizeof(mux)), packet_fmt_len + sizeof(bof) + sizeof(eof)); + if (retval < 0) { + dev_err(&p_ipc_spi->dev, "(%d) rx process, cp -> ap FMT memory FULL!!!.\n", __LINE__); + return; + } else { +/* dev_dbg(&p_ipc_spi->dev, "(%d) rx process, one packet FMT read Done.\n", __LINE__); */ + } + +#ifdef FORMAT_RX_DUMP + printk(KERN_ERR "[IPC_SPI <= FMT RX :"); + for (i = 0 ; i < (packet_fmt_len + sizeof(bof) + sizeof(eof)) ; i++) + printk(KERN_ERR " %02x", *((u8 *)(rx_b + p_read + sizeof(mux) + i))); + printk(KERN_ERR "]\n"); +#endif + + p_read += packet_fmt_len + sizeof(bof) + sizeof(eof) + sizeof(mux); + read_size += packet_fmt_len + sizeof(bof) + sizeof(eof) + sizeof(mux); +/* dev_dbg(&p_ipc_spi->dev, "(%d) rx process, FMT read size : %d\n", __LINE__, read_size); */ + } + + /* make data interrupt cmd */ + int_cmd = MB_DATA(MBD_SEND_FMT); + ipc_spi_make_data_interrupt(int_cmd, od); + break; + + case 0x0002: +/* dev_dbg(&p_ipc_spi->dev, "(%d) rx process, got RAW : %x\n", __LINE__, mux); */ + + if (rx_prev_data_saved) { + if (rx_prev_data_remain > DEF_BUF_SIZE) { + dev_dbg(&p_ipc_spi->dev, "(%d) <=rx 2nd prev data, RAW saved : %d, remain : %d\n", __LINE__, rx_prev_data_size, rx_prev_data_remain); + + rx_prev_data_mux = 0x0002; + + memcpy((void *)(rx_save_b + rx_prev_data_size), (void *)(rx_b + 4), DEF_BUF_SIZE); + rx_prev_data_size += DEF_BUF_SIZE; + rx_prev_data_remain -= DEF_BUF_SIZE; + dev_dbg(&p_ipc_spi->dev, "(%d) <=rx 2nd prev data save, RAW size : %d, remain size : %d\n", __LINE__, rx_prev_data_size, rx_prev_data_remain); + + rx_prev_data_saved = 1; + + return; + } + + memcpy((void *)(rx_save_b + rx_prev_data_size), (void *)(rx_b + 4), rx_prev_data_remain); + p_read += rx_prev_data_remain; + read_size += rx_prev_data_remain; + + if (*rx_save_b != 0x7F) { + dev_err(&p_ipc_spi->dev, "(%d) <=rx prev, RAW bof error : %x\n", __LINE__, *rx_save_b); + + rx_prev_data_saved = 0; + + return; + } + + if (*(u8 *)(rx_save_b + rx_prev_data_size + rx_prev_data_remain - 1) != 0x7E) { + dev_err(&p_ipc_spi->dev, "(%d) <=rx prev, RAW eof error : %x\n", __LINE__, *(u8 *)(rx_save_b + rx_prev_data_size + rx_prev_data_remain - 1)); + + rx_prev_data_saved = 0; + + return; + } + + /* copy to v_buff */ + retval = ipc_spi_copy_to_vbuff_raw_rx((void *)rx_save_b, rx_prev_data_size + rx_prev_data_remain); + if (retval < 0) { + dev_err(&p_ipc_spi->dev, "(%d) rx prev data, cp -> ap RAW memory FULL!!!.\n", __LINE__); + return; + } else { + dev_dbg(&p_ipc_spi->dev, "(%d) rx prev data, <= one packet RAW read Done. size : %d, total_size : %d, read_size : %d, p_read : %d\n", __LINE__, + rx_prev_data_size + rx_prev_data_remain, total_size, read_size, p_read); + } + +#ifdef RAW_RX_DUMP + printk(KERN_ERR "[IPC_SPI <= RAW RX :"); + for (i = 0 ; i < (rx_prev_data_size + rx_prev_data_remain) ; i++) + printk(KERN_ERR " %02x", *((u8 *)(rx_save_b + i))); + printk(KERN_ERR "]\n"); +#endif + + rx_prev_data_saved = 0; + } else { + /* read packet len */ + memcpy((void *)&packet_len, (void *)(rx_b + p_read + sizeof(mux) + sizeof(bof)), sizeof(packet_len)); + dev_dbg(&p_ipc_spi->dev, "(%d) <=rx process, RAW pkt len : %d\n", __LINE__, packet_len); +/* if (packet_len > DEF_BUF_SIZE) { + dev_err(&p_ipc_spi->dev, "(%d) rx process, RAW pkt len error : %d\n", __LINE__, packet_len); + return; + } */ + + if ((packet_len + sizeof(mux) + sizeof(bof) + sizeof(eof)) > (total_size - read_size)) { + dev_dbg(&p_ipc_spi->dev, "(%d) <=rx prev data, RAW packet_len : %d, remain : %d\n", __LINE__, packet_len, total_size - read_size); + + rx_prev_data_mux = 0x0002; + + memcpy((void *)rx_save_b, (void *)(rx_b + p_read + sizeof(mux)), total_size - read_size - sizeof(mux)); + rx_prev_data_size = total_size - read_size - sizeof(mux); + rx_prev_data_remain = packet_len + sizeof(bof) + sizeof(eof) - rx_prev_data_size; + dev_dbg(&p_ipc_spi->dev, "(%d) <=rx prev data save, RAW size : %d, remain size : %d\n", __LINE__, rx_prev_data_size, rx_prev_data_remain); + + rx_prev_data_saved = 1; + + return; + } + + /* copy to v_buff */ + retval = ipc_spi_copy_to_vbuff_raw_rx((void *)(rx_b + p_read + sizeof(mux)), packet_len + sizeof(bof) + sizeof(eof)); + if (retval < 0) { + dev_err(&p_ipc_spi->dev, "(%d) rx process, cp -> ap RAW memory FULL!!!.\n", __LINE__); + return; + } else { +/* dev_dbg(&p_ipc_spi->dev, "(%d) rx process, one packet RAW read Done.\n", __LINE__); */ + } + +#ifdef RAW_RX_DUMP + printk(KERN_ERR "[IPC_SPI <= RAW RX :"); + for (i = 0 ; i < (packet_len + sizeof(bof) + sizeof(eof)) ; i++) + printk(KERN_ERR " %02x", *((u8 *)(rx_b + p_read + sizeof(mux) + i))); + printk(KERN_ERR "]\n"); +#endif + + p_read += packet_len + sizeof(bof) + sizeof(eof) + sizeof(mux); + read_size += packet_len + sizeof(bof) + sizeof(eof) + sizeof(mux); +/* dev_dbg(&p_ipc_spi->dev, "(%d) rx process, RAW read size : %d\n", __LINE__, read_size); */ + } + + /* make data interrupt cmd */ + int_cmd = MB_DATA(MBD_SEND_RAW); + ipc_spi_make_data_interrupt(int_cmd, od); + break; + + case 0x0003: +/* dev_dbg(&p_ipc_spi->dev, "(%d) rx process, got RFS : %x\n", __LINE__, mux); */ + + if (rx_prev_data_saved) { + if (rx_prev_data_remain > DEF_BUF_SIZE) { + dev_dbg(&p_ipc_spi->dev, "(%d) <=rx 2nd prev data, RFS saved : %d, remain : %d\n", __LINE__, rx_prev_data_size, rx_prev_data_remain); + + rx_prev_data_mux = 0x0003; + + memcpy((void *)(rx_save_b + rx_prev_data_size), (void *)(rx_b + 4), DEF_BUF_SIZE); + rx_prev_data_size += DEF_BUF_SIZE; + rx_prev_data_remain -= DEF_BUF_SIZE; + dev_dbg(&p_ipc_spi->dev, "(%d) <=rx 2nd prev data save, RFS size : %d, remain size : %d\n", __LINE__, rx_prev_data_size, rx_prev_data_remain); + + rx_prev_data_saved = 1; + + return; + } + + memcpy((void *)(rx_save_b + rx_prev_data_size), (void *)(rx_b + 4), rx_prev_data_remain); + p_read += rx_prev_data_remain; + read_size += rx_prev_data_remain; + + if (*rx_save_b != 0x7F) { + dev_err(&p_ipc_spi->dev, "(%d) <=rx prev, RFS bof error : %x\n", __LINE__, *rx_save_b); + + rx_prev_data_saved = 0; + + return; + } + + if (*(u8 *)(rx_save_b + rx_prev_data_size + rx_prev_data_remain - 1) != 0x7E) { + dev_err(&p_ipc_spi->dev, "(%d) <=rx prev, RFS eof error : %x\n", __LINE__, *(u8 *)(rx_save_b + rx_prev_data_size + rx_prev_data_remain - 1)); + + rx_prev_data_saved = 0; + + return; + } + + /* copy to v_buff */ + retval = ipc_spi_copy_to_vbuff_rfs_rx((void *)rx_save_b, rx_prev_data_size + rx_prev_data_remain); + if (retval < 0) { + dev_err(&p_ipc_spi->dev, "(%d) rx prev data, cp -> ap RFS memory FULL!!!.\n", __LINE__); + return; + } else { + dev_dbg(&p_ipc_spi->dev, "(%d) rx prev data, <= one packet RFS read Done. size : %d, total_size : %d, read_size : %d, p_read : %d\n", __LINE__, + rx_prev_data_size + rx_prev_data_remain, total_size, read_size, p_read); + } + +#ifdef RFS_RX_DUMP + printk(KERN_ERR "[IPC_SPI <= RFS RX :"); + for (i = 0 ; i < (rx_prev_data_size + rx_prev_data_remain) ; i++) + printk(KERN_ERR " %02x", *((u8 *)(rx_save_b + i))); + printk(KERN_ERR "]\n"); +#endif + +#ifdef RFS_TX_RX_LENGTH_DUMP + printk(KERN_ERR "[IPC_SPI <= RFS RX : %d]\n", rx_prev_data_size + rx_prev_data_remain); +#endif + + rx_prev_data_saved = 0; + } else { + /* read packet len */ + memcpy((void *)&packet_len, (void *)(rx_b + p_read + sizeof(mux) + sizeof(bof)), sizeof(packet_len)); + dev_dbg(&p_ipc_spi->dev, "(%d) <=rx process, RFS pkt len : %d\n", __LINE__, packet_len); +/* if (packet_len > DEF_BUF_SIZE) { + dev_err(&p_ipc_spi->dev, "(%d) rx process, RFS pkt len error : %d\n", __LINE__, packet_len); + return; + } */ + + if ((packet_len + sizeof(mux) + sizeof(bof) + sizeof(eof)) > (total_size - read_size)) { + dev_dbg(&p_ipc_spi->dev, "(%d) <=rx prev data, RFS packet_len : %d, remain : %d\n", __LINE__, packet_len, total_size - read_size); + + rx_prev_data_mux = 0x0003; + + memcpy((void *)rx_save_b, (void *)(rx_b + p_read + sizeof(mux)), total_size - read_size - sizeof(mux)); + rx_prev_data_size = total_size - read_size - sizeof(mux); + rx_prev_data_remain = packet_len + sizeof(bof) + sizeof(eof) - rx_prev_data_size; + dev_dbg(&p_ipc_spi->dev, "(%d) <=rx prev data save, RFS size : %d, remain size : %d\n", __LINE__, rx_prev_data_size, rx_prev_data_remain); + + rx_prev_data_saved = 1; + + return; + } + + /* copy to v_buff */ + retval = ipc_spi_copy_to_vbuff_rfs_rx((void *)(rx_b + p_read + sizeof(mux)), packet_len + sizeof(bof) + sizeof(eof)); + if (retval < 0) { + dev_err(&p_ipc_spi->dev, "(%d) rx process, cp -> ap RFS memory FULL!!!.\n", __LINE__); + return; + } else { +/* dev_dbg(&p_ipc_spi->dev, "(%d) rx process, one packet RFS read Done.\n", __LINE__); */ + } + +#ifdef RFS_RX_DUMP + printk(KERN_ERR "[IPC_SPI <= RFS RX :"); + for (i = 0 ; i < (packet_len + sizeof(bof) + sizeof(eof)) ; i++) + printk(KERN_ERR " %02x", *((u8 *)(rx_b + p_read + sizeof(mux) + i))); + printk(KERN_ERR "]\n"); +#endif + + p_read += packet_len + sizeof(bof) + sizeof(eof) + sizeof(mux); + read_size += packet_len + sizeof(bof) + sizeof(eof) + sizeof(mux); +/* dev_dbg(&p_ipc_spi->dev, "(%d) rx process, RFS read size : %d\n", __LINE__, read_size); */ + +#ifdef RFS_TX_RX_LENGTH_DUMP + printk(KERN_ERR "[IPC_SPI <= RFS RX : %d]\n", packet_len); +#endif + + } + + /* make data interrupt cmd */ + int_cmd = MB_DATA(MBD_SEND_RFS); + ipc_spi_make_data_interrupt(int_cmd, od); + break; + + case 0x0004: + dev_dbg(&p_ipc_spi->dev, "(%d) <=rx process, got command : %x\n", __LINE__, mux); + break; + + default: + break; + } + + if (total_size < read_size) + dev_err(&p_ipc_spi->dev, "(%d) rx process, read error.\n", __LINE__); + +/* dev_dbg(&p_ipc_spi->dev, "(%d) rx process, LOOP ==== total : %d, read : %d, p_read : %d\n", __LINE__, total_size, read_size, p_read); */ + } +} + +static int ipc_spi_thread(void *data) +{ + struct ipc_spi *od = (struct ipc_spi *)data; + + int retval = 0; +#ifdef LOOPBACK_TEST + u8 *tx_buf = NULL; + struct spi_protocol_header *tx_header = NULL; + u8 *rx_buf = NULL; + struct spi_protocol_header *rx_header = NULL; + int skip_SRDY_chk = 0; + int clear_tx_buf = 0; + u8 *rx_save_buf = NULL; + u8 *test_data = NULL; + int tx_loopback_data = 1; + u8 rx_loopback_data; + int i, rx_loopback_count = 0; +#endif + +#if defined(CONFIG_MACH_Q1_BD) + u32 val = __raw_readl(S5P_INFORM2); + + if (val == 1) { + printk(KERN_ERR "[LPM mode] ipc_spi_thread exit!\n"); + goto exit; + } +#else + if (lpcharge == 1) { + printk(KERN_ERR "[LPM mode] ipc_spi_thread exit!\n"); + goto exit; + } +#endif + + daemonize("ipc_spi_thread"); + + printk(KERN_ERR "[%s] ipc_spi_thread start.\n", __func__); + boot_done = 1; + + wait_for_completion(&ril_init); + + printk(KERN_ERR "[%s] ril_init completed.\n", __func__); + + if (!p_ipc_spi) { + printk(KERN_ERR "[%s] p_ipc_spi is NULL.\n", __func__); + + retval = -ENODEV; + goto exit; + } + + if (!p_virtual_buff) { + dev_err(&p_ipc_spi->dev, "[%s] p_virtual_buff is NULL.\n", __func__); + + retval = -ENODEV; + goto exit; + } + +#ifdef LOOPBACK_TEST + tx_buf = kmalloc(DEF_BUF_SIZE + 4, GFP_ATOMIC); + if (!tx_buf) { + dev_err(&p_ipc_spi->dev, "[%s] tx_buf kmalloc fail.", __func__); + + retval = -ENOMEM; + goto exit; + } + tx_header = (struct spi_protocol_header *)tx_buf; + + rx_buf = kmalloc(DEF_BUF_SIZE + 4, GFP_ATOMIC); + if (!rx_buf) { + dev_err(&p_ipc_spi->dev, "[%s] rx_buf kmalloc fail.", __func__); + + retval = -ENOMEM; + goto exit; + } + rx_header = (struct spi_protocol_header *)rx_buf; + + rx_save_buf = kmalloc(DEF_BUF_SIZE * 3, GFP_ATOMIC); + if (!rx_save_buf) { + dev_err(&p_ipc_spi->dev, "[%s] rx_save_buf kmalloc fail.", __func__); + + retval = -ENOMEM; + goto exit; + } + memset((void *)rx_save_buf, 0, DEF_BUF_SIZE * 3); + + test_data = kmalloc(DEF_BUF_SIZE + 4, GFP_ATOMIC); + if (!test_data) { + dev_err(&p_ipc_spi->dev, "[%s] test_data kmalloc fail.", __func__); + + retval = -ENOMEM; + goto exit; + } + memset((void *)test_data, 0, DEF_BUF_SIZE + 4); +#endif + + dev_dbg(&p_ipc_spi->dev, "(%d) wait 2 sec... srdy : %d\n", __LINE__, gpio_get_value(gpio_srdy)); + msleep(2000); + + while (gpio_get_value(gpio_srdy)) + ; + dev_dbg(&p_ipc_spi->dev, "(%d) cp booting... Done.\n", __LINE__); + + dev_dbg(&p_ipc_spi->dev, "(%d) wait 1 sec...\n", __LINE__); + msleep(1000); + + printk(KERN_ERR "[IPC_SPI] Start IPC Communication. MRDY : %d, SRDY : %d\n", gpio_get_value(gpio_mrdy), gpio_get_value(gpio_srdy)); + sema_init(&srdy_sem, 0); + + ipc_spi_irq_log_flag = 1; + +#ifndef LOOPBACK_TEST + spi_main(1, od); +#else + while (1) { + + if (!ipc_spi_check_send_data()) { /* no send data */ + if (down_trylock(&srdy_sem)) { /* no srdy sem */ + dev_dbg(&p_ipc_spi->dev, "(%d) no data and no sem, wait tx-srdy event.\n", __LINE__); + + transfer_thread_waiting = 1; + + skip_SRDY_chk = 0; + + down(&transfer_event_sem); /* wait event(tx or srdy) */ + dev_dbg(&p_ipc_spi->dev, "(%d) got tx-srdy event.\n", __LINE__); + } else { + dev_dbg(&p_ipc_spi->dev, "(%d) srdy_sem already exist\n", __LINE__); + + skip_SRDY_chk = 1; + clear_tx_buf = 1; + } + } else { + dev_dbg(&p_ipc_spi->dev, "(%d) send data exist\n", __LINE__); + } + + /* HERE : Got tx data event or Got SRDY isr event */ + if (gpio_get_value(gpio_mrdy)) { + dev_dbg(&p_ipc_spi->dev, "(%d) MRDY HIGH!!!\n", __LINE__); + + ipc_spi_set_MRDY_pin(0); + } + ipc_spi_set_MRDY_pin(1); + + do { + + if (clear_tx_buf) { + memset((void *)tx_buf, 0, DEF_BUF_SIZE + 4); + + clear_tx_buf = 0; + } else { + ipc_spi_prepare_tx_data(tx_buf); + } +#ifdef LOOPBACK_TEST + if (loop_back_test) { + if (tx_header->current_data_size == 0x3) { + /*C2 send*/ + } else { + memset((void *)test_data, tx_loopback_data, LOOP_SIZE-18); + ipc_spi_prepare_loopback_tx_data(tx_buf, rx_buf , test_data); + if (tx_loopback_data == 0x0A) { + loop_back_test = 1; + tx_loopback_data = 1; + } else + tx_loopback_data++; + } + } + skip_SRDY_chk = 1; /* AP Only Loopback */ +#endif + + if (!skip_SRDY_chk) { +RETRY_WAIT_SEM: + + dev_dbg(&p_ipc_spi->dev, "(%d) wait SRDY : %d\n", __LINE__, gpio_get_value(gpio_srdy)); + + if (down_timeout(&srdy_sem, 2 * HZ)) { + dev_err(&p_ipc_spi->dev, "(%d) SRDY TimeOUT!!! MRDY : %d, SRDY : %d\n", __LINE__, gpio_get_value(gpio_mrdy), gpio_get_value(gpio_srdy)); + + ipc_spi_set_MRDY_pin(0); + mdelay(10); + ipc_spi_set_MRDY_pin(1); + + dev_err(&p_ipc_spi->dev, "(%d) SRDY TimeOUT Reset!!! MRDY : %d, SRDY : %d\n", __LINE__, gpio_get_value(gpio_mrdy), gpio_get_value(gpio_srdy)); + + if (!gpio_get_value(gpio_srdy)) + goto RETRY_WAIT_SEM; + } + dev_dbg(&p_ipc_spi->dev, "(%d) got SRDY : %d\n", __LINE__, gpio_get_value(gpio_srdy)); + } else { + dev_dbg(&p_ipc_spi->dev, "(%d) skip wait SRDY.\n", __LINE__); + + skip_SRDY_chk = 0; + } + + ipc_spi_swap_data_htn(tx_buf, DEF_BUF_SIZE + 4); /* host to network */ + + retval = ipc_spi_tx_rx_sync(tx_buf, rx_buf, DEF_BUF_SIZE + 4); + if (retval != 0) + dev_err(&p_ipc_spi->dev, "(%d) spi sync error : %d\n", __LINE__, retval); + else +/* dev_dbg(&p_ipc_spi->dev, "(%d) transmit Done.\n", __LINE__); */ + + ipc_spi_swap_data_nth(rx_buf, DEF_BUF_SIZE + 4); /* network to host */ + *(u32 *)tx_header = ntohl(*(u32 *)tx_header); + +#ifdef LOOPBACK_TEST + printk(KERN_ERR "[SPI DUMP] LOOP RX :]"); + for (i = 0 ; i < 30 ; i++) + printk(KERN_ERR "%02x ", *((u8 *)(rx_buf + i))); + + printk(KERN_ERR "| "); + for (i = LOOP_SIZE - 30 ; i < LOOP_SIZE ; i++) + printk(KERN_ERR "%02x ", *((u8 *)(rx_buf + i))); + + printk(KERN_ERR "]\n"); +#else + dev_dbg(&p_ipc_spi->dev, "[SPI DUMP] RX : [%02x %02x %02x %02x | %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x]\n", + rx_buf[0], rx_buf[1], rx_buf[2], rx_buf[3], rx_buf[4], rx_buf[5], rx_buf[6], rx_buf[7], rx_buf[8], rx_buf[9], rx_buf[10], rx_buf[11], rx_buf[12], rx_buf[13], rx_buf[14], rx_buf[15], rx_buf[16], rx_buf[17], rx_buf[18], rx_buf[19], + rx_buf[20], rx_buf[21], rx_buf[22], rx_buf[23], rx_buf[24], rx_buf[25], rx_buf[26], rx_buf[27], rx_buf[28], rx_buf[29], rx_buf[30], rx_buf[31], rx_buf[32], rx_buf[33], rx_buf[34], rx_buf[35], rx_buf[36], rx_buf[37], rx_buf[38], rx_buf[39]); +#endif + +#ifdef LOOPBACK_TEST + /* Loopback test, IPC header[0A 0C 03] */ + if (rx_buf[14] == 0x0A && rx_buf[15] == 0x0B && rx_buf[16] == 0x01) { + rx_loopback_data = rx_buf[17]; + + for (i = 0; i < LOOP_SIZE - 18; i++) { + if (rx_buf[17+i] != rx_loopback_data) { + printk(KERN_ERR "[Loopback] %dth %02x != %02x Invaild data!\n", i, rx_loopback_data, rx_buf[17+i]); + goto exit; + } + } + if (i == (LOOP_SIZE-18)) + printk(KERN_ERR "[Loopback] %02x is OK!\n", rx_loopback_data); + + rx_loopback_count++; + if ((rx_loopback_count % 10) == 0 && rx_loopback_data == 0x0A) { + tx_loopback_data = 1; + loop_back_test = 1; + printk(KERN_ERR "[Loopback] rx_loopback_count = %d\n", rx_loopback_count); + } + rx_header->RTSCTS = 0; + rx_header->more = 0; + } +#else + if (*(u32 *)rx_header != 0x00000000 && *(u32 *)rx_header != 0xFFFFFFFF) { /* valid spi header */ +/* dev_dbg(&p_ipc_spi->dev, "(%d) got valid rx data.\n", __LINE__); */ + + /* RX process */ + ipc_spi_rx_process(rx_buf, rx_save_buf, od); + + if (rx_header->RTSCTS) { /* modem is not available. */ + dev_err(&p_ipc_spi->dev, "(%d) rx CTS set.\n", __LINE__); + + clear_tx_buf = 1; + } + + } else { +/* dev_dbg(&p_ipc_spi->dev, "(%d) got invalid rx data.\n", __LINE__); */ + + rx_header->RTSCTS = 0; + rx_header->more = 0; + } +#endif + dev_dbg(&p_ipc_spi->dev, "(%d) check more, CTS : %d, rx more : %d, tx more : %d\n", __LINE__, rx_header->RTSCTS, rx_header->more, tx_header->more); + msleep(2000); + } while (rx_header->RTSCTS || rx_header->more || tx_header->more); + + ipc_spi_set_MRDY_pin(0); + +/* dev_dbg(&p_ipc_spi->dev, "(%d) LOOP Done ============================\n", __LINE__); */ + } +#endif + +exit: + printk(KERN_ERR "(%d) thread stop.\n", __LINE__); + + return retval; +} + +/* Send SPRD main image through SPI */ +#if defined(CONFIG_CHN_CMCC_SPI_SPRD) +#define CP_VER_2 +/* #define SPRD_TRANSLATE_PACKET */ +#define SPRD_BLOCK_SIZE 32768 + +#ifdef CP_VER_2 +enum image_type { + MODEM_MAIN, + MODEM_DSP, + MODEM_NV, + MODEM_EFS, + MODEM_RUN, +}; +#else +enum image_type { + MODEM_KERNEL, + MODEM_USER, + MODEM_DSP, + MODEM_NV, + MODEM_RUN, +}; +#endif + +struct image_buf { + unsigned int length; + unsigned int offset; + unsigned int address; + unsigned char *buf; +}; + +struct sprd_image_buf { + u8 *tx_b; + u8 *rx_b; + u8 *encoded_tx_b; + u8 *decoded_rx_b; + + int tx_size; + int rx_size; + int encoded_tx_size; + int decoded_rx_size; +}; + +/* CRC */ +#define CRC_16_POLYNOMIAL 0x1021 +#define CRC_16_L_POLYNOMIAL 0x8408 +#define CRC_16_L_SEED 0xFFFF +#define CRC_TAB_SIZE 256 /* 2^CRC_TAB_BITS */ +#define CRC_16_L_OK 0x0 +#define HDLC_FLAG 0x7E +#define HDLC_ESCAPE 0x7D +#define HDLC_ESCAPE_MASK 0x20 +#define CRC_CHECK_SIZE 0x02 + +#define M_32_SWAP(a) { \ + u32 _tmp; \ + _tmp = a; \ + ((u8 *)&a)[0] = ((u8 *)&_tmp)[3]; \ + ((u8 *)&a)[1] = ((u8 *)&_tmp)[2]; \ + ((u8 *)&a)[2] = ((u8 *)&_tmp)[1]; \ + ((u8 *)&a)[3] = ((u8 *)&_tmp)[0]; \ + } + +#define M_16_SWAP(a) { \ + u16 _tmp; \ + _tmp = (u16)a; \ + ((u8 *)&a)[0] = ((u8 *)&_tmp)[1]; \ + ((u8 *)&a)[1] = ((u8 *)&_tmp)[0]; \ + } + +unsigned int sprd_crc_calc(char *buf_ptr, unsigned int len) +{ + unsigned int i; + unsigned short crc = 0; + + while (len-- != 0) { + for (i = 0x80; i != 0 ; i = i>>1) { + if ((crc & 0x8000) != 0) { + crc = crc << 1 ; + crc = crc ^ 0x1021; + } else { + crc = crc << 1 ; + } + + if ((*buf_ptr & i) != 0) + crc = crc ^ 0x1021; + } + buf_ptr++; + } + + return crc; + +} + +unsigned short sprd_crc_calc_fdl(unsigned short *src, int len) +{ + unsigned int sum = 0; + unsigned short SourceValue, DestValue; + unsigned short lowSourceValue, hiSourceValue; + + /* Get sum value of the source.*/ + while (len > 1) { + SourceValue = *src++; + DestValue = 0; + lowSourceValue = (SourceValue & 0xFF00) >> 8; + hiSourceValue = (SourceValue & 0x00FF) << 8; + DestValue = lowSourceValue | hiSourceValue; + + sum += DestValue; + len -= 2; + } + + if (len == 1) + sum += *((unsigned char *) src); + + sum = (sum >> 16) + (sum & 0x0FFFF); + sum += (sum >> 16); + + return ~sum; +} + +int encode_msg(struct sprd_image_buf *img, int bcrc) +{ + u16 crc; /* CRC value*/ + u8 *src_ptr; /* source buffer pointer*/ + int dest_len; /* output buffer length*/ + u8 *dest_ptr; /* dest buffer pointer*/ + u8 high_crc, low_crc; + register int curr; + + /* CRC Check. */ + src_ptr = img->tx_b; + + /* CRC Check. */ + if (bcrc) + crc = sprd_crc_calc(src_ptr, img->tx_size); + else + crc = sprd_crc_calc_fdl((unsigned short *)src_ptr, img->tx_size); + + high_crc = (crc>>8) & 0xFF; + low_crc = crc & 0xFF; + + /* Get the total size to be allocated.*/ + dest_len = 0; + + for (curr = 0; curr < img->tx_size; curr++) { + switch (*(src_ptr+curr)) { + case HDLC_FLAG: + case HDLC_ESCAPE: + dest_len += 2; + break; + default: + dest_len++; + break; + } + } + + switch (low_crc) { + case HDLC_FLAG: + case HDLC_ESCAPE: + dest_len += 2; + break; + default: + dest_len++; + } + + switch (high_crc) { + case HDLC_FLAG: + case HDLC_ESCAPE: + dest_len += 2; + break; + default: + dest_len++; + } + + dest_ptr = kmalloc(dest_len + 2, GFP_ATOMIC); + /* Memory Allocate fail.*/ + if (dest_ptr == NULL) + return 0; + + *dest_ptr = HDLC_FLAG; + dest_len = 1; + + /* do escape*/ + for (curr = 0; curr < img->tx_size; curr++) { + switch (*(src_ptr+curr)) { + case HDLC_FLAG: + case HDLC_ESCAPE: + *(dest_ptr + dest_len++) = HDLC_ESCAPE; + *(dest_ptr + dest_len++) = *(src_ptr + curr) ^ HDLC_ESCAPE_MASK; + break; + default: + *(dest_ptr + dest_len++) = *(src_ptr + curr); + break; + } + } + + switch (high_crc) { + case HDLC_FLAG: + case HDLC_ESCAPE: + *(dest_ptr + dest_len++) = HDLC_ESCAPE; + *(dest_ptr + dest_len++) = high_crc ^ HDLC_ESCAPE_MASK; + break; + default: + *(dest_ptr + dest_len++) = high_crc; + } + + switch (low_crc) { + case HDLC_FLAG: + case HDLC_ESCAPE: + *(dest_ptr + dest_len++) = HDLC_ESCAPE; + *(dest_ptr + dest_len++) = low_crc ^ HDLC_ESCAPE_MASK; + break; + default: + *(dest_ptr + dest_len++) = low_crc; + } + + + *(dest_ptr + dest_len++) = HDLC_FLAG; + + memcpy(img->encoded_tx_b, dest_ptr, dest_len); + img->encoded_tx_size = dest_len; +/* + LOGD("[In encode_msg dest_ptr]"); + for (i=0;i<dest_len;i++) + LOGD("0x%X", *(dest_ptr+i)); + + LOGD("[In encode_msg output_buf]"); + for (i=0;i<img->encoded_tx_size;i++) + LOGD("0x%X", *(img->encoded_tx_b+i)); +*/ + kfree(dest_ptr); + return 1; +} + +int decode_msg(struct sprd_image_buf *img, int bcrc) +{ + u16 crc; /* CRC value*/ + u8 *src_ptr; /* source buffer pointer*/ + int dest_len; /* output buffer length*/ + u8 *dest_ptr; /* dest buffer pointer*/ + register int curr; + + /* Check if exist End Flag.*/ + src_ptr = img->rx_b; + + dest_len = 0; + + if (img->rx_size < 4) + return -1; + + /* Get the total size to be allocated for decoded message.*/ + for (curr = 1; curr < img->rx_size - 1; curr++) { + switch (*(src_ptr + curr)) { + case HDLC_ESCAPE: + curr++; + dest_len++; + break; + default: + dest_len++; + break; + } + } + + /* Allocate meomory for decoded message*/ + dest_ptr = NULL; + dest_ptr = kmalloc(dest_len, GFP_ATOMIC); + /* Memory Free fail.*/ + if (dest_ptr == NULL) + return -2; + + memset(dest_ptr, 0, dest_len); + + curr = 0; + dest_len = 0; + /* Do de-escape.*/ + for (curr = 1; curr < img->rx_size - 1; curr++) { + switch (*(src_ptr + curr)) { + case HDLC_ESCAPE: + curr++; + *(dest_ptr + dest_len) = *(src_ptr + curr) ^ HDLC_ESCAPE_MASK; + break; + default: + *(dest_ptr + dest_len) = *(src_ptr + curr); + break; + } + + dest_len = dest_len + 1; + } + + /* CRC Check. */ + if (bcrc) + crc = sprd_crc_calc(dest_ptr, dest_len); + else + crc = sprd_crc_calc_fdl((unsigned short *)dest_ptr, dest_len); + + if (crc != CRC_16_L_OK) { + dev_err(&p_ipc_spi->dev, "CRC error : 0x%X", crc); + kfree(dest_ptr); + return -3; + } + + memcpy(img->decoded_rx_b, dest_ptr, dest_len - CRC_CHECK_SIZE); + img->decoded_rx_size = dest_len - CRC_CHECK_SIZE ; +/* + LOGD("[In decode_msg dest_ptr]"); + for (i=0;i<dest_len;i++) + LOGD("0x%X", *(dest_ptr+i)); + + LOGD("[In decode_msg output_buf]"); + for (i=0;i<*img->decoded_rx_size;i++) + LOGD("0x%X", *(img->decoded_rx_b+i)); +*/ + kfree(dest_ptr); + return 1; +} + +static int ipc_spi_send_modem_bin_execute_cmd(struct ipc_spi *od, u8 *spi_ptr, u32 spi_size, u16 spi_type, u16 spi_crc, struct sprd_image_buf *sprd_img) +{ + int retval; + u16 send_packet_size; + u8 *send_packet_data; + u16 d1_crc; + u16 d2_crc = spi_crc; + u16 type = spi_type; +#ifdef SPRD_TRANSLATE_PACKET + int i, cnt_7E = 0; + int bcrc = 0; +#endif + + /* D1 */ + send_packet_size = spi_size; /* u32 -> u16 */ +#ifdef SPRD_TRANSLATE_PACKET + sprd_img->tx_size = 6; + memcpy(sprd_img->tx_b, &send_packet_size, sizeof(send_packet_size)); + *(sprd_img->tx_b+2) = 0x00; *(sprd_img->tx_b+3) = 0x00; /* reserved 4 bytes */ + *(sprd_img->tx_b+4) = 0x00; *(sprd_img->tx_b+5) = 0x00; + + d1_crc = sprd_crc_calc_fdl((unsigned short *)sprd_img->tx_b, sprd_img->tx_size); + M_16_SWAP(d1_crc); + memcpy((sprd_img->tx_b+6), &d1_crc, sizeof(d1_crc)); + sprd_img->tx_size += 2; +#else + sprd_img->tx_size = 6; + M_16_SWAP(d2_crc); + memcpy(sprd_img->tx_b, &send_packet_size, sizeof(send_packet_size)); + memcpy((sprd_img->tx_b+2), &type, sizeof(type)); + memcpy((sprd_img->tx_b+4), &d2_crc, sizeof(d2_crc)); + + d1_crc = sprd_crc_calc_fdl((unsigned short *)sprd_img->tx_b, sprd_img->tx_size); + M_16_SWAP(d1_crc); + memcpy((sprd_img->tx_b+6), &d1_crc, sizeof(d1_crc)); + sprd_img->tx_size += 2; +#endif +/* + dev_dbg(&p_ipc_spi->dev, "[SPI DUMP] TX_D1(%d) : [%02x %02x %02x %02x %02x %02x %02x %02x]\n", sprd_img->tx_size, + sprd_img->tx_b[0], sprd_img->tx_b[1], sprd_img->tx_b[2], sprd_img->tx_b[3], sprd_img->tx_b[4], + sprd_img->tx_b[5], sprd_img->tx_b[6], sprd_img->tx_b[7]); +*/ + + if (down_timeout(&srdy_sem, 2 * HZ)) { + dev_err(&p_ipc_spi->dev, "(%d) SRDY TimeOUT!!! SRDY : %d, SEM : %d\n", __LINE__, gpio_get_value(gpio_srdy), srdy_sem.count); + return -1; + } + + retval = ipc_spi_tx_rx_sync(sprd_img->tx_b, sprd_img->rx_b, sprd_img->tx_size); + if (retval != 0) { + dev_err(&p_ipc_spi->dev, "(%d) spi sync error : %d\n", __LINE__, retval); + return -1; + } else { +/* dev_dbg(&p_ipc_spi->dev, "(%d) transmit Done.\n", __LINE__); */ + } +/* + dev_dbg(&p_ipc_spi->dev, "[SPI DUMP] TX_D2(%d) : [%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x ... %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x]\n", + send_packet_size, spi_ptr[0], spi_ptr[1], spi_ptr[2], spi_ptr[3], spi_ptr[4], + spi_ptr[5], spi_ptr[6], spi_ptr[7], spi_ptr[8], spi_ptr[9], + spi_ptr[10], spi_ptr[11], spi_ptr[12], spi_ptr[13], spi_ptr[14], + spi_ptr[15], spi_ptr[16], spi_ptr[17], spi_ptr[18], spi_ptr[19], + spi_ptr[send_packet_size - 10], spi_ptr[send_packet_size - 9], spi_ptr[send_packet_size - 8], spi_ptr[send_packet_size - 7], spi_ptr[send_packet_size - 6], + spi_ptr[send_packet_size - 5], spi_ptr[send_packet_size - 4], spi_ptr[send_packet_size - 3], spi_ptr[send_packet_size - 2], spi_ptr[send_packet_size - 1]); +*/ + if ((type == 0x0003) || (type == 0x0004)) { + printk(KERN_ERR "D2 Skip!!\n"); + goto ACK; + } + + /* D2 */ + send_packet_data = spi_ptr; + + if (down_timeout(&srdy_sem, 2 * HZ)) { + dev_err(&p_ipc_spi->dev, "(%d) SRDY TimeOUT!!! SRDY : %d, SEM : %d\n", __LINE__, gpio_get_value(gpio_srdy), srdy_sem.count); + return -1; + } + + retval = ipc_spi_tx_rx_sync(send_packet_data, sprd_img->rx_b, send_packet_size); + if (retval != 0) { + dev_err(&p_ipc_spi->dev, "(%d) spi sync error : %d\n", __LINE__, retval); + return -1; + } else { +/* dev_dbg(&p_ipc_spi->dev, "(%d) transmit Done.\n", __LINE__); */ + } + +ACK: + + if (is_cp_reset) { + while (!gpio_get_value(gpio_srdy)) + ; + } else { + if (down_timeout(&srdy_sem, 2 * HZ)) { + dev_err(&p_ipc_spi->dev, "(%d) SRDY TimeOUT!!! SRDY : %d, SEM : %d\n", __LINE__, gpio_get_value(gpio_srdy), srdy_sem.count); + dev_err(&p_ipc_spi->dev, "[SPI DUMP] TX_D1(%d) : [%02x %02x %02x %02x %02x %02x %02x %02x]\n", sprd_img->tx_size, + sprd_img->tx_b[0], sprd_img->tx_b[1], sprd_img->tx_b[2], sprd_img->tx_b[3], sprd_img->tx_b[4], + sprd_img->tx_b[5], sprd_img->tx_b[6], sprd_img->tx_b[7]); + dev_err(&p_ipc_spi->dev, "[SPI DUMP] TX_D2(%d) : [%02x %02x %02x %02x %02x %02x %02x %02x]\n", + send_packet_size, spi_ptr[0], spi_ptr[1], spi_ptr[2], spi_ptr[3], spi_ptr[4], + spi_ptr[5], spi_ptr[6], spi_ptr[7]); + + /* WA (CP Reset) jongmoon.suh */ + if (gpio_get_value(gpio_srdy)) + ; + else + return -1; + + } + } + + memset(sprd_img->tx_b, 0, SPRD_BLOCK_SIZE+10); + retval = ipc_spi_tx_rx_sync(sprd_img->tx_b, sprd_img->rx_b, 8); + if (retval != 0) { + dev_err(&p_ipc_spi->dev, "(%d) spi sync error : %d\n", __LINE__, retval); + return -1; + } else { +/* dev_dbg(&p_ipc_spi->dev, "(%d) transmit Done.\n", __LINE__); */ + } +#ifdef SPRD_TRANSLATE_PACKET + for (i = 0; i < 8; i++) { + if (*(sprd_img->rx_b+i) == 0x7E) + cnt_7E++; + if (cnt_7E == 2) + break; + } + sprd_img->rx_size = i + 1; +/* + dev_dbg(&p_ipc_spi->dev, "[SPI DUMP] RX(%d) : [%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x]\n", sprd_img->rx_size, + sprd_img->rx_b[0], sprd_img->rx_b[1], sprd_img->rx_b[2], sprd_img->rx_b[3], sprd_img->rx_b[4], sprd_img->rx_b[5], sprd_img->rx_b[6], sprd_img->rx_b[7], + sprd_img->rx_b[8], sprd_img->rx_b[9], sprd_img->rx_b[10], sprd_img->rx_b[11], sprd_img->rx_b[12], sprd_img->rx_b[13], sprd_img->rx_b[14], sprd_img->rx_b[15]); +*/ + if (sprd_img->rx_size != 8) { + dev_err(&p_ipc_spi->dev, "ACK size error!, %d\n", sprd_img->rx_size); + dev_err(&p_ipc_spi->dev, "[SPI DUMP] RX(%d) : [%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x]\n", sprd_img->rx_size, + sprd_img->rx_b[0], sprd_img->rx_b[1], sprd_img->rx_b[2], sprd_img->rx_b[3], sprd_img->rx_b[4], sprd_img->rx_b[5], sprd_img->rx_b[6], sprd_img->rx_b[7], + sprd_img->rx_b[8], sprd_img->rx_b[9], sprd_img->rx_b[10], sprd_img->rx_b[11], sprd_img->rx_b[12], sprd_img->rx_b[13], sprd_img->rx_b[14], sprd_img->rx_b[15]); + + return -1; + } + retval = decode_msg(sprd_img, bcrc); + if (retval != 1) { + dev_err(&p_ipc_spi->dev, "decode_msg(ACK) error!\n"); + return -1; + } +#else + memcpy(sprd_img->decoded_rx_b, sprd_img->rx_b, 4); +#endif + if ((*(sprd_img->decoded_rx_b+0) == 0x00) && (*(sprd_img->decoded_rx_b+1) == 0x80) && + (*(sprd_img->decoded_rx_b+2) == 0x00) && (*(sprd_img->decoded_rx_b+3) == 0x00)) { + /* dev_dbg(&p_ipc_spi->dev, "[SPRD] CP sent ACK"); */ + } else { + dev_err(&p_ipc_spi->dev, "Transfer ACK error!srdy_sem = %d\n", srdy_sem.count); + dev_err(&p_ipc_spi->dev, "[SPI DUMP] RX : [%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x]\n", + sprd_img->rx_b[0], sprd_img->rx_b[1], sprd_img->rx_b[2], sprd_img->rx_b[3], sprd_img->rx_b[4], sprd_img->rx_b[5], sprd_img->rx_b[6], sprd_img->rx_b[7], + sprd_img->rx_b[8], sprd_img->rx_b[9], sprd_img->rx_b[10], sprd_img->rx_b[11], sprd_img->rx_b[12], sprd_img->rx_b[13], sprd_img->rx_b[14], sprd_img->rx_b[15]); + + return -1; + } + + return retval; + +} + +static int ipc_spi_send_modem_bin_xmit_img(struct ipc_spi *od, enum image_type type, struct image_buf *img) +{ + int retval = 0; + struct sprd_image_buf sprd_img; + unsigned int data_size; + unsigned int send_size = 0; + unsigned int rest_size = 0; + unsigned int spi_size = 0; + unsigned int address; + unsigned int fdl1_size; + /* No Translate */ + u16 crc = 0; + u16 spi_type = 0; + + unsigned char *spi_ptr; + unsigned char *ptr; + int i, j; +#ifdef SPRD_TRANSLATE_PACKET + int bcrc = 0; +#endif + u16 sprd_packet_size = SPRD_BLOCK_SIZE; + + sprd_img.tx_b = kmalloc(SPRD_BLOCK_SIZE*2, GFP_ATOMIC); + if (!sprd_img.tx_b) { + dev_err(&p_ipc_spi->dev, "(%d) tx_b kmalloc fail.", __LINE__); + return -1; + } + memset(sprd_img.tx_b, 0, SPRD_BLOCK_SIZE*2); + + sprd_img.rx_b = kmalloc(SPRD_BLOCK_SIZE*2, GFP_ATOMIC); + if (!sprd_img.rx_b) { + dev_err(&p_ipc_spi->dev, "(%d) rx_b kmalloc fail.", __LINE__); + return -1; + } + memset(sprd_img.rx_b, 0, SPRD_BLOCK_SIZE*2); + + sprd_img.encoded_tx_b = kmalloc(SPRD_BLOCK_SIZE*2, GFP_ATOMIC); + if (!sprd_img.encoded_tx_b) { + dev_err(&p_ipc_spi->dev, "(%d) encoded_tx_b kmalloc fail.", __LINE__); + return -1; + } + memset(sprd_img.encoded_tx_b, 0, SPRD_BLOCK_SIZE*2); + + sprd_img.decoded_rx_b = kmalloc(SPRD_BLOCK_SIZE*2, GFP_ATOMIC); + if (!sprd_img.decoded_rx_b) { + dev_err(&p_ipc_spi->dev, "(%d) encoded_rx_b kmalloc fail.", __LINE__); + return -1; + } + memset(sprd_img.decoded_rx_b, 0, SPRD_BLOCK_SIZE*2); + + dev_dbg(&p_ipc_spi->dev, "(%d) ipc_spi_send_modem_bin_xmit_img type : %d.\n", __LINE__, type); + memcpy(&fdl1_size, (void *)(p_virtual_buff + 4), 4); + + switch (type) { +#ifdef CP_VER_2 + case MODEM_MAIN: + memcpy(&img->address, (void *)(p_virtual_buff + 8), 4); + memcpy(&img->length , (void *)(p_virtual_buff + 12), 4); + img->buf = (unsigned char *)(p_virtual_buff + 0x30 + fdl1_size); + img->offset = img->length + fdl1_size + 0x30; + dev_dbg(&p_ipc_spi->dev, "(%d) ipc_spi_send_modem_bin_xmit_img save MAIN to img.\n", __LINE__); + + break; + + case MODEM_DSP: + memcpy(&img->address, (void *)(p_virtual_buff + 16), 4); + memcpy(&img->length , (void *)(p_virtual_buff + 20), 4); + img->buf = (unsigned char *)(p_virtual_buff + img->offset); + img->offset += img->length; + dev_dbg(&p_ipc_spi->dev, "(%d) ipc_spi_send_modem_bin_xmit_img save DSP to img.\n", __LINE__); + + break; + + case MODEM_NV: + memcpy(&img->address, (void *)(p_virtual_buff + 24), 4); + memcpy(&img->length , (void *)(p_virtual_buff + 28), 4); + img->buf = (unsigned char *)(p_virtual_buff + img->offset); + img->offset += img->length; + dev_dbg(&p_ipc_spi->dev, "(%d) ipc_spi_send_modem_bin_xmit_img save NV to img.\n", __LINE__); + + break; + + case MODEM_EFS: + memcpy(&img->address, (void *)(p_virtual_buff + 32), 4); + memcpy(&img->length , (void *)(p_virtual_buff + 36), 4); + img->buf = (unsigned char *)(p_virtual_buff + img->offset); + img->offset += img->length; + dev_dbg(&p_ipc_spi->dev, "(%d) ipc_spi_send_modem_bin_xmit_img save EFS to img.\n", __LINE__); + + break; +#else + case MODEM_KERNEL: + memcpy(&img->address, (void *)(p_virtual_buff + 8), 4); + memcpy(&img->length , (void *)(p_virtual_buff + 12), 4); + img->buf = (unsigned char *)(p_virtual_buff + 0x30 + fdl1_size); + img->offset = img->length + fdl1_size + 0x30; + dev_dbg(&p_ipc_spi->dev, "(%d) ipc_spi_send_modem_bin_xmit_img save KERNEL to img.\n", __LINE__); + + break; + + case MODEM_USER: + memcpy(&img->address, (void *)(p_virtual_buff + 16), 4); + memcpy(&img->length , (void *)(p_virtual_buff + 20), 4); + img->buf = (unsigned char *)(p_virtual_buff + img->offset); + img->offset += img->length; + dev_dbg(&p_ipc_spi->dev, "(%d) ipc_spi_send_modem_bin_xmit_img save USER to img.\n", __LINE__); + + break; + + case MODEM_DSP: + memcpy(&img->address, (void *)(p_virtual_buff + 24), 4); + memcpy(&img->length , (void *)(p_virtual_buff + 28), 4); + img->buf = (unsigned char *)(p_virtual_buff + img->offset); + img->offset += img->length; + dev_dbg(&p_ipc_spi->dev, "(%d) ipc_spi_send_modem_bin_xmit_img save DSP to img.\n", __LINE__); + + break; + + case MODEM_NV: + memcpy(&img->address, (void *)(p_virtual_buff + 32), 4); + memcpy(&img->length , (void *)(p_virtual_buff + 36), 4); + img->buf = (unsigned char *)(p_virtual_buff + img->offset); + img->offset += img->length; + dev_dbg(&p_ipc_spi->dev, "(%d) ipc_spi_send_modem_bin_xmit_img save NV to img.\n", __LINE__); + + break; +#endif + case MODEM_RUN: +#ifdef SPRD_TRANSLATE_PACKET + sprd_img.tx_size = 4; + *(sprd_img.tx_b+0) = 0x00; *(sprd_img.tx_b+1) = 0x04; + *(sprd_img.tx_b+2) = 0x00; *(sprd_img.tx_b+3) = 0x00; + retval = encode_msg(&sprd_img, bcrc); + if (retval != 1) { + dev_err(&p_ipc_spi->dev, "encode_msg(Transfer Start) error!"); + return -1; + } +#else + memset(sprd_img.encoded_tx_b, 0, SPRD_BLOCK_SIZE*2); + sprd_img.encoded_tx_size = 0; + spi_type = 0x0004; + crc = 0; +#endif + spi_ptr = sprd_img.encoded_tx_b; + spi_size = sprd_img.encoded_tx_size; + + retval = ipc_spi_send_modem_bin_execute_cmd(od, spi_ptr, spi_size, spi_type, crc, &sprd_img); + if (retval < 0) { + dev_err(&p_ipc_spi->dev, "(%d) ipc_spi_send_modem_bin_execute_cmd fail : %d", __LINE__, retval); + return -1; + } + return retval; + + default: + dev_err(&p_ipc_spi->dev, "(%d) ipc_spi_send_modem_bin_xmit_img wrong : %d.", __LINE__, type); + return -1; + } + + dev_dbg(&p_ipc_spi->dev, "(%d) Start send img. size : %d\n", __LINE__, img->length); + + ptr = img->buf; + data_size = sprd_packet_size; + rest_size = img->length; + address = img->address; + + M_32_SWAP(img->address); + M_32_SWAP(img->length); + + /* Send Transfer Start */ +#ifdef SPRD_TRANSLATE_PACKET + sprd_img.tx_size = 12; + *(sprd_img.tx_b+0) = 0x00; *(sprd_img.tx_b+1) = 0x01; + *(sprd_img.tx_b+2) = 0x00; *(sprd_img.tx_b+3) = 0x08; + memcpy((sprd_img.tx_b+4), &img->address, sizeof(img->address)); + memcpy((sprd_img.tx_b+8), &img->length, sizeof(img->length)); + + retval = encode_msg(&sprd_img, bcrc); + if (retval != 1) { + dev_err(&p_ipc_spi->dev, "encode_msg(Transfer Start) error!"); + return -1; + } +#else + sprd_img.tx_size = 8; + memcpy((sprd_img.tx_b+0), &img->address, sizeof(img->address)); + memcpy((sprd_img.tx_b+4), &img->length, sizeof(img->length)); + + spi_type = 0x0001; + crc = sprd_crc_calc_fdl((unsigned short *)sprd_img.tx_b, sprd_img.tx_size); + memcpy(sprd_img.encoded_tx_b, sprd_img.tx_b, sprd_img.tx_size); + sprd_img.encoded_tx_size = sprd_img.tx_size; +#endif + + spi_ptr = sprd_img.encoded_tx_b; + spi_size = sprd_img.encoded_tx_size; + + dev_dbg(&p_ipc_spi->dev, "(%d) [Transfer Start, Type = %d, Packet = %d]\n", __LINE__, type, sprd_packet_size); + retval = ipc_spi_send_modem_bin_execute_cmd(od, spi_ptr, spi_size, spi_type, crc, &sprd_img); + if (retval < 0) { + dev_err(&p_ipc_spi->dev, "(%d) ipc_spi_send_modem_bin_execute_cmd fail : %d", __LINE__, retval); + return -1; + } + M_32_SWAP(img->length); + + /* Send Data */ + for (i = 0; send_size < img->length; i++) { + if (rest_size < sprd_packet_size) + data_size = rest_size; +#ifdef SPRD_TRANSLATE_PACKET + sprd_img.tx_size = sprd_packet_size + 4; + *(sprd_img.tx_b+0) = 0x00; *(sprd_img.tx_b+1) = 0x02; /* type */ + M_16_SWAP(sprd_packet_size); + memcpy((sprd_img.tx_b + 2), &sprd_packet_size, sizeof(sprd_packet_size)); + M_16_SWAP(sprd_packet_size); + + for (j = 0; j < data_size; j++) + *(sprd_img.tx_b+4+j) = *(ptr + j); + + retval = encode_msg(&sprd_img, bcrc); + if (retval != 1) { + dev_err(&p_ipc_spi->dev, "encode_msg(TransferData_%d) error!", i); + return -1; + } +#else + sprd_img.encoded_tx_size = sprd_packet_size; + for (j = 0; j < data_size; j++) + *(sprd_img.encoded_tx_b+j) = *(ptr + j); + + spi_type = 0x0002; + crc = sprd_crc_calc_fdl((unsigned short *)sprd_img.encoded_tx_b, sprd_img.encoded_tx_size); +#endif + spi_ptr = sprd_img.encoded_tx_b; + spi_size = sprd_img.encoded_tx_size; + + retval = ipc_spi_send_modem_bin_execute_cmd(od, spi_ptr, spi_size, spi_type, crc, &sprd_img); + if (retval < 0) { + dev_err(&p_ipc_spi->dev, "(%d) ipc_spi_send_modem_bin_execute_cmd fail : %d, %d", __LINE__, retval, i); + return -1; + } + + send_size += data_size; + rest_size -= data_size; + ptr += data_size; + + if (!(i % 100)) + dev_dbg(&p_ipc_spi->dev, "(%d) [%d] 0x%x size done, rest size: 0x%x\n", __LINE__, i, send_size, rest_size); + } + + /* Send Transfer End */ +#ifdef SPRD_TRANSLATE_PACKET + sprd_img.tx_size = 4; + *(sprd_img.tx_b+0) = 0x00; *(sprd_img.tx_b+1) = 0x03; + *(sprd_img.tx_b+2) = 0x00; *(sprd_img.tx_b+3) = 0x00; + retval = encode_msg(&sprd_img, bcrc); + if (retval != 1) { + dev_err(&p_ipc_spi->dev, "encode_msg(TransferEnd) error!"); + return -1; + } +#else + memset(sprd_img.encoded_tx_b, 0, SPRD_BLOCK_SIZE * 2); + sprd_img.encoded_tx_size = 0; + + spi_type = 0x0003; + crc = 0; +#endif + spi_ptr = sprd_img.encoded_tx_b; + spi_size = sprd_img.encoded_tx_size; + + dev_dbg(&p_ipc_spi->dev, "(%d) [Transfer END]\n", __LINE__); + retval = ipc_spi_send_modem_bin_execute_cmd(od, spi_ptr, spi_size, spi_type, crc, &sprd_img); + if (retval < 0) { + dev_err(&p_ipc_spi->dev, "(%d) ipc_spi_send_modem_bin_execute_cmd fail : %d", __LINE__, retval); + return -1; + } + + kfree(sprd_img.tx_b); + kfree(sprd_img.rx_b); + kfree(sprd_img.encoded_tx_b); + kfree(sprd_img.decoded_rx_b); + + return retval; + +} + +static void ipc_spi_send_modem_bin(struct work_struct *send_modem_w) +{ + int retval = 0; + u32 int_cmd = 0xABCDABCD; + u32 int_cmd_fail = 0xDCBADCBA; + struct image_buf img; + unsigned long tick1, tick2; + + struct ipc_spi_send_modem_bin_workq_data *smw + = container_of(send_modem_w, struct ipc_spi_send_modem_bin_workq_data, send_modem_w); + struct ipc_spi *od = smw->od; + + tick1 = jiffies_to_msecs(jiffies); + + dev_dbg(&p_ipc_spi->dev, "[SPI DUMP] p_virtual_buff : [%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x]\n", + *(u8 *)(p_virtual_buff), *(u8 *)(p_virtual_buff + 1), *(u8 *)(p_virtual_buff + 2), *(u8 *)(p_virtual_buff + 3), *(u8 *)(p_virtual_buff + 4), + *(u8 *)(p_virtual_buff + 5), *(u8 *)(p_virtual_buff + 6), *(u8 *)(p_virtual_buff + 7), *(u8 *)(p_virtual_buff + 8), *(u8 *)(p_virtual_buff + 9), + *(u8 *)(p_virtual_buff + 10), *(u8 *)(p_virtual_buff + 11), *(u8 *)(p_virtual_buff + 12), *(u8 *)(p_virtual_buff + 13), *(u8 *)(p_virtual_buff + 14), + *(u8 *)(p_virtual_buff + 15), *(u8 *)(p_virtual_buff + 16), *(u8 *)(p_virtual_buff + 17), *(u8 *)(p_virtual_buff + 18), *(u8 *)(p_virtual_buff + 19)); +#ifdef CP_VER_2 + retval = ipc_spi_send_modem_bin_xmit_img(od, MODEM_MAIN, &img); + if (retval < 0) { + dev_err(&p_ipc_spi->dev, "(%d) ipc_spi_send_modem_bin_xmit_img fail : %d", __LINE__, retval); + goto err; + } +#else + retval = ipc_spi_send_modem_bin_xmit_img(od, MODEM_KERNEL, &img); + if (retval < 0) { + dev_err(&p_ipc_spi->dev, "(%d) ipc_spi_send_modem_bin_xmit_img fail : %d", __LINE__, retval); + goto err; + } + + retval = ipc_spi_send_modem_bin_xmit_img(od, MODEM_USER, &img); + if (retval < 0) { + dev_err(&p_ipc_spi->dev, "(%d) ipc_spi_send_modem_bin_xmit_img fail : %d", __LINE__, retval); + goto err; + } +#endif + retval = ipc_spi_send_modem_bin_xmit_img(od, MODEM_DSP, &img); + if (retval < 0) { + dev_err(&p_ipc_spi->dev, "(%d) ipc_spi_send_modem_bin_xmit_img fail : %d", __LINE__, retval); + goto err; + } + + retval = ipc_spi_send_modem_bin_xmit_img(od, MODEM_NV, &img); + if (retval < 0) { + dev_err(&p_ipc_spi->dev, "(%d) ipc_spi_send_modem_bin_xmit_img fail : %d", __LINE__, retval); + goto err; + } +#ifdef CP_VER_2 + retval = ipc_spi_send_modem_bin_xmit_img(od, MODEM_EFS, &img); + if (retval < 0) { + dev_err(&p_ipc_spi->dev, "(%d) ipc_spi_send_modem_bin_xmit_img fail : %d", __LINE__, retval); + goto err; + } +#endif + retval = ipc_spi_send_modem_bin_xmit_img(od, MODEM_RUN, &img); + if (retval < 0) { + dev_err(&p_ipc_spi->dev, "(%d) ipc_spi_send_modem_bin_xmit_img fail : %d", __LINE__, retval); + goto err; + } + + send_modem_spi = 0; + is_cp_reset = 0; + tick2 = jiffies_to_msecs(jiffies); + dev_dbg(&p_ipc_spi->dev, "Downloading takes %lu msec\n", (tick2-tick1)); + + /* make data interrupt cmd */ + ipc_spi_make_data_interrupt(int_cmd, od); + + return; + +err: + /* make data interrupt cmd */ + ipc_spi_make_data_interrupt(int_cmd_fail, od); + +} + +#else /*EUR */ +struct ipc_spi_send_modem_bin_header { + u16 sot; + u16 type; + u16 length; +}; + +struct ipc_spi_send_modem_bin_footer { + u16 crc; + u16 eot; +}; + + +static u16 ipc_spi_send_modem_bin_make_crc(u8 *buf) +{ + u16 crc = 0; + int i; + struct ipc_spi_send_modem_bin_header *header = (struct ipc_spi_send_modem_bin_header *)(buf + 4); + + crc += header->type; + crc += header->length; + + buf += 4; + buf += sizeof(struct ipc_spi_send_modem_bin_header); + for (i = 0 ; i < header->length ; i++) + crc += *buf++; + + return crc; +} + +#define EBL_PACKET_SIZE 4096 +static int ipc_spi_send_modem_bin_execute_cmd(struct ipc_spi *od, u16 type, u32 len, void *data) +{ + int retval = 0; + u8 *tx_b = NULL; + u8 *rx_b = NULL; + struct spi_protocol_header *tx_spi_header = NULL; + struct spi_protocol_header *rx_spi_header = NULL; + struct ipc_spi_send_modem_bin_header *tx_header = NULL; + struct ipc_spi_send_modem_bin_footer *tx_footer = NULL; + struct ipc_spi_send_modem_bin_header *rx_header = NULL; + struct ipc_spi_send_modem_bin_footer *rx_footer = NULL; + + tx_b = kmalloc(EBL_PACKET_SIZE, GFP_ATOMIC); + if (!tx_b) { + dev_err(od->dev, "(%d) tx_b kmalloc fail.", __LINE__); + return -ENOMEM; + } + tx_spi_header = (struct spi_protocol_header *)tx_b; + tx_header = (struct ipc_spi_send_modem_bin_header *)(tx_b + 4); + memset(tx_b, 0, EBL_PACKET_SIZE); + + rx_b = kmalloc(EBL_PACKET_SIZE, GFP_ATOMIC); + if (!rx_b) { + dev_err(od->dev, "(%d) rx_b kmalloc fail.", __LINE__); + return -ENOMEM; + } + rx_spi_header = (struct spi_protocol_header *)rx_b; + rx_header = (struct ipc_spi_send_modem_bin_header *)(rx_b + 4); + memset(rx_b, 0, EBL_PACKET_SIZE); + + ipc_spi_set_MRDY_pin(1); + + tx_spi_header->next_data_size = (EBL_PACKET_SIZE - 4) >> 2; + tx_spi_header->current_data_size = sizeof(struct ipc_spi_send_modem_bin_header) + len + sizeof(struct ipc_spi_send_modem_bin_footer); + + tx_header->sot = 0x0002; + tx_header->type = type; + tx_header->length = len; + + memcpy((void *)(tx_b + sizeof(struct ipc_spi_send_modem_bin_header) + 4), data, len); + + tx_footer = (struct ipc_spi_send_modem_bin_footer *)(tx_b + 4 + sizeof(struct ipc_spi_send_modem_bin_header) + len); + + tx_footer->crc = ipc_spi_send_modem_bin_make_crc(tx_b); + + tx_footer->eot = 0x0003; +/* + dev_dbg(od->dev, "[SPI DUMP] tx : [%02x %02x %02x %02x | %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x ... %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x]\n", + tx_b[0], tx_b[1], tx_b[2], tx_b[3], tx_b[4], + tx_b[5], tx_b[6], tx_b[7], tx_b[8], tx_b[9], + tx_b[10], tx_b[11], tx_b[12], tx_b[13], tx_b[14], + tx_b[15], tx_b[16], tx_b[17], tx_b[18], tx_b[19], + tx_b[EBL_PACKET_SIZE - 10], tx_b[EBL_PACKET_SIZE - 9], tx_b[EBL_PACKET_SIZE - 8], tx_b[EBL_PACKET_SIZE - 7], tx_b[EBL_PACKET_SIZE - 6], + tx_b[EBL_PACKET_SIZE - 5], tx_b[EBL_PACKET_SIZE - 4], tx_b[EBL_PACKET_SIZE - 3], tx_b[EBL_PACKET_SIZE - 2], tx_b[EBL_PACKET_SIZE - 1]); +*/ + ipc_spi_swap_data_htn(tx_b, EBL_PACKET_SIZE); + + down(&srdy_sem); + + retval = ipc_spi_tx_rx_sync(tx_b, rx_b, EBL_PACKET_SIZE); + if (retval != 0) + dev_err(od->dev, "(%d) spi sync error : %d\n", __LINE__, retval); + else + ;/*dev_dbg(od->dev, "(%d) transmit Done.\n", __LINE__); */ + +/* + dev_dbg(od->dev, "[SPI DUMP] rx : [%02x %02x %02x %02x | %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x ... %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x]\n", + rx_b[0], rx_b[1], rx_b[2], rx_b[3], rx_b[4], + rx_b[5], rx_b[6], rx_b[7], rx_b[8], rx_b[9], + rx_b[10], rx_b[11], rx_b[12], rx_b[13], rx_b[14], + rx_b[15], rx_b[16], rx_b[17], rx_b[18], rx_b[19], + rx_b[EBL_PACKET_SIZE - 10], rx_b[EBL_PACKET_SIZE - 9], rx_b[EBL_PACKET_SIZE - 8], rx_b[EBL_PACKET_SIZE - 7], rx_b[EBL_PACKET_SIZE - 6], + rx_b[EBL_PACKET_SIZE - 5], rx_b[EBL_PACKET_SIZE - 4], rx_b[EBL_PACKET_SIZE - 3], rx_b[EBL_PACKET_SIZE - 2], rx_b[EBL_PACKET_SIZE - 1]); +*/ + + if (type == 0x0208) /* ReqForceHwReset */ + return 0; + + memset(tx_b, 0, EBL_PACKET_SIZE); + memset(rx_b, 0, EBL_PACKET_SIZE); + + down(&srdy_sem); + + retval = ipc_spi_tx_rx_sync(tx_b, rx_b, EBL_PACKET_SIZE); + if (retval != 0) + dev_err(od->dev, "(%d) spi sync error : %d\n", __LINE__, retval); + else + ;/*dev_dbg(od->dev, "(%d) transmit Done.\n", __LINE__); */ + + ipc_spi_swap_data_nth(rx_b, EBL_PACKET_SIZE); +/* + dev_dbg(od->dev, "[SPI DUMP] rx : [%02x %02x %02x %02x | %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x ... %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x]\n", + rx_b[0], rx_b[1], rx_b[2], rx_b[3], rx_b[4], + rx_b[5], rx_b[6], rx_b[7], rx_b[8], rx_b[9], + rx_b[10], rx_b[11], rx_b[12], rx_b[13], rx_b[14], + rx_b[15], rx_b[16], rx_b[17], rx_b[18], rx_b[19], + rx_b[EBL_PACKET_SIZE - 10], rx_b[EBL_PACKET_SIZE - 9], rx_b[EBL_PACKET_SIZE - 8], rx_b[EBL_PACKET_SIZE - 7], rx_b[EBL_PACKET_SIZE - 6], + rx_b[EBL_PACKET_SIZE - 5], rx_b[EBL_PACKET_SIZE - 4], rx_b[EBL_PACKET_SIZE - 3], rx_b[EBL_PACKET_SIZE - 2], rx_b[EBL_PACKET_SIZE - 1]); +*/ + if (rx_header->type != type) { + dev_err(od->dev, "(%d) execute cmd ack error : 0x%x(0x%x)\n", __LINE__, rx_header->type, type); + + dev_dbg(od->dev, "[SPI DUMP] rx : [%02x %02x %02x %02x | %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x ... %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x]\n", + rx_b[0], rx_b[1], rx_b[2], rx_b[3], rx_b[4], + rx_b[5], rx_b[6], rx_b[7], rx_b[8], rx_b[9], + rx_b[10], rx_b[11], rx_b[12], rx_b[13], rx_b[14], + rx_b[15], rx_b[16], rx_b[17], rx_b[18], rx_b[19], + rx_b[EBL_PACKET_SIZE - 10], rx_b[EBL_PACKET_SIZE - 9], rx_b[EBL_PACKET_SIZE - 8], rx_b[EBL_PACKET_SIZE - 7], rx_b[EBL_PACKET_SIZE - 6], + rx_b[EBL_PACKET_SIZE - 5], rx_b[EBL_PACKET_SIZE - 4], rx_b[EBL_PACKET_SIZE - 3], rx_b[EBL_PACKET_SIZE - 2], rx_b[EBL_PACKET_SIZE - 1]); + + retval = -1; + } else { +/* dev_dbg(od->dev, "(%d) execute cmd ack Done.\n", __LINE__); */ + } + + ipc_spi_set_MRDY_pin(0); + + return retval; +} + +enum image_type { + MODEM_PSI, + MODEM_EBL, + MODEM_MAIN, + MODEM_NV, +}; + +struct image_buf { + unsigned int length; + unsigned char *buf; +}; + +#define PSI_OFFSET 0 +#define EBL_OFFSET 0x10000 +#define MAIN_OFFSET 0x28000 +#define NV_OFFSET 0xA00000 + +#define PSI_LEN (EBL_OFFSET - PSI_OFFSET - 1) +#define EBL_LEN (MAIN_OFFSET - EBL_OFFSET) +#define MAIN_LEN (NV_OFFSET - MAIN_OFFSET) +#define NV_LEN (2 * 1024 * 1024) + +#define MAIN_OFFSET_VM 0 +#define NV_OFFSET_VM 0xD80000 + +#define SPI_SEND_BLOCK_SIZE 4080 + +static int ipc_spi_send_modem_bin_xmit_img(struct ipc_spi *od, enum image_type type, unsigned int *address) +{ + int retval = 0; + struct image_buf img; + unsigned int data_size; + unsigned int send_size = 0; + unsigned int rest_size = 0; + unsigned char *ptr; + int i; + + dev_dbg(od->dev, "(%d) ipc_spi_send_modem_bin_xmit_img type : %d.\n", __LINE__, type); + switch (type) { + case MODEM_MAIN: + img.buf = (unsigned char *)(p_virtual_buff + MAIN_OFFSET_VM); + img.length = MAIN_LEN; + dev_dbg(od->dev, "(%d) ipc_spi_send_modem_bin_xmit_img save MAIN to img.\n", __LINE__); + + break; + + case MODEM_NV: + img.buf = (unsigned char *)(p_virtual_buff + NV_OFFSET_VM); + img.length = NV_LEN; + dev_dbg(od->dev, "(%d) ipc_spi_send_modem_bin_xmit_img save NV to img.\n", __LINE__); + + break; + + default: + dev_err(od->dev, "(%d) ipc_spi_send_modem_bin_xmit_img wrong : %d.", __LINE__, type); + return -1; + } + + /* Command : ReqFlashSetAddress(0x0802) */ + retval = ipc_spi_send_modem_bin_execute_cmd(od, 0x0802, sizeof(unsigned int), address); + if (retval < 0) { + dev_err(od->dev, "(%d) ipc_spi_send_modem_bin_execute_cmd fail : %d", __LINE__, retval); + return -1; + } + dev_dbg(od->dev, "(%d) ipc_spi_send_modem_bin_execute_cmd Done.\n", __LINE__); + + dev_dbg(od->dev, "(%d) Start send img. size : %d\n", __LINE__, img.length); + ptr = img.buf; + data_size = SPI_SEND_BLOCK_SIZE; + rest_size = img.length; + + for (i = 0 ; send_size < img.length ; i++) { + if (rest_size < SPI_SEND_BLOCK_SIZE) + data_size = rest_size; + + /* Command : ReqFlashWriteBlock(0x0804) */ + retval = ipc_spi_send_modem_bin_execute_cmd(od, 0x0804, data_size, ptr); + if (retval < 0) { + dev_err(od->dev, "(%d) ipc_spi_send_modem_bin_execute_cmd fail : %d", __LINE__, retval); + return -1; + } + + send_size += data_size; + rest_size -= data_size; + ptr += data_size; + + if (!(i % 100)) + dev_dbg(od->dev, "(%d) [%d] 0x%x size done, rest size: 0x%x\n", __LINE__, i, send_size, rest_size); + } + + return retval; +} + +static void ipc_spi_send_modem_bin(struct work_struct *send_modem_w) +{ + int retval = 0; + u32 int_cmd = 0xABCDABCD; + u32 int_cmd_fail = 0xDCBADCBA; + struct ipc_spi_send_modem_bin_workq_data *smw + = container_of(send_modem_w, struct ipc_spi_send_modem_bin_workq_data, send_modem_w); + struct ipc_spi *od = smw->od; + + unsigned int modem_addr = 0x60300000; + unsigned int nvm_static_fix_addr = 0x60e80000; + unsigned int nvm_static_cal_addr = 0x60f00000; + unsigned int nvm_dynamic_addr = 0x60f80000; + unsigned int nvm_addr = 0x60C00000; + unsigned short sec_end = 0x0000; + unsigned int force_hw_reset = 0x00111001; + + u8 *sec_start = NULL; + + dev_dbg(od->dev, "[SPI DUMP] mb : [%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x]\n", + *(u8 *)(p_virtual_buff), *(u8 *)(p_virtual_buff + 1), *(u8 *)(p_virtual_buff + 2), *(u8 *)(p_virtual_buff + 3), *(u8 *)(p_virtual_buff + 4), + *(u8 *)(p_virtual_buff + 5), *(u8 *)(p_virtual_buff + 6), *(u8 *)(p_virtual_buff + 7), *(u8 *)(p_virtual_buff + 8), *(u8 *)(p_virtual_buff + 9), + *(u8 *)(p_virtual_buff + 10), *(u8 *)(p_virtual_buff + 11), *(u8 *)(p_virtual_buff + 12), *(u8 *)(p_virtual_buff + 13), *(u8 *)(p_virtual_buff + 14), + *(u8 *)(p_virtual_buff + 15), *(u8 *)(p_virtual_buff + 16), *(u8 *)(p_virtual_buff + 17), *(u8 *)(p_virtual_buff + 18), *(u8 *)(p_virtual_buff + 19)); + + /* Command : ReqSecStart(0x0204) */ + sec_start = kmalloc(2048, GFP_ATOMIC); + if (!sec_start) { + dev_err(od->dev, "(%d) sec_start kmalloc fail.", __LINE__); + goto err; + } + memset(sec_start, 0, 2048); + + retval = ipc_spi_send_modem_bin_execute_cmd(od, 0x0204, 2048, (void *)sec_start); + if (retval < 0) { + dev_err(od->dev, "(%d) ipc_spi_send_modem_bin_execute_cmd fail : %d", __LINE__, retval); + goto err; + } + dev_dbg(od->dev, "(%d) ipc_spi_send_modem_bin_execute_cmd Done.\n", __LINE__); + + retval = ipc_spi_send_modem_bin_xmit_img(od, MODEM_MAIN, &modem_addr); + if (retval < 0) { + dev_err(od->dev, "(%d) ipc_spi_send_modem_bin_xmit_img fail : %d", __LINE__, retval); + goto err; + } + dev_dbg(od->dev, "(%d) ipc_spi_send_modem_bin_xmit_img MODEM_MAIN Done.\n", __LINE__); + + retval = ipc_spi_send_modem_bin_xmit_img(od, MODEM_NV, &nvm_addr); + if (retval < 0) { + dev_err(od->dev, "(%d) ipc_spi_send_modem_bin_xmit_img fail : %d", __LINE__, retval); + goto err; + } + dev_dbg(od->dev, "(%d) ipc_spi_send_modem_bin_xmit_img MODEM_NV:nvm_addr Done.\n", __LINE__); + +/* + retval = ipc_spi_send_modem_bin_xmit_img(od, MODEM_NV, &nvm_static_cal_addr); + if (retval < 0) { + dev_err(od->dev, "(%d) ipc_spi_send_modem_bin_xmit_img fail : %d", __LINE__, retval); + goto err; + } + dev_dbg(od->dev, "(%d) ipc_spi_send_modem_bin_xmit_img MODEM_NV:nvm_static_cal_addr Done.\n", __LINE__); + + retval = ipc_spi_send_modem_bin_xmit_img(od, MODEM_NV, &nvm_dynamic_addr); + if (retval < 0) { + dev_err(od->dev, "(%d) ipc_spi_send_modem_bin_xmit_img fail : %d", __LINE__, retval); + goto err; + } + dev_dbg(od->dev, "(%d) ipc_spi_send_modem_bin_xmit_img MODEM_NV:nvm_dynamic_addr Done.\n", __LINE__); +*/ + + /* Command : ReqSecEnd(0x0205) */ + retval = ipc_spi_send_modem_bin_execute_cmd(od, 0x0205, sizeof(unsigned short), &sec_end); + if (retval < 0) { + dev_err(od->dev, "(%d) ipc_spi_send_modem_bin_execute_cmd fail : %d", __LINE__, retval); + goto err; + } + dev_dbg(od->dev, "(%d) ipc_spi_send_modem_bin_execute_cmd; ReqSecEnd Done.\n", __LINE__); + + /* Command : ReqForceHwReset(0x0208) */ + retval = ipc_spi_send_modem_bin_execute_cmd(od, 0x0208, sizeof(unsigned int), &force_hw_reset); + if (retval < 0) { + dev_err(od->dev, "(%d) ipc_spi_send_modem_bin_execute_cmd fail : %d", __LINE__, retval); + goto err; + } + dev_dbg(od->dev, "(%d) ipc_spi_send_modem_bin_execute_cmd; ReqForceHwReset Done.\n", __LINE__); + + kfree(sec_start); + + ipc_spi_set_MRDY_pin(0); + + ipc_spi_make_data_interrupt(int_cmd, od); + + return; + +err: + ipc_spi_make_data_interrupt(int_cmd_fail, od); +} +#endif + +static int __devinit ipc_spi_platform_probe(struct platform_device *pdev) +{ + int r; + int irq; + struct ipc_spi *od = NULL; + struct ipc_spi_platform_data *pdata; + struct resource *res; + struct task_struct *th; + + printk(KERN_ERR "[%s]\n", __func__); + pdata = pdev->dev.platform_data; + if (!pdata || !pdata->cfg_gpio) { + dev_err(&pdev->dev, "No platform data\n"); + r = -EINVAL; + goto err; + } + + gpio_mrdy = pdata->gpio_ipc_mrdy; + gpio_srdy = pdata->gpio_ipc_srdy; + + dev_dbg(&pdev->dev, "(%d) gpio_mrdy : %d, gpio_srdy : %d(%d)\n", __LINE__, gpio_mrdy, gpio_srdy, gpio_get_value(gpio_srdy)); + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(&pdev->dev, "(%d) failed to get irq number\n", __LINE__); + + r = -EINVAL; + goto err; + } + irq = res->start; + + od = kzalloc(sizeof(struct ipc_spi), GFP_KERNEL); + if (!od) { + dev_err(&pdev->dev, "(%d) failed to allocate device\n", __LINE__); + + r = -ENOMEM; + goto err; + } + ipc_spi = od; + + dev_dbg(&pdev->dev, "(%d) IpcSpi dev: %p\n", __LINE__, od); + + od->base = 0; + od->size = 0x1000000; /* 16M */ + r = _request_mem(od, pdev); + if (r) + goto err; + + /* init mailbox state before registering irq handler */ + onedram_init_mailbox(); + + _init_data(od); + + printk(KERN_ERR "cfg_gpio run %p\n", pdata); + + pdata->cfg_gpio(); + +#ifndef FEATURE_SAMSUNG_SPI + irq = gpio_to_irq(gpio_srdy); + r = request_irq(irq, ipc_spi_irq_handler, IRQF_TRIGGER_RISING, "IPC_SRDY", od); + if (r) { + dev_err(&pdev->dev, "(%d) Failed to allocate an interrupt: %d\n", __LINE__, irq); + + goto err; + } + od->irq = irq; + enable_irq_wake(irq); +#endif + + /* Init work structure */ + ipc_spi_send_modem_work_data = kmalloc(sizeof(struct ipc_spi_send_modem_bin_workq_data), GFP_ATOMIC); + if (!ipc_spi_send_modem_work_data) { + dev_err(&pdev->dev, "(%d) memory alloc fail\n", __LINE__); + + r = -ENOMEM; + goto err; + } + INIT_WORK(&ipc_spi_send_modem_work_data->send_modem_w, ipc_spi_send_modem_bin); + + r = _register_chrdev(od); + if (r) { + dev_err(&pdev->dev, "(%d) Failed to register chrdev\n", __LINE__); + + goto err; + } + + r = sysfs_create_group(&od->dev->kobj, &ipc_spi_group); + if (r) { + dev_err(&pdev->dev, "(%d) Failed to create sysfs files\n", __LINE__); + + goto err; + } + od->group = &ipc_spi_group; + + platform_set_drvdata(pdev, od); + +#ifdef FEATURE_SAMSUNG_SPI + spi_main_init(pdata); +#endif + + th = kthread_create(ipc_spi_thread, (void *)od, "ipc_spi_thread"); + if (IS_ERR(th)) { + dev_err(&pdev->dev, "kernel_thread() failed : %d\n", r); + + goto err; + } + wake_up_process(th); + + dev_info(&pdev->dev, "(%d) platform probe Done.\n", __LINE__); + + return 0; + +err: + _release(od); + return r; +} + +static int __devexit ipc_spi_platform_remove(struct platform_device *pdev) +{ + struct ipc_spi *od = platform_get_drvdata(pdev); + + /* TODO: need onedram_resource clean? */ + _unregister_all_handlers(); + platform_set_drvdata(pdev, NULL); + ipc_spi = NULL; + _release(od); + + /* Free work queue data */ + kfree(ipc_spi_send_modem_work_data); + + return 0; +} + +#ifdef CONFIG_PM +static int ipc_spi_platform_suspend(struct platform_device *pdev, pm_message_t state) +{ +/* struct onedram *od = platform_get_drvdata(pdev); */ + + return 0; +} + +static int ipc_spi_platform_resume(struct platform_device *pdev) +{ +/* struct onedram *od = platform_get_drvdata(pdev); */ + + return 0; +} +#else +# define ipc_spi_platform_suspend NULL +# define ipc_spi_platform_resume NULL +#endif + +static int ipc_spi_probe(struct spi_device *spi) +{ + int retval = 0; + + printk(KERN_ERR "[%s]\n", __func__); + + p_ipc_spi = spi; + p_ipc_spi->mode = SPI_MODE_1; + p_ipc_spi->bits_per_word = 32; + + retval = spi_setup(p_ipc_spi); + if (retval != 0) { + printk(KERN_ERR "[%s] spi_setup ERROR : %d\n", __func__, retval); + + return retval; + } + + dev_info(&p_ipc_spi->dev, "(%d) spi probe Done.\n", __LINE__); + + return retval; +} + +static int ipc_spi_remove(struct spi_device *spi) +{ + return 0; +} + +static struct platform_driver ipc_spi_platform_driver = { + .probe = ipc_spi_platform_probe, + .remove = __devexit_p(ipc_spi_platform_remove), + .suspend = ipc_spi_platform_suspend, + .resume = ipc_spi_platform_resume, + .driver = { + .name = DRVNAME, + }, +}; + +static struct spi_driver ipc_spi_driver = { + .probe = ipc_spi_probe, + .remove = __devexit_p(ipc_spi_remove), + .driver = { + .name = "ipc_spi", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, +}; + +int ipc_spi_thread_restart() +{ + int retval; + + send_modem_spi = 1; + is_cp_reset = 1; + + printk(KERN_ERR "[IPC_SPI] ipc_spi_thread_restart\n"); + + spi_set_restart(); + + platform_driver_unregister(&ipc_spi_platform_driver); + + retval = platform_driver_register(&ipc_spi_platform_driver); + if (retval < 0) { + printk(KERN_ERR "[%s] platform_driver_register ERROR : %d\n", __func__, retval); + + goto exit; + } + + return 0; + +exit: + return retval; +} +EXPORT_SYMBOL(ipc_spi_thread_restart); + +static int __init ipc_spi_init(void) +{ + int retval = 0; + + printk(KERN_ERR "[%s]\n", __func__); + + retval = spi_register_driver(&ipc_spi_driver); + if (retval < 0) { + printk(KERN_ERR "[%s] spi_register_driver ERROR : %d\n", __func__, retval); + + goto exit; + } + + retval = platform_driver_register(&ipc_spi_platform_driver); + if (retval < 0) { + printk(KERN_ERR "[%s] platform_driver_register ERROR : %d\n", __func__, retval); + + goto exit; + } + + /* creat work queue thread */ + ipc_spi_wq = create_singlethread_workqueue("ipc_spi_wq"); + + if (!ipc_spi_wq) { + printk(KERN_ERR "[%s] get workqueue thread fail\n", __func__); + + retval = -ENOMEM; + goto exit; + } + + printk(KERN_ERR "[%s](%d) init Done - 1011260100.\n", __func__, __LINE__); + + return 0; + +exit: + return retval; +} + +static void __exit ipc_spi_exit(void) +{ + printk(KERN_ERR "[%s]\n", __func__); + + spi_unregister_driver(&ipc_spi_driver); + platform_driver_unregister(&ipc_spi_platform_driver); + + if (ipc_spi_wq) + destroy_workqueue(ipc_spi_wq); +} + +module_init(ipc_spi_init); +module_exit(ipc_spi_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Wonhee Seo <wonhee48.seo@samsung.com>"); +MODULE_DESCRIPTION("IpcSpi driver"); diff --git a/drivers/phone_svn/ipc_spi/spi_app.c b/drivers/phone_svn/ipc_spi/spi_app.c new file mode 100644 index 0000000..110d193 --- /dev/null +++ b/drivers/phone_svn/ipc_spi/spi_app.c @@ -0,0 +1,183 @@ +/************************************************************** + + spi_app.c + + + + interface spi and app + + + + This is MASTER side. + +***************************************************************/ + + + +/************************************************************** + + Preprocessor by common + +***************************************************************/ + +#include "spi_main.h" +#include "spi_app.h" +#include "spi_os.h" +#include "spi_data.h" +#include "spi_test.h" + +static unsigned int _pack_spi_data(enum SPI_DATA_TYPE_T type, void *buf, + void *data, unsigned int length); + + +/********************************************************** +Prototype void spi_app_receive_msg ( struct spi_os_msg * msg ) +Type function +Description receive data from spi task message queue and + Pack data for spi data. + Then inqueue the message to spi_data_queue_XXX_tx +Param input msg : message received from other task +Return value (none) +***********************************************************/ +void spi_receive_msg_from_app(struct spi_os_msg *msg) +{ + enum SPI_MAIN_MSG_T type; + enum SPI_DATA_QUEUE_TYPE_T q_type; + enum SPI_DATA_TYPE_T mux_type; + unsigned int in_length = 0, out_length = 0; + void *in_buffer = 0; + void *out_buffer = 0; + + type = msg->signal_code; + in_length = msg->data_length; + in_buffer = msg->data; + + switch (type) { + case SPI_MAIN_MSG_IPC_SEND: + q_type = SPI_DATA_QUEUE_TYPE_IPC_TX; + mux_type = SPI_DATA_MUX_IPC; + break; + + case SPI_MAIN_MSG_RAW_SEND: + q_type = SPI_DATA_QUEUE_TYPE_RAW_TX; + mux_type = SPI_DATA_MUX_RAW; + break; + + case SPI_MAIN_MSG_RFS_SEND: + q_type = SPI_DATA_QUEUE_TYPE_RFS_TX; + mux_type = SPI_DATA_MUX_RFS; + break; + + default: + SPI_OS_ASSERT(("[SPI] spi_app_receive_msg Unknown type")); + return; + } + + out_buffer = spi_os_malloc(in_length+SPI_DATA_HEADER_SIZE); + out_length = _pack_spi_data(mux_type, out_buffer, in_buffer, in_length); + + if (spi_data_inqueue(&spi_queue_info[q_type], out_buffer, + out_length) == 0) { + SPI_OS_ASSERT(("[SPI] spi_app_receive_msg inqueue[%d] Fail", + q_type)); + } + + spi_os_free(in_buffer); + spi_os_free(out_buffer); +} + + +/********************************************************** +Prototype void spi_send_msg ( void ) +Type function +Description Dequeue a spi data from spi_data_queue_XXX_rx + Unpack the spi data for ipc, raw or rfs data + Send msg to other task until that all queues are empty + CP use this functions for other task as below + IPC : ipc_cmd_send_queue + RAW : data_send_queue + RFS : rfs_send_queue +Param input (none) +Return value (none) +***********************************************************/ +void spi_send_msg_to_app(void) +{ + u32 int_cmd = 0; + struct ipc_spi *od_spi = NULL; + + #define MB_VALID 0x0080 + #define MB_DATA(x) (MB_VALID | x) + #define MBD_SEND_FMT 0x0002 + #define MBD_SEND_RAW 0x0001 + #define MBD_SEND_RFS 0x0100 + + + od_spi = ipc_spi; + + + if (spi_data_queue_is_empty(SPI_DATA_QUEUE_TYPE_IPC_RX) == 0) { + int_cmd = MB_DATA(MBD_SEND_FMT); + ipc_spi_make_data_interrupt(int_cmd, od_spi); + } + + if (spi_data_queue_is_empty(SPI_DATA_QUEUE_TYPE_RAW_RX) == 0) { + int_cmd = MB_DATA(MBD_SEND_RAW); + ipc_spi_make_data_interrupt(int_cmd, od_spi); + } + + if (spi_data_queue_is_empty(SPI_DATA_QUEUE_TYPE_RFS_RX) == 0) { + int_cmd = MB_DATA(MBD_SEND_RFS); + ipc_spi_make_data_interrupt(int_cmd, od_spi); + } +} + + +/********************************************************** +Prototype unsigned int _pack_spi_data (SPI_DATA_TYPE_T type,void * buf, void * data, unsigned int length) +Type static function +Description pack data for spi +Param input type : type of data type + buf : address of buffer to be saved + data : address of data to pack + length : length of input data +Return value length of packed data +***********************************************************/ +static unsigned int _pack_spi_data(enum SPI_DATA_TYPE_T type, void *buf, + void *data, unsigned int length) +{ + char *spi_packet = NULL; + unsigned int out_length = 0; + + spi_packet = (char *) buf; + + spi_os_memset((char *)spi_packet, 0x00, (unsigned int)length); + spi_os_memset((char *)spi_packet, (unsigned char)SPI_DATA_BOF, + SPI_DATA_BOF_SIZE); + spi_os_memcpy((char *)spi_packet + SPI_DATA_BOF_SIZE, data, length); + spi_os_memset((char *)spi_packet + SPI_DATA_BOF_SIZE + length, + (unsigned char)SPI_DATA_EOF, SPI_DATA_EOF_SIZE); + + out_length = SPI_DATA_BOF_SIZE + length + SPI_DATA_EOF_SIZE; + + return out_length; +} + + +/********************************************************** +Prototype int spi_app_ready ( void ) +Type function +Description check if spi initialization is done. + Decide it as spi_main_state. +Param input (none) +Return value 1 : spi initialization is done. + 0 : spi initialization is not done. +***********************************************************/ +int spi_is_ready(void) +{ + if ((spi_main_state == SPI_MAIN_STATE_START) || + (spi_main_state == SPI_MAIN_STATE_END)) + return 0; + + return 1; +} + diff --git a/drivers/phone_svn/ipc_spi/spi_app.h b/drivers/phone_svn/ipc_spi/spi_app.h new file mode 100644 index 0000000..70a9d69 --- /dev/null +++ b/drivers/phone_svn/ipc_spi/spi_app.h @@ -0,0 +1,15 @@ +#ifndef _SPI_APP_H_ +#define _SPI_APP_H_ + +#include "spi_main.h" +#include "spi_data.h" +#include "spi_os.h" + +extern void spi_receive_msg_from_app(struct spi_os_msg *msg); +extern void spi_send_msg_to_app(void); +extern int spi_is_ready(void); + +extern struct ipc_spi *ipc_spi; +extern void ipc_spi_make_data_interrupt(u32 cmd, struct ipc_spi *od); + +#endif diff --git a/drivers/phone_svn/ipc_spi/spi_data.c b/drivers/phone_svn/ipc_spi/spi_data.c new file mode 100644 index 0000000..ecd62ab --- /dev/null +++ b/drivers/phone_svn/ipc_spi/spi_data.c @@ -0,0 +1,981 @@ +/************************************************************** + + spi_data.c + + + + handling spi data + + + + This is MASTER side. + +***************************************************************/ + + + +/************************************************************** + + Preprocessor by common + +***************************************************************/ + +#include "spi_data.h" +#include "spi_os.h" +#include "spi_dev.h" + + + +/************************************************************** + + Definition of Variables and Functions by common + +***************************************************************/ + +struct spi_data_queue *spi_queue; +struct spi_data_queue_info *spi_queue_info; +struct spi_data_div_buf *spi_div_buf; + +char *gspi_data_prepare_packet; + +char *gspi_data_packet_buf; +char *gspi_data_sync_buf; + +static int _prepare_tx_type_packet(void *buf, + struct spi_data_queue_info *queue_info, + struct spi_data_div_buf *spi_data_buf, + enum SPI_DATA_TYPE_T spi_type); +static int _spi_data_verify(void *buf, unsigned int mux); + + +/********************************************************** + +Prototype void spi_data_queue_init ( void ) + +Type function + +Description init buffer and data variable + +Param input (none) + +Return value (none) + +***********************************************************/ +void spi_data_queue_init(void) +{ + int i = 0; + + /* This is defined at ipc_spi.c file and copied. */ + #define FMT_OUT 0x0FE000 + #define FMT_IN 0x10E000 + #define FMT_SZ 0x10000 /* 65536 bytes */ + + #define RAW_OUT 0x11E000 + #define RAW_IN 0x21E000 + #define RAW_SZ 0x100000 /* 1 MB */ + + #define RFS_OUT 0x31E000 + #define RFS_IN 0x41E000 + #define RFS_SZ 0x100000 /* 1 MB */ + + void *p_virtual_buff; + unsigned int buffer_offset[SPI_DATA_QUEUE_TYPE_NB] = { + FMT_OUT, FMT_IN, RAW_OUT, RAW_IN, RFS_OUT, RFS_IN}; + + p_virtual_buff = ipc_spi_get_queue_buff(); + spi_queue = p_virtual_buff+sizeof(struct spi_data_queue)*2; + + + spi_queue_info = (struct spi_data_queue_info *) spi_os_vmalloc( + sizeof(struct spi_data_queue_info)*SPI_DATA_QUEUE_TYPE_NB); + spi_os_memset(spi_queue_info, 0x00, + sizeof(struct spi_data_queue_info)*SPI_DATA_QUEUE_TYPE_NB); + + spi_div_buf = (struct spi_data_div_buf *) spi_os_vmalloc( + sizeof(struct spi_data_div_buf)*SPI_DATA_QUEUE_TYPE_NB); + spi_os_memset(spi_div_buf, 0x00, sizeof(struct spi_data_div_buf) + *SPI_DATA_QUEUE_TYPE_NB); + gspi_data_prepare_packet = (char *) spi_os_vmalloc( + SPI_DEV_MAX_PACKET_SIZE); + gspi_data_packet_buf = (char *) spi_os_malloc(SPI_DEV_MAX_PACKET_SIZE); + gspi_data_sync_buf = (char *) spi_os_malloc(SPI_DEV_MAX_PACKET_SIZE); + + for (i = 0 ; i < SPI_DATA_QUEUE_TYPE_NB ; i++) { + spi_queue_info[i].header = &spi_queue[i]; + + switch (i) { + case SPI_DATA_QUEUE_TYPE_IPC_TX: + case SPI_DATA_QUEUE_TYPE_IPC_RX: + spi_queue_info[i].buf_size = SPI_DATA_IPC_QUEUE_SIZE; + spi_queue_info[i].type = SPI_DATA_MUX_IPC; + spi_div_buf[i].buffer = (char *)spi_os_vmalloc( + SPI_DATA_IPC_DIV_BUFFER_SIZE); + break; + case SPI_DATA_QUEUE_TYPE_RAW_TX: + case SPI_DATA_QUEUE_TYPE_RAW_RX: + spi_queue_info[i].buf_size = SPI_DATA_RAW_QUEUE_SIZE; + spi_queue_info[i].type = SPI_DATA_MUX_RAW; + spi_div_buf[i].buffer = (char *)spi_os_vmalloc( + SPI_DATA_RAW_DIV_BUFFER_SIZE); + break; + case SPI_DATA_QUEUE_TYPE_RFS_TX: + case SPI_DATA_QUEUE_TYPE_RFS_RX: + spi_queue_info[i].buf_size = SPI_DATA_RFS_QUEUE_SIZE; + spi_queue_info[i].type = SPI_DATA_MUX_RFS; + spi_div_buf[i].buffer = (char *)spi_os_vmalloc( + SPI_DATA_RFS_DIV_BUFFER_SIZE); + break; + } + + if (spi_queue_info[i].buffer == 0) + spi_queue_info[i].buffer = + (char *)(p_virtual_buff + buffer_offset[i]); + } +} + + +/********************************************************** + +Prototype void spi_data_queue_destroy ( void ) + +Type function + +Description memfree buffer and data variable + +Param input (none) + +Return value (none) + +***********************************************************/ + +void spi_data_queue_destroy(void) +{ + int i = 0; + + if (spi_div_buf != NULL) { + for (i = 0 ; i < SPI_DATA_QUEUE_TYPE_NB ; i++) { + if (spi_div_buf[i].buffer != NULL) { + spi_os_vfree(spi_div_buf[i].buffer); + spi_div_buf[i].buffer = NULL; + } + } + } + + if (gspi_data_prepare_packet != NULL) { + spi_os_vfree(gspi_data_prepare_packet); + gspi_data_prepare_packet = NULL; + } + + if (gspi_data_packet_buf != NULL) { + spi_os_free(gspi_data_packet_buf); + gspi_data_packet_buf = NULL; + } + + if (gspi_data_sync_buf != NULL) { + spi_os_free(gspi_data_sync_buf); + gspi_data_sync_buf = NULL; + } + + if (spi_div_buf != NULL) { + spi_os_vfree(spi_div_buf); + spi_div_buf = NULL; + } + + if (spi_queue_info != NULL) { + spi_os_vfree(spi_queue_info); + spi_queue_info = NULL; + } +} + + + +/********************************************************** + +Prototype int spi_data_inqueue (struct spi_data_queue_info * queue_info, + void * data, unsigned int length ) + +Type function + +Description inqueue data to spi rx or tx buffer + +Param input queue_info : queue for inqueue + + data : address of data + + lenth : length of data + +Return value 0 : fail + + 1 : success + +***********************************************************/ +int spi_data_inqueue(struct spi_data_queue_info *queue_info, + void *data, unsigned int length) +{ + char *pdata = NULL; + struct spi_data_queue *queue = NULL; + pdata = data; + queue = queue_info->header; + + if (queue->tail < queue->head) { + if ((queue->head - queue->tail) < length) { + SPI_OS_ASSERT(("%s%s[%u],%s[%u],%s[%u],%s[%u]\n", + "[SPI] ERROR : spi_data_inqueue : ", + "queue is overflow head", queue->head, + "tail", queue->tail, "length", length, + "buf_size", queue_info->buf_size)); + return 0; + } + } else { + if ((queue_info->buf_size - (queue->tail - queue->head)) + < length) { + SPI_OS_ASSERT(("%s%s[%u],%s[%u],%s[%u],%s[%u]\n", + "[SPI] ERROR : spi_data_inqueue : queue is overflow ", + "head", queue->head, "tail", queue->tail, + "request length", length, + "buf_size", queue_info->buf_size)); + return 0; + } + } + + if (&spi_queue_info[SPI_DATA_QUEUE_TYPE_IPC_RX] == queue_info + || &spi_queue_info[SPI_DATA_QUEUE_TYPE_RAW_RX] == queue_info + || &spi_queue_info[SPI_DATA_QUEUE_TYPE_RFS_RX] == queue_info) { + pdata += (SPI_DATA_MUX_SIZE+SPI_DATA_LENGTH_SIZE); + length -= (SPI_DATA_MUX_SIZE+SPI_DATA_LENGTH_SIZE); + } + + if (queue_info->buf_size < (queue->tail + length)) { + /* maximum < tail + length */ + unsigned int pre_data_len = 0; + pre_data_len = queue_info->buf_size - queue->tail; + spi_os_memcpy(queue_info->buffer + queue->tail, + pdata, pre_data_len); + spi_os_memcpy(queue_info->buffer, pdata + pre_data_len, + length - pre_data_len); + queue->tail = ((queue->tail+length)%queue_info->buf_size); + } else if (queue_info->buf_size == (queue->tail + length)) { + /* maximum == tail + length */ + spi_os_memcpy((queue_info->buffer + queue->tail), + (char *)pdata, length); + queue->tail = 0; + } else { + /* maximum > tail + length */ + spi_os_memcpy((queue_info->buffer + queue->tail), + (char *)pdata, length); + queue->tail = queue->tail + length; + } + + return 1; +} + + +/********************************************************** + +Prototype unsigned int spi_data_dequeue + (struct spi_data_queue_info * queue_info, void * pdata) + +Type function + +Description dequeue data to spi + +Param input queue_info : queue for dequeue + + pdata : address of data + +Return value 0 : fail + + 1 : success + +***********************************************************/ + +unsigned int spi_data_dequeue(struct spi_data_queue_info *queue_info, + void *pdata) +{ + unsigned int length = 0, buf_length = 0, pre_data_len = 0; + struct spi_data_queue *queue = NULL; + + queue = queue_info->header; + + if (queue->tail == queue->head) { /* empty */ + SPI_OS_ERROR(("%s %s\n", + "[SPI] ERROR : spi_data_dequeue:", + "queue is empty")); + return 0; + } + + if (queue->head == queue_info->buf_size) + queue->head = 0; + + /* check length of data */ + if (&spi_queue_info[SPI_DATA_QUEUE_TYPE_IPC_TX] == queue_info + || &spi_queue_info[SPI_DATA_QUEUE_TYPE_IPC_RX] == queue_info) { + /* IPC Case */ + if (queue_info->buf_size == (queue->head + SPI_DATA_BOF_SIZE)) { + /* maximum == head + pos_len */ + spi_os_memcpy(&length, queue_info->buffer, + SPI_DATA_IPC_INNER_LENGTH_SIZE); + } else if (queue_info->buf_size < (queue->head + + SPI_DATA_BOF_SIZE + + SPI_DATA_IPC_INNER_LENGTH_SIZE)) { + /* maximum < head + pos_len */ + char data_header[SPI_DATA_IPC_INNER_LENGTH_SIZE] = {0,}; + pre_data_len = queue_info->buf_size - queue->head - + SPI_DATA_BOF_SIZE; + + spi_os_memcpy(data_header, (queue_info->buffer + + queue->head + SPI_DATA_BOF_SIZE), pre_data_len); + spi_os_memcpy(data_header + pre_data_len, + queue_info->buffer, + SPI_DATA_IPC_INNER_LENGTH_SIZE - pre_data_len); + spi_os_memcpy(&length, data_header, + SPI_DATA_IPC_INNER_LENGTH_SIZE); + } else { /* maximum > head + pos_len */ + spi_os_memcpy(&length, + (queue_info->buffer + queue->head + + SPI_DATA_BOF_SIZE), + SPI_DATA_IPC_INNER_LENGTH_SIZE); + } + } else { /* RAW, RFS Case */ + if (queue_info->buf_size == (queue->head + SPI_DATA_BOF_SIZE)) { + /* maximum == head + pos_len */ + spi_os_memcpy(&length, queue_info->buffer, + SPI_DATA_INNER_LENGTH_SIZE); + } else if (queue_info->buf_size < (queue->head + + SPI_DATA_BOF_SIZE + SPI_DATA_INNER_LENGTH_SIZE)) { + /* maximum < head + pos_len */ + char data_header[SPI_DATA_INNER_LENGTH_SIZE] = {0,}; + pre_data_len = queue_info->buf_size - + queue->head - SPI_DATA_BOF_SIZE; + + spi_os_memcpy(data_header, + (queue_info->buffer + queue->head + + SPI_DATA_BOF_SIZE), + pre_data_len); + spi_os_memcpy(data_header + pre_data_len, + queue_info->buffer, + SPI_DATA_INNER_LENGTH_SIZE - pre_data_len); + spi_os_memcpy(&length, data_header, + SPI_DATA_INNER_LENGTH_SIZE); + } else { /* maximum > head + pos_len */ + spi_os_memcpy(&length, + (queue_info->buffer + queue->head + + SPI_DATA_BOF_SIZE), + SPI_DATA_INNER_LENGTH_SIZE); + } + } + length += SPI_DATA_BOF_SIZE + SPI_DATA_EOF_SIZE; + buf_length = length + SPI_DATA_MUX_SIZE + SPI_DATA_LENGTH_SIZE; + + if (length > SPI_DEV_MAX_PACKET_SIZE) { + if (&spi_queue_info[SPI_DATA_QUEUE_TYPE_IPC_TX] == queue_info || + &spi_queue_info[SPI_DATA_QUEUE_TYPE_IPC_RX] == queue_info) { + /* IPC Case */ + SPI_OS_ERROR(("%s %s[%x],%s[%u],%s[%u],%s[%u]\n", + "[SPI] ERROR : spi_data_dequeue: IPC error", + "length", length, "buf_size", + queue_info->buf_size, "head", + queue->head, "tail", queue->tail)); + } else if ( + &spi_queue_info[SPI_DATA_QUEUE_TYPE_RAW_TX] == queue_info + || &spi_queue_info[SPI_DATA_QUEUE_TYPE_RAW_RX] == queue_info) { + /* RAW Case */ + SPI_OS_ERROR(("%s %s[%x],%s[%u],%s[%u],%s[%u]\n", + "[SPI] ERROR : spi_data_dequeue: RAW error", + "length", length, + "buf_size", queue_info->buf_size, + "head", queue->head, "tail", queue->tail)); + } else { /* RFS Case */ + SPI_OS_ERROR(("%s %s[%x],%s[%u],%s[%u],%s[%u]\n", + "[SPI] ERROR : spi_data_dequeue: RFS error", + "length", length, + "buf_size", queue_info->buf_size, + "head", queue->head, "tail", queue->tail)); + } + spi_os_trace_dump_low("spi_data_dequeue error", + queue_info->buffer + queue->head - 1, 16); + spi_os_trace_dump_low("spi_data_dequeue error", + queue_info->buffer + queue->tail - 1, 16); + return 0; + } + + if (&spi_queue_info[SPI_DATA_QUEUE_TYPE_IPC_TX] == queue_info + || &spi_queue_info[SPI_DATA_QUEUE_TYPE_RAW_TX] == queue_info + || &spi_queue_info[SPI_DATA_QUEUE_TYPE_RFS_TX] == queue_info) { + unsigned int templength; + + spi_os_memcpy((char *) pdata, &queue_info->type, + SPI_DATA_MUX_SIZE); + pdata += SPI_DATA_MUX_SIZE; + templength = length-SPI_DATA_BOF_SIZE-SPI_DATA_EOF_SIZE; + spi_os_memcpy((char *) pdata, &templength, + SPI_DATA_LENGTH_SIZE); + pdata += SPI_DATA_LENGTH_SIZE; + } + + if (queue->tail > queue->head) { + if (queue->tail - queue->head < length) { + SPI_OS_ERROR(("%s %s tail[%u], head[%u], length[%u]\n", + "[SPI] ERROR : spi_data_dequeue:", + "request data length is less than queue`s remain data.", + queue->tail, queue->head, length)); + + spi_os_trace_dump_low("spi_data_dequeue error", + queue_info->buffer + queue->head - 1, 16); + spi_os_trace_dump_low("spi_data_dequeue error", + queue_info->buffer + queue->tail - 1, 16); + return 0; + } + } else if (queue->tail < queue->head) { + if ((queue_info->buf_size - queue->head + queue->tail) + < length) { + SPI_OS_ERROR(("%s %s tail[%u], head[%u], length[%u]\n", + "[SPI] ERROR : spi_data_dequeue:", + "request data length is less than queue`s remain data.", + queue->tail, queue->head, length)); + + spi_os_trace_dump_low("spi_data_dequeue error", + queue_info->buffer + queue->head - 1, 16); + spi_os_trace_dump_low("spi_data_dequeue error", + queue_info->buffer + queue->tail - 1, 16); + return 0; + } + } + + if (queue_info->buf_size < (queue->head+length)) { + /* maximum < head + length */ + pre_data_len = queue_info->buf_size - queue->head; + spi_os_memcpy((char *)pdata, + queue_info->buffer + queue->head, pre_data_len); + spi_os_memcpy((char *)pdata + pre_data_len, + queue_info->buffer, length - pre_data_len); + queue->head = length - pre_data_len; + } else if (queue_info->buf_size == (queue->head+length)) { + /* maximum = head + length */ + spi_os_memcpy((char *)pdata, + (queue_info->buffer + queue->head), length); + queue->head = 0; + } else { /* maximum > head + length */ + spi_os_memcpy((char *)pdata, + (queue_info->buffer + queue->head), length); + queue->head = queue->head + length; + } + + return buf_length; +} + + +/********************************************************** + +Prototype int spi_data_queue_is_empty + +Type function + +Description check queue space empty or not + +Param input type : queue type + +Return value 0 : not empty + + 1 : empty + +***********************************************************/ + +int spi_data_queue_is_empty(enum SPI_DATA_QUEUE_TYPE_T type) +{ + if (spi_queue[type].head == spi_queue[type].tail) + return 1; /* empty */ + else + return 0; /* not empty */ +} + + +/********************************************************** + +Prototype int spi_data_div_buf_is_empty + +Type function + +Description check queue space empty or not + +Param input type : queue type + +Return value 0 : not empty + + 1 : empty + +***********************************************************/ + +int spi_data_div_buf_is_empty(enum SPI_DATA_QUEUE_TYPE_T type) +{ + if (spi_div_buf[type].length > 0) + return 0; /* not empty */ + else + return 1; /* Empty */ +} + + +/********************************************************** + +Prototype int spi_data_check_tx_queue( void ) + +Type function + +Description check tx queue space empty or not + +Param input (none) + +Return value 0 : empty + + 1 : data exist + +***********************************************************/ + +int spi_data_check_tx_queue(void) +{ + if ((spi_data_queue_is_empty(SPI_DATA_QUEUE_TYPE_IPC_TX) == 0) + || (spi_data_queue_is_empty(SPI_DATA_QUEUE_TYPE_RAW_TX) == 0) + || (spi_data_queue_is_empty(SPI_DATA_QUEUE_TYPE_RFS_TX) == 0)) + return 1; /* has data */ + else if ((spi_data_div_buf_is_empty(SPI_DATA_QUEUE_TYPE_IPC_TX) == 0) + || (spi_data_div_buf_is_empty(SPI_DATA_QUEUE_TYPE_RAW_TX) == 0) + || (spi_data_div_buf_is_empty(SPI_DATA_QUEUE_TYPE_RFS_TX) == 0)) + return 1; /* has data */ + else + return 0; /* empty */ +} + + +/********************************************************** + +Prototype int spi_data_prepare_tx_packet ( void * buf ) + +Type function + +Description prepare packet for sync. attach spi_data_protocol_header, + mux, bof, data and eof + +Param input buf : address of data + +Return value 0 : fail + + 1 : success + +***********************************************************/ + +int spi_data_prepare_tx_packet(void *buf) +{ + struct spi_data_queue *queue = NULL; + int flag_to_send = 0; + + /* process IPC data */ + queue = &spi_queue[SPI_DATA_QUEUE_TYPE_IPC_TX]; + if ((queue->head != queue->tail) + || (spi_div_buf[SPI_DATA_QUEUE_TYPE_IPC_TX].length > 0)) { + if (_prepare_tx_type_packet(buf, + &spi_queue_info[SPI_DATA_QUEUE_TYPE_IPC_TX], + &spi_div_buf[SPI_DATA_QUEUE_TYPE_IPC_TX], + SPI_DATA_MUX_IPC) == 0) + return SPI_DATA_PACKET_HEADER_SIZE + + ((struct spi_data_packet_header *) + buf)->current_data_size; + + flag_to_send++; + } + + /* process RAW data */ + queue = &spi_queue[SPI_DATA_QUEUE_TYPE_RAW_TX]; + if ((queue->head != queue->tail) + || (spi_div_buf[SPI_DATA_QUEUE_TYPE_RAW_TX].length > 0)) { + if (_prepare_tx_type_packet(buf, + &spi_queue_info[SPI_DATA_QUEUE_TYPE_RAW_TX], + &spi_div_buf[SPI_DATA_QUEUE_TYPE_RAW_TX], + SPI_DATA_MUX_RAW) == 0) + return SPI_DATA_PACKET_HEADER_SIZE + + ((struct spi_data_packet_header *) + buf)->current_data_size; + + flag_to_send++; + } + + /* process RFS data */ + queue = &spi_queue[SPI_DATA_QUEUE_TYPE_RFS_TX]; + if ((queue->head != queue->tail) + || (spi_div_buf[SPI_DATA_QUEUE_TYPE_RFS_TX].length > 0)) { + if (_prepare_tx_type_packet(buf, + &spi_queue_info[SPI_DATA_QUEUE_TYPE_RFS_TX], + &spi_div_buf[SPI_DATA_QUEUE_TYPE_RFS_TX], + SPI_DATA_MUX_RFS) == 0) + return SPI_DATA_PACKET_HEADER_SIZE + + ((struct spi_data_packet_header *) + buf)->current_data_size; + + flag_to_send++; + } + + if (flag_to_send == 0) + return 0; + else + return SPI_DATA_PACKET_HEADER_SIZE + + ((struct spi_data_packet_header *) + buf)->current_data_size; +} + + +/********************************************************** + +Prototype static int _prepare_tx_type_packet + +Type static function + +Description prepare spi data for copy to spi packet from rx queue. + If spi data bigger than spi packet free size, + it need separate and inqueue extra data + +Param input buf : address of buffer to be saved + queue_info : tx queue for IPC or RAW or RFS + spi_data_buf : tx buffer for IPC or RAW or RFS + +Return value 0 : buffer full + + (other) : packet size include header and tail + +***********************************************************/ + +static int _prepare_tx_type_packet(void *buf, + struct spi_data_queue_info *queue_info, + struct spi_data_div_buf *spi_data_buf, + enum SPI_DATA_TYPE_T spi_type) +{ + char *spi_packet = NULL; + + struct spi_data_packet_header *spi_packet_header = NULL; + unsigned int spi_packet_free_length = 0; + + unsigned int cur_dequeue_length = 0; + unsigned int spi_packet_count = 0; + unsigned int spi_data_mux = 0; + + struct spi_data_queue *queue = NULL; + + queue = queue_info->header; + + spi_packet = (char *)buf; + spi_packet_header = (struct spi_data_packet_header *)buf; + spi_packet_free_length = SPI_DATA_PACKET_MAX_PACKET_BODY_SIZE - + spi_packet_header->current_data_size; + + /* not enough space in spi packet */ + /* spi_packet_header->current_data_size > 2022(2048 - 16) */ + if (spi_packet_header->current_data_size > + SPI_DATA_PACKET_MAX_PACKET_BODY_SIZE - SPI_DATA_MIN_SIZE) { + if (spi_data_check_tx_queue() == 1) + spi_packet_header->more = 1; + return 0; + } + + while ((queue->head != queue->tail) || (spi_data_buf->length > 0)) { + spi_os_memset(gspi_data_prepare_packet, + 0, SPI_DEV_MAX_PACKET_SIZE); + cur_dequeue_length = 0; + + /* dequeue SPI data */ + if (spi_data_buf->length > 0) { + if ((*(unsigned int *)spi_data_buf->buffer) + == SPI_DATA_FF_PADDING_HEADER) { + /* if data has 0xFF padding header */ + /* send packet directly */ + spi_os_memcpy(spi_packet, spi_data_buf->buffer, + spi_data_buf->length); + spi_data_buf->length = 0; + return 0; + } else { + /* read from tx div buf */ + spi_os_memcpy(gspi_data_prepare_packet, + spi_data_buf->buffer, + spi_data_buf->length); + cur_dequeue_length = spi_data_buf->length; + spi_data_buf->length = 0; + } + } else { + /* read from tx queue */ + cur_dequeue_length = spi_data_dequeue(queue_info, + gspi_data_prepare_packet); + } + + if (cur_dequeue_length == 0) + continue; + + if (spi_packet_free_length < cur_dequeue_length) { + spi_os_memcpy(spi_data_buf->buffer, + gspi_data_prepare_packet, cur_dequeue_length); + spi_data_buf->length = cur_dequeue_length; + spi_packet_header->more = 1; + return 0; + } + + /* check mux value */ + spi_os_memcpy(&spi_data_mux, gspi_data_prepare_packet, + SPI_DATA_MUX_SIZE); + if (spi_data_mux == 0) + spi_os_memcpy(gspi_data_prepare_packet, + &spi_type, SPI_DATA_MUX_SIZE); + + spi_os_memcpy(spi_packet + SPI_DATA_PACKET_HEADER_SIZE + + spi_packet_header->current_data_size, + gspi_data_prepare_packet, cur_dequeue_length); + + /* update header */ + spi_packet_header->current_data_size += cur_dequeue_length; + spi_packet_free_length -= cur_dequeue_length; + + /* increase spi packet count */ + spi_packet_count++; + + /* check spi packet size */ + if (spi_packet_free_length < SPI_DATA_MIN_SIZE) { + if (spi_data_check_tx_queue() == 1) + spi_packet_header->more = 1; + return 0; + } + + /* check spi maximum count per packet */ + if (spi_packet_count >= SPI_DATA_MAX_COUNT_PER_PACKET) { + + SPI_OS_ERROR(("%s %s\n", + "[SPI] ERROR : spi _prepare_tx_type_packet :", + "spi_packet_count is full")); + + return 0; + } + } + + return spi_packet_header->current_data_size + + SPI_DATA_PACKET_HEADER_SIZE; +} + + +/********************************************************** + +Prototype int spi_data_parsing_rx_packet + ( void * buf, unsigned int length ) + +Type function + +Description parsing rx packet for transfer to application. It extract data + + and inqueue each SPI_DATA_TYPE queue + +Param input buf : address of buffer to be saved + + lenth : length of data + +Return value 0 : fail + + 1 : success + +***********************************************************/ + +int spi_data_parsing_rx_packet(void *buf, unsigned int length) +{ + struct spi_data_packet_header *spi_packet_header = NULL; + char *spi_packet = NULL; + unsigned int spi_packet_length = 0; + unsigned int spi_packet_cur_pos = SPI_DATA_PACKET_HEADER_SIZE; + + unsigned int spi_data_mux = 0; + unsigned int spi_data_length = 0; + char *spi_cur_data = NULL; + + struct spi_data_queue_info *queue_info = NULL; + struct spi_data_div_buf *tx_div_buf = NULL; + + + /* check spi packet header */ + if (*(unsigned int *)buf == 0x00000000 + || *(unsigned int *)buf == 0xFFFFFFFF) { + /* if spi header is invalid, */ + /* read spi header again with next 4 byte */ + buf += SPI_DATA_PACKET_HEADER_SIZE; + } + + spi_packet = (char *) buf; + + /* read spi packet header */ + spi_packet_header = (struct spi_data_packet_header *) buf; + spi_packet_length = SPI_DATA_PACKET_HEADER_SIZE + + spi_packet_header->current_data_size; + + + do { + /* read spi data mux and set current queue */ + spi_os_memcpy(&spi_data_mux, + spi_packet + spi_packet_cur_pos, SPI_DATA_MUX_SIZE); + + switch (spi_data_mux & SPI_DATA_MUX_NORMAL_MASK) { + case SPI_DATA_MUX_IPC: + queue_info = + &spi_queue_info[SPI_DATA_QUEUE_TYPE_IPC_RX]; + tx_div_buf = + &spi_div_buf[SPI_DATA_QUEUE_TYPE_IPC_RX]; + break; + + case SPI_DATA_MUX_RAW: + queue_info = + &spi_queue_info[SPI_DATA_QUEUE_TYPE_RAW_RX]; + tx_div_buf = + &spi_div_buf[SPI_DATA_QUEUE_TYPE_RAW_RX]; + break; + + case SPI_DATA_MUX_RFS: + queue_info = + &spi_queue_info[SPI_DATA_QUEUE_TYPE_RFS_RX]; + tx_div_buf = + &spi_div_buf[SPI_DATA_QUEUE_TYPE_RFS_RX]; + break; + + default: + SPI_OS_ERROR(("%s len[%u], pos[%u]\n", + "[SPI] ERROR : spi_data_parsing_rx_packet : MUX error", + spi_packet_length, spi_packet_cur_pos)); + + spi_os_trace_dump_low("mux error", + spi_packet + spi_packet_cur_pos, 16); + return spi_packet_cur_pos - SPI_DATA_PACKET_HEADER_SIZE; + } + + /* read spi data length */ + spi_os_memcpy(&spi_data_length, spi_packet + + spi_packet_cur_pos + SPI_DATA_LENGTH_OFFSET, + SPI_DATA_LENGTH_SIZE); + + if (spi_data_mux & SPI_DATA_MUX_MORE_H + || spi_data_mux & SPI_DATA_MUX_MORE_M) + spi_data_length += SPI_DATA_HEADER_SIZE_FRONT; + else if (spi_data_mux & SPI_DATA_MUX_MORE_T) + spi_data_length += SPI_DATA_HEADER_SIZE; + else + spi_data_length += SPI_DATA_HEADER_SIZE; + + /* read data and make spi data */ + spi_cur_data = spi_packet + spi_packet_cur_pos; + + /* verify spi data */ + if (_spi_data_verify(spi_cur_data, spi_data_mux) == 0) { + spi_packet_cur_pos += spi_data_length; + continue; + } + + /* inqueue rx buffer */ + if (spi_data_mux & SPI_DATA_MUX_MORE_H + || spi_data_mux & SPI_DATA_MUX_MORE_M) { + /* middle of divided packet. save to rx_div_buf */ + spi_os_memcpy((void *)(tx_div_buf->buffer + + tx_div_buf->length), + spi_cur_data, spi_data_length); + tx_div_buf->length += + (spi_data_length - SPI_DATA_HEADER_SIZE_FRONT); + } else if (spi_data_mux & SPI_DATA_MUX_MORE_T) { + unsigned int spi_origine_len = 0; + + /* tail of divided packet. save to rx_div_buf */ + spi_os_memcpy((void *)(tx_div_buf->buffer + + tx_div_buf->length), + spi_cur_data, spi_data_length); + tx_div_buf->length += spi_data_length; + /* update spi data length at spi header */ + spi_origine_len = + tx_div_buf->length - SPI_DATA_HEADER_SIZE; + spi_os_memcpy(tx_div_buf->buffer + + SPI_DATA_LENGTH_OFFSET, + &(spi_origine_len), SPI_DATA_LENGTH_SIZE); + + /* inqueue from rx_div_buf */ + spi_data_inqueue(queue_info, + tx_div_buf->buffer, tx_div_buf->length); + + spi_os_memset(tx_div_buf->buffer, + 0, SPI_DATA_DIVIDE_BUFFER_SIZE); + tx_div_buf->length = 0; + } else { + /* normal packet */ + spi_data_inqueue(queue_info, + spi_cur_data, spi_data_length); + } + + /* move spi packet current posision */ + spi_packet_cur_pos += spi_data_length; + } while ((spi_packet_length - 1) > spi_packet_cur_pos); + + return 1; +} + + +/********************************************************** + +Prototype int _spi_data_verify( void * buf, unsigned int mux ) + +Type function + +Description verify integrity of packet + +Param input buf : address of packet data received + + mux : data selection + +Return value 0 : fail + + 1 : success + +***********************************************************/ + +int _spi_data_verify(void *buf, unsigned int mux) +{ + unsigned int length = 0; + + unsigned int max_data_len = SPI_DATA_MAX_SIZE_PER_PACKET; + char bof = 0x00; + char eof = 0x00; + + spi_os_memcpy(&length, (char *)buf+SPI_DATA_LENGTH_OFFSET, + SPI_DATA_LENGTH_SIZE); + spi_os_memcpy(&bof, (char *)buf+SPI_DATA_BOF_OFFSET, SPI_DATA_BOF_SIZE); + + if (mux & SPI_DATA_MUX_MORE_H || mux & SPI_DATA_MUX_MORE_M) + max_data_len += SPI_DATA_EOF_SIZE; + + if (bof == SPI_DATA_BOF) { + if (length <= max_data_len) { + if (mux & SPI_DATA_MUX_MORE_H + || mux & SPI_DATA_MUX_MORE_M) + return 1; + else { + spi_os_memcpy(&eof, + (char *)buf+SPI_DATA_EOF_OFFSET(length), + SPI_DATA_EOF_SIZE); + + if (eof != SPI_DATA_EOF) { + SPI_OS_ERROR(("%s %s\n", + "[SPI] ERROR : _spi_data_verify:", + "invalid")); + + return 0; + } + } + } else { + SPI_OS_ERROR(("%s %s\n", + "[SPI] ERROR : _spi_data_verify:", + "invalid : length error")); + + return 0; + } + } else { + SPI_OS_ERROR(("%s %s\n", + "[SPI] ERROR : _spi_data_verify:", + "invalid : bof error")); + + return 0; + } + + return 1; +} diff --git a/drivers/phone_svn/ipc_spi/spi_data.h b/drivers/phone_svn/ipc_spi/spi_data.h new file mode 100644 index 0000000..ceb23c8 --- /dev/null +++ b/drivers/phone_svn/ipc_spi/spi_data.h @@ -0,0 +1,129 @@ +#ifndef _SPI_DATA_H_ +#define _SPI_DATA_H_ + +#include "spi_dev.h" + +#define SPI_TASK_EVENT_COUNT 64 +#define SPI_DATA_BOF 0x7F +#define SPI_DATA_EOF 0x7E +#define SPI_DATA_FF_PADDING_HEADER 0xFFFFFFFF + +#define SPI_DATA_MUX_NORMAL_MASK 0x0F +#define SPI_DATA_MUX_MORE_H 0x10 +#define SPI_DATA_MUX_MORE_M 0x20 +#define SPI_DATA_MUX_MORE_T 0x30 + +#define SPI_DATA_MUX_SIZE 2 +#define SPI_DATA_LENGTH_SIZE 4 +#define SPI_DATA_BOF_SIZE 1 +#define SPI_DATA_INNER_LENGTH_SIZE 4 +#define SPI_DATA_IPC_INNER_LENGTH_SIZE 2 +#define SPI_DATA_IPC_INNER_CONTROL_SIZE 1 +#define SPI_DATA_EOF_SIZE 1 +#define SPI_DATA_HEADER_SIZE (SPI_DATA_MUX_SIZE+ \ + SPI_DATA_LENGTH_SIZE+SPI_DATA_BOF_SIZE+SPI_DATA_EOF_SIZE) +#define SPI_DATA_HEADER_SIZE_FRONT (SPI_DATA_MUX_SIZE+ \ + SPI_DATA_LENGTH_SIZE+SPI_DATA_BOF_SIZE) +#define SPI_DATA_IPC_INNER_HEADER_SIZE \ + (SPI_DATA_IPC_INNER_LENGTH_SIZE+ \ + SPI_DATA_IPC_INNER_CONTROL_SIZE) +#define SPI_DATA_SIZE(X) (SPI_DATA_HEADER_SIZE+X) + +#define SPI_DATA_LENGTH_OFFSET SPI_DATA_MUX_SIZE +#define SPI_DATA_BOF_OFFSET (SPI_DATA_LENGTH_OFFSET+ \ + SPI_DATA_LENGTH_SIZE) +#define SPI_DATA_DATA_OFFSET (SPI_DATA_BOF_OFFSET+ \ + SPI_DATA_BOF_SIZE) +#define SPI_DATA_EOF_OFFSET(X) (SPI_DATA_DATA_OFFSET+X) + +#define SPI_DATA_PACKET_HEADER_SIZE 4 +#define SPI_DATA_PACKET_MUX_ERROR_SPARE_SIZE 4 +#define SPI_DATA_PACKET_MAX_PACKET_BODY_SIZE \ + (SPI_DEV_MAX_PACKET_SIZE-SPI_DATA_PACKET_HEADER_SIZE- \ + SPI_DATA_PACKET_MUX_ERROR_SPARE_SIZE) + +#define SPI_DATA_MIN_SIZE (SPI_DATA_HEADER_SIZE*2) +#define SPI_DATA_MAX_SIZE_PER_PACKET (SPI_DEV_MAX_PACKET_SIZE- \ + SPI_DATA_PACKET_HEADER_SIZE-SPI_DATA_HEADER_SIZE- \ + SPI_DATA_PACKET_MUX_ERROR_SPARE_SIZE) +#define SPI_DATA_MAX_COUNT_PER_PACKET 240 + +#define SPI_DATA_IPC_QUEUE_SIZE 0x10000 +#define SPI_DATA_RAW_QUEUE_SIZE 0x100000 +#define SPI_DATA_RFS_QUEUE_SIZE 0x100000 + +#define SPI_DATA_IPC_DIV_BUFFER_SIZE 0x1000 +#define SPI_DATA_RAW_DIV_BUFFER_SIZE 0x1000 +#define SPI_DATA_RFS_DIV_BUFFER_SIZE 0x1000 + +#define SPI_DATA_DIVIDE_BUFFER_SIZE SPI_DEV_MAX_PACKET_SIZE + + + +enum SPI_DATA_TYPE_T { + SPI_DATA_MUX_IPC = 0x01, + SPI_DATA_MUX_RAW = 0x02, + SPI_DATA_MUX_RFS = 0x03, + SPI_DATA_MUX_CMD = 0x04, +}; + +struct spi_data_packet_header { + /* 12bit : packet size less than SPI_DEV_PACKET_SIZE */ + unsigned long current_data_size:31; + /* 1bit : packet division flag */ + unsigned long more:1; +}; + +enum SPI_DATA_QUEUE_TYPE_T { + SPI_DATA_QUEUE_TYPE_IPC_TX, /* 0 : formatted(IPC) tx buffer */ + SPI_DATA_QUEUE_TYPE_IPC_RX, /* 1 : formatted(IPC) rx buffer */ + SPI_DATA_QUEUE_TYPE_RAW_TX, /* 2 : raw tx buffer */ + SPI_DATA_QUEUE_TYPE_RAW_RX, /* 3 : raw rx buffer */ + SPI_DATA_QUEUE_TYPE_RFS_TX, /* 4 : rfs tx buffer */ + SPI_DATA_QUEUE_TYPE_RFS_RX, /* 5 : rfs rx buffer */ + SPI_DATA_QUEUE_TYPE_NB /* 6 : number of buffer */ +}; + +struct spi_data_queue { + unsigned int tail; /* circular queue header */ + unsigned int head; /* circular queue tail */ +}; + +struct spi_data_queue_info { + struct spi_data_queue *header; + enum SPI_DATA_TYPE_T type; + unsigned int buf_size; /* queue size */ + char *buffer; /* queue data */ +}; + +struct spi_data_div_buf { + unsigned int length; + char *buffer; /* queue data */ +}; + +extern void *ipc_spi_get_queue_buff(void); + +extern struct spi_data_queue *spi_data_queue; +extern struct spi_data_queue_info *spi_queue_info; +extern char *gspi_data_packet_buf; +extern char *gspi_data_sync_buf; + +extern void spi_data_queue_init(void); +extern void spi_data_queue_destroy(void); +extern unsigned int spi_data_dequeue(struct spi_data_queue_info *queue_info, + void *pdata); +extern int spi_data_inqueue(struct spi_data_queue_info *queue, void *data, + unsigned int length); +extern void spi_data_msg_inqueuefront(enum SPI_MAIN_MSG_T task_msg); +extern int spi_data_queue_is_empty(enum SPI_DATA_QUEUE_TYPE_T type); +extern int spi_data_div_buf_is_empty(enum SPI_DATA_QUEUE_TYPE_T type); +extern int spi_data_check_tx_queue(void); + +extern int spi_data_prepare_tx_packet(void *buf); +extern int spi_data_parsing_rx_packet(void *buf, unsigned int length); + +extern void spi_data_print_raw_data(void *data, unsigned int length); +#endif + + + diff --git a/drivers/phone_svn/ipc_spi/spi_dev.c b/drivers/phone_svn/ipc_spi/spi_dev.c new file mode 100644 index 0000000..54746d9 --- /dev/null +++ b/drivers/phone_svn/ipc_spi/spi_dev.c @@ -0,0 +1,345 @@ +/************************************************************** + + spi_dev.c + + adapt device api and spi api + + This is MASTER side. + +***************************************************************/ + + + +/************************************************************** + + Preprocessor by common + +***************************************************************/ + +#include "spi_main.h" +#include "spi_dev.h" +#include "spi_os.h" + + + +/************************************************************** + + Preprocessor by platform + (OMAP4430 && MSM7X30) + +***************************************************************/ + +#include <linux/spi/spi.h> +#include <linux/interrupt.h> +#include <linux/gpio.h> +#include <linux/phone_svn/ipc_spi.h> + + + +/************************************************************** + + Definition of Variables and Functions by common + +***************************************************************/ + +int spi_dev_gpio_mrdy; +int spi_dev_gpio_srdy; +int spi_dev_gpio_submrdy; +int spi_dev_gpio_subsrdy; + + + +/************************************************************** + + Preprocessor by platform + (OMAP4430 && MSM7X30) + +***************************************************************/ + +int spi_is_restart; + + + +/********************************************************** + +Prototype void spi_dev_init ( void * data) + +Type function + +Description init spi gpio info + +Param input (none) + +Return value (none) + +***********************************************************/ +void spi_dev_init(void *data) +{ + struct ipc_spi_platform_data *pdata = NULL; + + + SPI_OS_TRACE(("[SPI] spi_dev_init\n")); + + + pdata = (struct ipc_spi_platform_data *)data; + + spi_dev_gpio_mrdy = (int) pdata->gpio_ipc_mrdy; + spi_dev_gpio_srdy = (int) pdata->gpio_ipc_srdy; + spi_dev_gpio_submrdy = (int) pdata->gpio_ipc_sub_mrdy; + spi_dev_gpio_subsrdy = (int) pdata->gpio_ipc_sub_srdy; + + spi_dev_set_gpio(spi_dev_gpio_mrdy, SPI_DEV_GPIOLEVEL_LOW); + if (spi_is_restart == 0) + spi_dev_set_gpio(spi_dev_gpio_submrdy, SPI_DEV_GPIOLEVEL_LOW); +} + + +/********************************************************** + +Prototype void spi_dev_destroy( void ) + +Type function + +Description unregister irq handler + +Param input (none) + +Return value (none) + +***********************************************************/ + +void spi_dev_destroy(void) +{ + spi_dev_unreigster_irq_handler(spi_dev_gpio_srdy, 0); + spi_dev_unreigster_irq_handler(spi_dev_gpio_subsrdy, 0); +} + + + +/********************************************************** + +Prototype int spi_dev_send + +Type function + +Description starting data send by DMA(CP side) + starting send clock for data switching(AP side) + +Param input buf : data for send + length : data size. this lengt must be fixed + +Return value 0 : success + (others) : error cause + +***********************************************************/ + +int spi_dev_send(void *buf, void *sync_buf, unsigned int length) +{ + int result = 0; + + result = ipc_spi_tx_rx_sync(buf, sync_buf, length); + + return result; +} + + +/********************************************************** + +Prototype int spi_dev_receive + +Type function + +Description starting data receive by DMA(CP side) + starting send clock for data switching(AP side) + +Param input buf : buffer for saving data + length : data size. this lengt must be fixed + +Return value 0 : success + (others) : error cause + +***********************************************************/ + +int spi_dev_receive(void *sync_buf, void *buf, unsigned int length) +{ + int value = 0; + + value = ipc_spi_tx_rx_sync(sync_buf, buf, length); + + return value; +} + + +/********************************************************** + +Prototype void spi_dev_set_gpio + +Type function + +Description set gpio pin state + +Param input gpio_id : number of gpio id + value : SPI_DEV_GPIOLEVEL_HIGH for raising pin state up + : SPI_DEV_GPIOLEVEL_LOW for getting pin state down + +Return value (none) + +***********************************************************/ +void spi_dev_set_gpio(int gpio_id, enum SPI_DEV_GPIOLEVEL_T value) +{ + int level = 0; + + SPI_OS_TRACE_MID(("%s gpio_id =[%d], value =[%d]\n", + "[SPI] spi_dev_set_gpio :", gpio_id, (int) value)); + + switch (value) { + case SPI_DEV_GPIOLEVEL_LOW: + level = 0; + break; + case SPI_DEV_GPIOLEVEL_HIGH: + level = 1; + break; + } + + gpio_set_value((unsigned int) gpio_id, level); +} + + + +/********************************************************** + +Prototype SPI_DEV_GPIOLEVEL_T spi_dev_get_gpio(int gpio_id) + +Type function + +Description get gpio pin state + +Param input gpio_id : number of gpio id + +Return value SPI_DEV_GPIOLEVEL + +***********************************************************/ +enum SPI_DEV_GPIOLEVEL_T spi_dev_get_gpio(int gpio_id) +{ + enum SPI_DEV_GPIOLEVEL_T value = SPI_DEV_GPIOLEVEL_LOW; + int level = 0; + + level = gpio_get_value((unsigned int) gpio_id); + + switch (level) { + case 0: + value = SPI_DEV_GPIOLEVEL_LOW; + break; + case 1: + value = SPI_DEV_GPIOLEVEL_HIGH; + break; + } + + return value; +} + + +/********************************************************** + +Prototype int spi_dev_reigster_irq_handler + +Type function + +Description regist irq callback function to each gpio interrupt + +Param input gpio_id : gpio pin id + tigger : interrupt detection mode + handler : interrupt handler function + name : register name + +Return value 0 : fail + 1 : success + +***********************************************************/ +int spi_dev_reigster_irq_handler(int gpio_id, + enum SPI_DEV_IRQ_TRIGGER_T trigger, + SPI_DEV_IRQ_HANDLER_T handler, + char *name, void *data) +{ +#if defined(SPI_FEATURE_OMAP4430) + int value = 0; + int _trigger = IRQF_TRIGGER_NONE; + + switch (trigger) { + case SPI_DEV_IRQ_TRIGGER_RISING: + _trigger = IRQF_TRIGGER_RISING; + break; + case SPI_DEV_IRQ_TRIGGER_FALLING: + _trigger = IRQF_TRIGGER_FALLING; + break; + default: + _trigger = IRQF_TRIGGER_NONE; + break; + } + + value = request_irq(OMAP_GPIO_IRQ(gpio_id), handler, + _trigger, name, data); + + if (value != 0) { + SPI_OS_ERROR(("%s regist fail(%d)", + "[SPI] ERROR : spi_dev_reigster_irq_handler :", + value)); + return 0; + } + +#elif defined(SPI_FEATURE_S5PC210) + int value = 0; + + int _trigger = IRQF_TRIGGER_NONE; + int irq = 0; + + + switch (trigger) { + case SPI_DEV_IRQ_TRIGGER_RISING: + _trigger = IRQF_TRIGGER_RISING; + break; + case SPI_DEV_IRQ_TRIGGER_FALLING: + _trigger = IRQF_TRIGGER_FALLING; + break; + default: + _trigger = IRQF_TRIGGER_NONE; + break; + } + + irq = gpio_to_irq(gpio_id); + value = request_irq(irq, handler, _trigger, name, data); + if (value != 0) { + SPI_OS_ERROR(("spi_dev_reigster_irq_handler: regist fail(%d)", + value)); + return 0; + } + enable_irq_wake(irq); +#endif + + return 1; +} + + +/********************************************************** + +void spi_dev_unreigster_irq_handler (int gpio_id, void * data) + +Type function + +Description unregister irq hanger by gpio api + +Param input gpio_id : gpio pin id + data : param + +Return value (none) + +***********************************************************/ + +void spi_dev_unreigster_irq_handler(int gpio_id, void *data) +{ +#if defined(SPI_FEATURE_OMAP4430) + free_irq(OMAP_GPIO_IRQ(gpio_id), data); +#elif defined(SPI_FEATURE_S5PC210) + free_irq(gpio_to_irq(gpio_id), data); +#endif +} diff --git a/drivers/phone_svn/ipc_spi/spi_dev.h b/drivers/phone_svn/ipc_spi/spi_dev.h new file mode 100644 index 0000000..6da9e64 --- /dev/null +++ b/drivers/phone_svn/ipc_spi/spi_dev.h @@ -0,0 +1,49 @@ +#ifndef _SPI_DEV_H_ +#define _SPI_DEV_H_ + +#include "spi_main.h" + +#include <linux/interrupt.h> + +#define SPI_DEV_MAX_PACKET_SIZE (2048 * 6) + +enum SPI_DEV_SYNC_STATE_T { + SPI_DEV_SYNC_OFF = 0, + SPI_DEV_SYNC_ON +}; + +enum SPI_DEV_GPIOLEVEL_T { + SPI_DEV_GPIOLEVEL_LOW = 0, + SPI_DEV_GPIOLEVEL_HIGH +}; + +enum SPI_DEV_IRQ_TRIGGER_T { + SPI_DEV_IRQ_TRIGGER_RISING = 1, + SPI_DEV_IRQ_TRIGGER_FALLING +}; + +typedef irqreturn_t (*SPI_DEV_IRQ_HANDLER_T)(int, void *); +#define SPI_DEV_IRQ_HANDLER(X) irqreturn_t X(int irq, void *data) + +extern int spi_dev_gpio_mrdy; +extern int spi_dev_gpio_srdy; +extern int spi_dev_gpio_submrdy; +extern int spi_dev_gpio_subsrdy; + +extern int spi_is_restart; + +extern void spi_dev_init(void *data); +extern void spi_dev_destroy(void); +extern int spi_dev_send(void *buf, void * sync_buf, unsigned int length); +extern int spi_dev_receive(void *sync_buf, void *buf, unsigned int length); +extern void spi_dev_set_gpio(int gpio_id, enum SPI_DEV_GPIOLEVEL_T value); +extern enum SPI_DEV_GPIOLEVEL_T spi_dev_get_gpio(int gpio_id); +extern int spi_dev_reigster_irq_handler(int gpio_id, + enum SPI_DEV_IRQ_TRIGGER_T trigger, + SPI_DEV_IRQ_HANDLER_T handler, + char *name, void *data); +extern void spi_dev_unreigster_irq_handler(int gpio_id, void *data); + +extern int ipc_spi_tx_rx_sync(u8 *tx_d, u8 *rx_d, unsigned len); + +#endif diff --git a/drivers/phone_svn/ipc_spi/spi_main.c b/drivers/phone_svn/ipc_spi/spi_main.c new file mode 100644 index 0000000..6b104fa --- /dev/null +++ b/drivers/phone_svn/ipc_spi/spi_main.c @@ -0,0 +1,563 @@ +/************************************************************** + spi_main.c + + process of spi + + This is MASTER side. +***************************************************************/ + +/************************************************************** + Preprocessor by common +***************************************************************/ + +#include "spi_main.h" +#include "spi_os.h" +#include "spi_dev.h" +#include "spi_app.h" +#include "spi_data.h" +#include "spi_test.h" + +/************************************************************** + Preprocessor by platform + (OMAP4430 && MSM7X30) +***************************************************************/ +#include <linux/workqueue.h> +#include <linux/wakelock.h> +#include <linux/semaphore.h> +static struct wake_lock spi_wakelock; + +/************************************************************** + Definition of Variables and Functions by common +***************************************************************/ +enum SPI_MAIN_STATE_T spi_main_state = SPI_MAIN_STATE_END; + + + +static int _start_data_send(void); + +static void _start_packet_tx(void); + +static void _start_packet_tx_send(void); + +static void _start_packet_receive(void); + + + + +/************************************************************** + Definition of Variables and Functions by platform +***************************************************************/ + +#define MRDY_GUARANTEE_MRDY_TIME_GAP 0 /* under 1ms */ + +#define MRDY_GUARANTEE_MRDY_TIME_SLEEP 2 + +#define MRDY_GUARANTEE_MRDY_LOOP_COUNT 10000 /* 120us */ + + +static void spi_main_process(struct work_struct *pspi_wq); + + + +#ifdef SPI_GUARANTEE_MRDY_GAP + +unsigned long mrdy_low_time_save, mrdy_high_time_save; + +int check_mrdy_time_gap(unsigned long x, unsigned long y) +{ + if (x == y) + return 1; + else if ((x < y) && ((y-x) <= MRDY_GUARANTEE_MRDY_TIME_GAP)) + return 1; + else if ((x > y)) + return 1; + else + return 0; +} +#endif + + +/*===================================== +* Description set state of spi + (Common) +=====================================*/ +void spi_main_set_state(enum SPI_MAIN_STATE_T state) +{ + spi_main_state = state; + SPI_OS_TRACE_MID(("[SPI] spi_main_set_state %d=>%d\n", + (int)spi_main_state, (int)state)); +} + + +/*===================================== +* Description an interrupt handler for mrdy rising +=====================================*/ +SPI_DEV_IRQ_HANDLER(spi_main_mrdy_rising_handler) +{ + irqreturn_t result = IRQ_HANDLED; + + return result; +} + + +/*===================================== +//Description an interrupt handler for srdy rising +=====================================*/ +SPI_DEV_IRQ_HANDLER(spi_main_srdy_rising_handler) +{ + irqreturn_t result = IRQ_HANDLED; + + if (!boot_done) + return result; + + if (!wake_lock_active(&spi_wakelock)) { + wake_lock(&spi_wakelock); + SPI_OS_TRACE_MID(("[SPI] [%s](%d) spi_wakelock locked .\n", + __func__, __LINE__)); + } + + if (send_modem_spi == 1) + up(&srdy_sem); /* signal srdy event */ + + /* SRDY interrupt work on SPI_MAIN_STATE_IDLE state for receive data */ + if (spi_main_state == SPI_MAIN_STATE_IDLE + || spi_main_state == SPI_MAIN_STATE_RX_TERMINATE + || spi_main_state == SPI_MAIN_STATE_TX_TERMINATE) { + spi_main_send_signalfront(SPI_MAIN_MSG_RECEIVE); + return result; + } + + return result; +} + + +/*===================================== +* Description an interrupt handler for submrdy rising +=====================================*/ +SPI_DEV_IRQ_HANDLER(spi_main_submrdy_rising_handler) +{ + irqreturn_t result = IRQ_HANDLED; + + return result; +} + + +/*===================================== +* Description an interrupt handler for subsrdy rising +=====================================*/ +SPI_DEV_IRQ_HANDLER(spi_main_subsrdy_rising_handler) +{ + irqreturn_t result = IRQ_HANDLED; + + /* SRDY interrupt work on SPI_MAIN_STATE_TX_WAIT state for send data */ + if (spi_main_state == SPI_MAIN_STATE_TX_WAIT) + return result; + + SPI_OS_TRACE_MID(("%s spi_main_state[%d]\n", + "[SPI] spi_main_subsrdy_rising_handler :", + (int)spi_main_state)); + + return result; +} + + +/*===================================== +* Description Send the signal to SPI Task +=====================================*/ +void spi_main_send_signal(enum SPI_MAIN_MSG_T spi_sigs) +{ + struct spi_work *spi_wq = NULL; + spi_wq = spi_os_malloc(sizeof(struct spi_work)); + spi_wq->signal_code = spi_sigs; + INIT_WORK(&spi_wq->work, spi_main_process); + queue_work(ipc_spi_wq, (struct work_struct *)spi_wq); +} + + +/*===================================== +* Description Send the signal to SPI Task +=====================================*/ +void spi_main_send_signalfront(enum SPI_MAIN_MSG_T spi_sigs) +{ + struct spi_work *spi_wq = NULL; + spi_wq = spi_os_malloc(sizeof(struct spi_work)); + spi_wq->signal_code = spi_sigs; + INIT_WORK(&spi_wq->work, spi_main_process); + queue_work_front(ipc_spi_wq, (struct work_struct *)spi_wq); +} + + +/*===================================== +* Description check each queue data and start send routine +=====================================*/ +static int _start_data_send(void) +{ + if (spi_data_check_tx_queue() == 1) { + spi_main_send_signal(SPI_MAIN_MSG_SEND); + return 1; + } + + return 0; +} + + +/*===================================== +* Description start to send packet data +=====================================*/ +static void _start_packet_tx(void) +{ + int i = 0; + + if (spi_data_check_tx_queue() == 0) + return; + + + /* check SUB SRDY state */ + if (spi_dev_get_gpio(spi_dev_gpio_subsrdy) == + SPI_DEV_GPIOLEVEL_HIGH) { + spi_main_send_signal(SPI_MAIN_MSG_SEND); + return; + } + + /* check SRDY state */ + if (spi_dev_get_gpio(spi_dev_gpio_srdy) == + SPI_DEV_GPIOLEVEL_HIGH) { + spi_main_send_signal(SPI_MAIN_MSG_SEND); + return; + } + + if (get_console_suspended()) { + spi_main_send_signal(SPI_MAIN_MSG_SEND); + return; + } + + /* change state SPI_MAIN_STATE_IDLE to SPI_MAIN_STATE_TX_WAIT */ + spi_main_state = SPI_MAIN_STATE_TX_WAIT; + +#ifdef SPI_GUARANTEE_MRDY_GAP + mrdy_high_time_save = spi_os_get_tick(); + if (check_mrdy_time_gap(mrdy_low_time_save, mrdy_high_time_save)) + spi_os_sleep(1); +#endif + + spi_dev_set_gpio(spi_dev_gpio_mrdy, SPI_DEV_GPIOLEVEL_HIGH); + + /* check SUBSRDY state */ + while (spi_dev_get_gpio(spi_dev_gpio_subsrdy) == + SPI_DEV_GPIOLEVEL_LOW) { + if (i == 0) { + spi_os_sleep(1); + i++; + continue; + } else + spi_os_sleep(MRDY_GUARANTEE_MRDY_TIME_SLEEP); + + i++; + if (i > MRDY_GUARANTEE_MRDY_TIME_SLEEP * 20) { + SPI_OS_ERROR(("%s spi_main_state=[%d]\n", + "[SPI] === spi Fail to receiving SUBSRDY CONF :", + (int)spi_main_state)); + + spi_dev_set_gpio(spi_dev_gpio_mrdy, + SPI_DEV_GPIOLEVEL_LOW); +#ifdef SPI_GUARANTEE_MRDY_GAP + mrdy_low_time_save = spi_os_get_tick(); +#endif + /* change state SPI_MAIN_STATE_TX_WAIT */ + /* to SPI_MAIN_STATE_IDLE */ + if (spi_main_state != SPI_MAIN_STATE_START + && spi_main_state != SPI_MAIN_STATE_END + && spi_main_state != SPI_MAIN_STATE_INIT) { + spi_main_state = SPI_MAIN_STATE_IDLE; + _start_data_send(); + } + return; + } + } + if (spi_main_state != SPI_MAIN_STATE_START + && spi_main_state != SPI_MAIN_STATE_END + && spi_main_state != SPI_MAIN_STATE_INIT) { + _start_packet_tx_send(); + } else + SPI_OS_ERROR(("[SPI] ERR : _start_packet_tx:spi_main_state[%d]", + (int)spi_main_state)); + + return; +} + + +/*===================================== +* Description send data +=====================================*/ +static void _start_packet_tx_send(void) +{ + char *spi_packet_buf = NULL; + char *spi_sync_buf = NULL; + + /* change state SPI_MAIN_STATE_TX_WAIT to SPI_MAIN_STATE_TX_SENDING */ + spi_main_state = SPI_MAIN_STATE_TX_SENDING; + + spi_packet_buf = gspi_data_packet_buf; + spi_sync_buf = gspi_data_sync_buf; + + spi_os_memset(spi_packet_buf, 0, SPI_DEV_MAX_PACKET_SIZE); + spi_os_memset(spi_sync_buf, 0, SPI_DEV_MAX_PACKET_SIZE); + + spi_data_prepare_tx_packet(spi_packet_buf); + if (spi_dev_send((void *)spi_packet_buf, (void *)spi_sync_buf, + SPI_DEV_MAX_PACKET_SIZE) != 0) { + /* TODO: save failed packet */ + /* back data to each queue */ + SPI_OS_ERROR(("[SPI] spi_dev_send fail\n")); + panic("[SPI] spi_dev_send\n"); + } + + spi_main_state = SPI_MAIN_STATE_TX_TERMINATE; + + spi_dev_set_gpio(spi_dev_gpio_mrdy, SPI_DEV_GPIOLEVEL_LOW); +#ifdef SPI_GUARANTEE_MRDY_GAP + mrdy_low_time_save = spi_os_get_tick(); +#endif + + /* change state SPI_MAIN_STATE_TX_SENDING to SPI_MAIN_STATE_IDLE */ + spi_main_state = SPI_MAIN_STATE_IDLE; + _start_data_send(); +} + + +/*===================================== +* Description start to receive packet data +=====================================*/ +static void _start_packet_receive(void) +{ + char *spi_packet_buf = NULL; + char *spi_sync_buf = NULL; + struct spi_data_packet_header spi_receive_packet_header = {0, }; + int i = 0; + + if (!wake_lock_active(&spi_wakelock)) + return; + + if (spi_dev_get_gpio(spi_dev_gpio_srdy) == SPI_DEV_GPIOLEVEL_LOW) + return; + + if (get_console_suspended()) + return; + + spi_main_state = SPI_MAIN_STATE_RX_WAIT; + + + spi_dev_set_gpio(spi_dev_gpio_submrdy, SPI_DEV_GPIOLEVEL_HIGH); + + + /* check SUBSRDY state */ + while (spi_dev_get_gpio(spi_dev_gpio_subsrdy) == + SPI_DEV_GPIOLEVEL_LOW) { + if (i == 0) { + spi_os_sleep(1); + i++; + continue; + } else + spi_os_sleep(MRDY_GUARANTEE_MRDY_TIME_SLEEP); + + i++; + if (i > MRDY_GUARANTEE_MRDY_TIME_SLEEP * 20) { + SPI_OS_ERROR(("[SPI] ERROR(Failed MASTER RX:%d/%d)", + i, MRDY_GUARANTEE_MRDY_TIME_SLEEP * 20)); + if (spi_main_state != SPI_MAIN_STATE_START + && spi_main_state != SPI_MAIN_STATE_END + && spi_main_state != SPI_MAIN_STATE_INIT) { + spi_dev_set_gpio(spi_dev_gpio_submrdy, + SPI_DEV_GPIOLEVEL_LOW); + + /* change state SPI_MAIN_STATE_RX_WAIT */ + /* to SPI_MAIN_STATE_IDLE */ + spi_main_state = SPI_MAIN_STATE_IDLE; + } + return; + } + } + + if (spi_main_state == SPI_MAIN_STATE_START + || spi_main_state == SPI_MAIN_STATE_END + || spi_main_state == SPI_MAIN_STATE_INIT) + return; + + spi_packet_buf = gspi_data_packet_buf; + spi_sync_buf = gspi_data_sync_buf; + + spi_os_memset(spi_packet_buf, 0, SPI_DEV_MAX_PACKET_SIZE); + spi_os_memset(spi_sync_buf, 0, SPI_DEV_MAX_PACKET_SIZE); + + if (spi_dev_receive((void *)spi_sync_buf, (void *)spi_packet_buf, + SPI_DEV_MAX_PACKET_SIZE) == 0) { + /* parsing SPI packet */ + spi_os_memcpy(&spi_receive_packet_header, spi_packet_buf, + SPI_DATA_PACKET_HEADER_SIZE); + + if (spi_data_parsing_rx_packet((void *)spi_packet_buf, + spi_receive_packet_header.current_data_size) > 0) { + /* call function for send data to IPC, RAW, RFS */ + spi_send_msg_to_app(); + } + } else{ + SPI_OS_ERROR(("[SPI] spi_dev_receive fail\n")); + panic("[SPI] spi_dev_receive\n"); + } + + spi_main_state = SPI_MAIN_STATE_RX_TERMINATE; + + spi_dev_set_gpio(spi_dev_gpio_submrdy, SPI_DEV_GPIOLEVEL_LOW); + + /* change state SPI_MAIN_STATE_RX_WAIT to SPI_MAIN_STATE_IDLE */ + spi_main_state = SPI_MAIN_STATE_IDLE; + _start_data_send(); + + return; +} + + +/*===================================== +* Description creating a task +=====================================*/ +void spi_main_init(void *data) +{ + struct ipc_spi_platform_data *pdata; + + SPI_OS_TRACE(("[SPI] spi_main_init\n")); + + spi_main_state = SPI_MAIN_STATE_START; + + + pdata = (struct ipc_spi_platform_data *)data; + + wake_lock_init(&spi_wakelock, WAKE_LOCK_SUSPEND, + "samsung-spiwakelock"); + + spi_dev_init(pdata); + + spi_dev_reigster_irq_handler (spi_dev_gpio_srdy, + SPI_DEV_IRQ_TRIGGER_RISING, + spi_main_srdy_rising_handler, + "spi_srdy_rising", 0); + spi_dev_reigster_irq_handler (spi_dev_gpio_subsrdy, + SPI_DEV_IRQ_TRIGGER_RISING, + spi_main_subsrdy_rising_handler, + "spi_subsrdy_rising", 0); +} + + +/*===================================== +* Description main process +=====================================*/ + +static void spi_main_process(struct work_struct *work) +{ + int signal_code; + + struct spi_work *spi_wq = container_of(work, struct spi_work, work); + signal_code = spi_wq->signal_code; + + + if (spi_main_state == SPI_MAIN_STATE_END + || spi_main_state == SPI_MAIN_STATE_START) { + spi_os_free(spi_wq); + return; + } + + switch (signal_code) { + case SPI_MAIN_MSG_SEND: + if (spi_main_state == SPI_MAIN_STATE_IDLE) + _start_packet_tx(); + + break; + + case SPI_MAIN_MSG_PACKET_SEND: + _start_packet_tx_send(); + break; + + case SPI_MAIN_MSG_RECEIVE: + if (spi_main_state == SPI_MAIN_STATE_IDLE + || spi_main_state == SPI_MAIN_STATE_RX_TERMINATE + || spi_main_state == SPI_MAIN_STATE_TX_TERMINATE) + _start_packet_receive(); + break; + + /* Receive data from IPC,RAW,RFS. */ + /* It need analyze and save to tx queue */ + case SPI_MAIN_MSG_IPC_SEND: + case SPI_MAIN_MSG_RAW_SEND: + case SPI_MAIN_MSG_RFS_SEND: + /* start send data during SPI_MAIN_STATE_IDLE state */ + if (spi_main_state == SPI_MAIN_STATE_IDLE) + _start_data_send(); + break; + + default: + SPI_OS_ERROR(("[SPI] ERROR(No signal_code in spi_main[%d])\n", + signal_code)); + break; + } + + spi_os_free(spi_wq); + if (wake_lock_active(&spi_wakelock)) { + wake_unlock(&spi_wakelock); + SPI_OS_TRACE_MID(("[SPI] [%s](%d) spi_wakelock unlocked .\n", + __func__, __LINE__)); + } +} + + +/*===================================== +//Description main task +=====================================*/ +void spi_main(unsigned long argc, void *argv) +{ + /* queue init */ + spi_data_queue_init(); + + SPI_OS_TRACE(("[SPI] spi_main %x\n", (unsigned int)argv)); + + spi_dev_set_gpio(spi_dev_gpio_submrdy, SPI_DEV_GPIOLEVEL_HIGH); + + do { + spi_os_sleep(30); + } while (spi_dev_get_gpio(spi_dev_gpio_subsrdy) == + SPI_DEV_GPIOLEVEL_LOW); + + if (spi_is_restart) + spi_os_sleep(100); + spi_dev_set_gpio(spi_dev_gpio_submrdy, SPI_DEV_GPIOLEVEL_LOW); + spi_main_state = SPI_MAIN_STATE_IDLE; + spi_is_restart = 0; +} + + +/*===================================== +* Description spi restart for CP slient reset +=====================================*/ + +void spi_set_restart(void) +{ + SPI_OS_ERROR(("[SPI] spi_set_restart(spi_main_state=[%d])\n", + (int)spi_main_state)); + + spi_dev_set_gpio(spi_dev_gpio_submrdy, SPI_DEV_GPIOLEVEL_LOW); + + spi_main_state = SPI_MAIN_STATE_END; + + spi_is_restart = 1; + + flush_workqueue(ipc_spi_wq); + + spi_data_queue_destroy(); + + spi_dev_destroy(); + + if (wake_lock_active(&spi_wakelock)) { + wake_unlock(&spi_wakelock); + SPI_OS_TRACE_MID(("[SPI] [%s](%d) spi_wakelock unlocked.\n", + __func__, __LINE__)); + } + wake_lock_destroy(&spi_wakelock); +} diff --git a/drivers/phone_svn/ipc_spi/spi_main.h b/drivers/phone_svn/ipc_spi/spi_main.h new file mode 100644 index 0000000..b15465a --- /dev/null +++ b/drivers/phone_svn/ipc_spi/spi_main.h @@ -0,0 +1,80 @@ +#ifndef __SPI_MAIN_H__ +#define __SPI_MAIN_H__ + +#define SPI_FEATURE_S5PC210 +#define SPI_FEATURE_MASTER + +#define SPI_FEATURE_NOLOG +/* #define SPI_FEATURE_DEBUG */ +#define SPI_GUARANTEE_MRDY_GAP + +/* +#define SPI_FEATURE_TEST_SCENARIO SPI_TEST_SCENARIO_MASTER_SENDING +#define SPI_FEATURE_TEST_SCENARIO SPI_TEST_SCENARIO_SLAVE_SENDING +#define SPI_FEATURE_TEST_SCENARIO SPI_TEST_SCENARIO_PHYSICAL +#define SPI_FEATURE_TEST_SCENARIO SPI_TEST_SCENARIO_NONE +#define SPI_FEATURE_TEST_DURATION 1000 +*/ + +#define SPI_SLAVE_TX 0 +#define SPI_SLAVE_RX 0 +#define SPI_MASTER_TX 0 +#define SPI_MASTER_RX 0 + +#include "spi_os.h" + +enum SPI_MAIN_MSG_T { + SPI_MAIN_MSG_BASE, + SPI_MAIN_MSG_SEND, + SPI_MAIN_MSG_RECEIVE, + SPI_MAIN_MSG_PACKET_SEND, + SPI_MAIN_MSG_PACKET_RECEIVE, + SPI_MAIN_MSG_IPC_SEND, /* IPC sends message to SPI */ + SPI_MAIN_MSG_RAW_SEND, /* RAW sends message to SPI */ + SPI_MAIN_MSG_RFS_SEND /* RFS sends message to SPI */ +}; + +enum SPI_MAIN_STATE_T { + SPI_MAIN_STATE_START, /* before init complete */ + SPI_MAIN_STATE_INIT, /* initialising */ + SPI_MAIN_STATE_IDLE, /* suspend. Waiting for event */ + + /* state to start tx. Become from idle */ + SPI_MAIN_STATE_TX_START, + SPI_MAIN_STATE_TX_CONFIRM, + + /* (in case of master) */ + /* wait srdy rising interrupt to check slave preparing */ + /* (in case of slave) */ + /* wait submrdy rising interrupt to check sync complete */ + SPI_MAIN_STATE_TX_WAIT, + SPI_MAIN_STATE_TX_SENDING, /* tx data sending */ + SPI_MAIN_STATE_TX_TERMINATE, + SPI_MAIN_STATE_TX_MORE, + + /* in case of slave, wait submrdy rising interrupt to */ + SPI_MAIN_STATE_RX_WAIT, + + /* check sync complete then it starts to read buffer */ + SPI_MAIN_STATE_RX_MORE, + SPI_MAIN_STATE_RX_TERMINATE, + SPI_MAIN_STATE_END /* spi task is stopped */ +}; + +extern struct workqueue_struct *ipc_spi_wq; +extern int send_modem_spi; +extern int boot_done; +extern struct semaphore srdy_sem; + + +extern enum SPI_MAIN_STATE_T spi_main_state; + +extern void spi_main(unsigned long argc, void *argv); +extern void spi_main_init(void *data); +extern void spi_main_send_signal(enum SPI_MAIN_MSG_T spi_sigs); +extern void spi_main_send_signalfront(enum SPI_MAIN_MSG_T spi_sigs); +extern void spi_set_restart(void); +extern int get_console_suspended(void); + +#endif + diff --git a/drivers/phone_svn/ipc_spi/spi_os.c b/drivers/phone_svn/ipc_spi/spi_os.c new file mode 100644 index 0000000..f1d2550 --- /dev/null +++ b/drivers/phone_svn/ipc_spi/spi_os.c @@ -0,0 +1,634 @@ +/************************************************************** + spi_os.c + + adapt os api and spi api + + This is MATER side. +***************************************************************/ + +/************************************************************** + Preprocessor by common +***************************************************************/ + +#include "spi_main.h" +#include "spi_os.h" + + +/************************************************************** + Preprocessor by platform + (Android) +***************************************************************/ + +#include <linux/vmalloc.h> +#include <linux/spi/spi.h> +#include <linux/gpio.h> +#include <linux/delay.h> +#include <linux/in.h> +#include <linux/time.h> + +/********************************************************** +Prototype void * spi_os_malloc ( unsigned int length ) + +Type function + +Description allocate memory + +Param input length : length of size + +Return value 0 : fail + (other) : address of memory allocated +***********************************************************/ +void *spi_os_malloc(unsigned int length) +{ + if (length <= 0) { + SPI_OS_ERROR(("[SPI] ERROR : spi_os_malloc fail : len 0\n")); + return 0; + } + + return kmalloc(length, GFP_ATOMIC); +} + + +/*==================================== +Prototype void * spi_os_vmalloc ( unsigned int length ) + +Type function + +Description allocate memory with vmalloc + +Param input length : length of size + +Return value 0 : fail + (other) : address of memory allocated +====================================*/ +void *spi_os_vmalloc(unsigned int length) +{ + if (length <= 0) { + SPI_OS_ERROR(("spi_os_malloc fail : length is 0\n")); + return 0; + } + return vmalloc(length); +} + + +/********************************************************** +Prototype int spi_os_free ( void * addr ) + +Type function + +Description free memory + +Param input addr : address of memory to be released + +Return value 0 : fail + 1 : success +***********************************************************/ +int spi_os_free(void *addr) +{ + if (addr == 0) { + SPI_OS_ERROR(("[SPI] ERROR : spi_os_free fail : addr is 0\n")); + return 0; + } + + kfree(addr); + return 1; +} + + +/********************************************************** +Prototype int spi_os_vfree ( void * addr ) + +Type function + +Description free memory with vfree + +Param input addr : address of memory to be released + +Return value 0 : fail + 1 : success +***********************************************************/ +int spi_os_vfree(void *addr) +{ + if (addr == 0) { + SPI_OS_ERROR(("spi_os_free fail : addr is 0\n")); + return 0; + } + +#ifdef SPI_FEATURE_OMAP4430 + vfree(addr); +#elif defined SPI_FEATURE_SC8800G + SCI_FREE(addr); +#endif + + return 1; +} + + +/********************************************************** +Prototype int spi_os_memcpy ( void * dest, void * src, unsigned int length ) + +Type function + +Description copy memory + +Param input dest : address of memory to be save + src : address of memory to copy + length : length of memory to copy + +Return value 0 : fail + 1 : success +***********************************************************/ +int spi_os_memcpy(void *dest, void *src, unsigned int length) +{ + if (dest == 0 || src == 0) { + SPI_OS_ERROR(("[SPI] ERROR : spi_os_memcpy fail\n")); + return 0; + } + + memcpy(dest, src, length); + + return 1; +} + + +/********************************************************** +Prototype void * spi_os_memset ( void * addr, int value, unsigned int length ) + +Type function + +Description set memory as parameter + +Param input addr : address of memory to be set + value : value to set + length : length of memory to be set + +Return value (none) +***********************************************************/ +void *spi_os_memset(void *addr, int value, unsigned int length) +{ + if (addr == 0) { + SPI_OS_ERROR(("[SPI] ERROR : spi_os_memcpy fail\n")); + return 0; + } + + return memset(addr, value, length); +} + + +/********************************************************** +Prototype int spi_os_create_mutex( char * name, unsigned int priority_inherit ) + +Type function + +Description create mutex + +Param input name : name of mutex + priority_inherit : mutex inheritance + +Return value id of mutex created +***********************************************************/ +int spi_os_create_mutex(char *name, unsigned int priority_inherit) +{ + if (name == 0) { + SPI_OS_ERROR(("[SPI] ERROR : spi_os_memcpy fail\n")); + return 0; + } + + return 0; +} + + +/********************************************************** +Prototype int spi_os_delete_mutex ( int pmutex ) + +Type function + +Description delete mutex + +Param input pmutex : id of mutex + +Return value 0 : success + 1 : fail +***********************************************************/ +int spi_os_delete_mutex(int pmutex) +{ + return 0; +} + + +/********************************************************** +Prototype int spi_os_acquire_mutex ( int pmutex, unsigned int wait ) + +Type function + +Description acquire mutex + +Param input pmutex : id of mutex to acquire + wait : mutex waiting time + +Return value 0 : success + 1 : fail +***********************************************************/ +int spi_os_acquire_mutex(int pmutex, unsigned int wait) +{ + return 0; +} + + +/********************************************************** +Prototype int spi_os_release_mutex (int pmutex ) + +Type function + +Description release mutex + +Param input pmutex : id of mutex to release + +Return value 0 : success + 1 : fail +***********************************************************/ +int spi_os_release_mutex(int pmutex) +{ + return 0; +} + + +/********************************************************** +Prototype int spi_os_create_sem ( char * name, unsigned int init_count) + +Type function + +Description create semaphore + +Param input name : name of semaphore + init_count : semaphore count + +Return value id of semaphore created +***********************************************************/ +int spi_os_create_sem(char *name, unsigned int init_count) +{ + if (name == 0) { + SPI_OS_ERROR(("[SPI] ERROR : spi_os_memcpy fail\n")); + return 0; + } + + return 0; +} + + +/********************************************************** +Prototype int spi_os_delete_sem ( int sem ) + +Type function + +Description delete semaphore + +Param input sem : id of semaphore to delete + +Return value 0 : success + 1 : fail +***********************************************************/ +int spi_os_delete_sem(int sem) +{ + return 0; +} + + +/********************************************************** +Prototype int spi_os_acquire_sem (int sem, unsigned int wait ) + +Type function + +Description acquire semaphore + +Param input sem : id of semaphore to acquire + wait : mutex waiting time + +Return value 0 : success + 1 : fail +***********************************************************/ +int spi_os_acquire_sem(int sem, unsigned int wait) +{ + return 0; +} + + +/********************************************************** +Prototype int spi_os_release_sem (int sem ) + +Type function + +Description release semaphore + +Param input sem : id of semaphore to release + +Return value 0 : success + 1 : fail +***********************************************************/ +int spi_os_release_sem(int sem) +{ + return 0; +} + + +/********************************************************** +Prototype void spi_os_sleep ( unsigned long msec ) + +Type function + +Description sleep os + +Param input msec : time to sleep + +Return value (none) +***********************************************************/ +void spi_os_sleep(unsigned long msec) +{ + if (msec <= 0) { + SPI_OS_ERROR(("[SPI] ERROR : spi_os_sleep fail\n")); + return; + } + + msleep(msec); +} + + +/********************************************************** +Prototype void spi_os_loop_delay ( unsigned long cnt ) + +Type function + +Description delay task with loop + +Param input cnt : delay count + +Return value (none) +***********************************************************/ +void spi_os_loop_delay(unsigned long cnt) +{ + unsigned int timeout; + timeout = 0; + while (++timeout < cnt) + ; +} + + +/********************************************************** +Prototype void * spi_os_create_timer + +Type function + +Description create timer + +Param input name : timer name + callback : timer callback function + param : timer param + duration : timer expire duration + +Return value timer ptr +***********************************************************/ +int spi_os_create_timer(void *timer, char *name, + SPI_OS_TIMER_T callback, int param, unsigned long duration) +{ + struct timer_list *tm = timer; + + + if (name == 0 || callback == 0 || param <= 0 || duration <= 0) { + SPI_OS_ERROR(("[SPI] ERROR : spi_os_create_timer fail\n")); + return 0; + } + + init_timer(tm); + + tm->expires = jiffies + ((duration * HZ) / 1000); + tm->data = (unsigned long) param; + tm->function = callback; + + return 1; +} + + +/********************************************************** +Prototype int spi_os_start_timer + +Type function + +Description start timer + +Param input timer : timer ptr + callback : timer callback function + param : timer param + duration : timer expire duration + +Return value 1 : success + 0 : fail +***********************************************************/ +int spi_os_start_timer(void *timer, SPI_OS_TIMER_T callback, + int param, unsigned long duration) +{ + add_timer((struct timer_list *) timer); + return 1; +} + + +/********************************************************** +Prototype int spi_os_stop_timer (void * timer) + +Type function + +Description stop timer + +Param input timer : timer ptr + +Return value 1 : success + 0 : fail +***********************************************************/ +int spi_os_stop_timer(void *timer) +{ + int value = 0; + + value = del_timer((struct timer_list *) timer); + + return value; +} + + +/********************************************************** +Prototype int spi_os_delete_timer (void * timer) + +Type function + +Description delete timer + +Param input timer : timer ptr + +Return value 1 : success + 0 : fail +***********************************************************/ +int spi_os_delete_timer(void *timer) +{ + return 1; +} + + +/********************************************************** +Prototype unsigned long spi_os_get_tick (void) + +Type function + +Description get system tick + +Param input (none) + +Return value system tick + +***********************************************************/ +unsigned long spi_os_get_tick(void) +{ + unsigned long tick = 0; + + tick = jiffies_to_msecs(jiffies); + return tick; +} + + +/********************************************************** +Prototype void spi_os_get_tick_by_log (char * name) + +Type function + +Description print tick time to log + +Param input name : print name + +Return value (none) +***********************************************************/ +void spi_os_get_tick_by_log(char *name) +{ + SPI_OS_TRACE(("[SPI] %s tick %lu ms\n", name, spi_os_get_tick())); + +} + + +/********************************************************** +Prototype void spi_os_trace_dump (char * name, void * data, int length) + +Description print buffer value by hex code + if buffer size too big, it change to.... + and print 64 byte of front and 64 byte of tail + +Param input name : print name + data : buffer for print + length : print length + +Return value (none) +***********************************************************/ +void spi_os_trace_dump(char *name, void *data, int length) +{ +#ifdef SPI_FEATURE_DEBUG + #define SPI_OS_TRACE_DUMP_PER_LINE 16 + #define SPI_OS_TRACE_MAX_LINE 8 + #define SPI_OS_TRACE_HALF_LINE + (SPI_OS_TRACE_MAX_LINE / 2) + #define SPI_OS_TRACE_MAX_DUMP_SIZE + (SPI_OS_TRACE_DUMP_PER_LINE*SPI_OS_TRACE_MAX_LINE) + + int i = 0, lines = 0, halflinemode = 0; + + char buf[SPI_OS_TRACE_DUMP_PER_LINE * 3 + 1]; + char *pdata = NULL; + + char ch = 0; + + SPI_OS_TRACE_MID(("[SPI] spi_os_trace_dump (%s length[%d])\n", + name, length)); + + spi_os_memset(buf, 0x00, sizeof(buf)); + + if (length > SPI_OS_TRACE_MAX_DUMP_SIZE) + halflinemode = 1; + + pdata = data; + for (i = 0 ; i < length ; i++) { + if ((i != 0) && ((i % SPI_OS_TRACE_DUMP_PER_LINE) == 0)) { + buf[SPI_OS_TRACE_DUMP_PER_LINE*3] = 0; + SPI_OS_TRACE_MID(("%s\n", buf)); + spi_os_memset(buf, 0x00, sizeof(buf)); + lines++; + if (SPI_OS_TRACE_HALF_LINE == lines + && halflinemode == 1) { + SPI_OS_TRACE_MID((" ......\n")); + pdata += (length - SPI_OS_TRACE_MAX_DUMP_SIZE); + i += (length - SPI_OS_TRACE_MAX_DUMP_SIZE); + } + } + + ch = (*pdata&0xF0)>>4; + buf[(i%SPI_OS_TRACE_DUMP_PER_LINE)*3] = + ((ch > 9) ? (ch-10 + 'A') : (ch + '0')); + ch = (*pdata&0x0F); + buf[(i%SPI_OS_TRACE_DUMP_PER_LINE)*3+1] = + ((ch > 9) ? (ch-10 + 'A') : (ch + '0')); + buf[(i%SPI_OS_TRACE_DUMP_PER_LINE)*3+2] = 0x20; + pdata++; + } + + if (buf[0] != '\0') + SPI_OS_TRACE_MID(("%s\n", buf)); + + #undef SPI_OS_TRACE_DUMP_PER_LINE + #undef SPI_OS_TRACE_MAX_LINE + #undef SPI_OS_TRACE_HALF_LINE + #undef SPI_OS_TRACE_MAX_DUMP_SIZE +#endif +} + + +/********************************************************** +Prototype void spi_os_trace_dump (char * name, void * data, int length) + +Description print buffer value by hex code as SPI_OS_TRACE + + this function print only 16 byte + +Param input name : print name + data : buffer for print + length : print length + +Return value (none) +***********************************************************/ + +void spi_os_trace_dump_low(char *name, void *data, int length) +{ + #define SPI_OS_TRACE_DUMP_PER_LINE 16 + + int i = 0; + char buf[SPI_OS_TRACE_DUMP_PER_LINE * 3 + 1] = {0,}; + char *pdata = NULL; + char ch = 0; + + SPI_OS_ERROR(("[SPI] spi_os_trace_dump_low (%s length[%d])\n", + name, length)); + + spi_os_memset(buf, 0x00, sizeof(buf)); + + if (length > SPI_OS_TRACE_DUMP_PER_LINE) + length = SPI_OS_TRACE_DUMP_PER_LINE; + + pdata = data; + for (i = 0 ; i < length ; i++) { + ch = (*pdata&0xF0)>>4; + buf[(i%SPI_OS_TRACE_DUMP_PER_LINE)*3] = + ((ch > 9) ? (ch-10 + 'A') : (ch + '0')); + ch = (*pdata&0x0F); + buf[(i%SPI_OS_TRACE_DUMP_PER_LINE)*3+1] = + ((ch > 9) ? (ch-10 + 'A') : (ch + '0')); + buf[(i%SPI_OS_TRACE_DUMP_PER_LINE)*3+2] = 0x20; + pdata++; + } + + if (buf[0] != '\0') + SPI_OS_ERROR(("%s\n", buf)); + + #undef SPI_OS_TRACE_DUMP_PER_LINE +} diff --git a/drivers/phone_svn/ipc_spi/spi_os.h b/drivers/phone_svn/ipc_spi/spi_os.h new file mode 100644 index 0000000..5174144 --- /dev/null +++ b/drivers/phone_svn/ipc_spi/spi_os.h @@ -0,0 +1,82 @@ +#ifndef __SPI_OS_H__ +#define __SPI_OS_H__ + +#include "spi_main.h" + +#include <linux/kernel.h> +#include <linux/workqueue.h> + + +#define SPI_OS_ASSERT(x) printk x +#define SPI_OS_ERROR(x) printk x + +#ifdef SPI_FEATURE_NOLOG +#define SPI_OS_TRACE_MID(x) +#define SPI_OS_TRACE(x) +#else +#define SPI_OS_TRACE(x) printk x +#ifdef SPI_FEATURE_DEBUG +#define SPI_OS_TRACE_MID(x) printk x +#else +#define SPI_OS_TRACE_MID(x) +#endif /* SPI_FEATURE_DEBUG */ +#endif /* SPI_FEATURE_NOLOG */ + +struct spi_work { + struct work_struct work; + int signal_code; +}; + +struct spi_os_msg { + unsigned int signal_code; + unsigned int data_length; + void *data; +}; + +typedef void (*SPI_OS_TIMER_T)(unsigned long); +/* Inherit option */ +#define SPI_OS_MUTEX_NO_INHERIT 0 +#define SPI_OS_MUTEX_INHERIT 1 +/* Wait option. */ +#define SPI_OS_MUTEX_NO_WAIT /* TBD */ +#define SPI_OS_MUTEX_WAIT_FOREVER /* TBD */ +#define SPI_OS_TIMER_CALLBACK(X) void X(unsigned long param) + +/* Memory */ +extern void *spi_os_malloc(unsigned int length); +extern void *spi_os_vmalloc(unsigned int length); +extern int spi_os_free(void *addr); +extern int spi_os_vfree(void *addr); +extern int spi_os_memcpy(void *dest, void *src, unsigned int length); +extern void *spi_os_memset(void *addr, int value, unsigned int length); + +/* Mutex */ +extern int spi_os_create_mutex(char *name, + unsigned int priority_inherit); +extern int spi_os_delete_mutex(int sem); +extern int spi_os_acquire_mutex(int sem, unsigned int wait); +extern int spi_os_release_mutex(int sem); + + +/* Semaphore */ +extern int spi_os_create_sem(char *name, unsigned int init_count); +extern int spi_os_delete_sem(int sem); +extern int spi_os_acquire_sem(int sem, unsigned int wait); +extern int spi_os_release_sem(int sem); + +/* Timer */ +extern void spi_os_sleep(unsigned long msec); +extern void spi_os_loop_delay(unsigned long cnt); +extern int spi_os_create_timer(void *timer, char *name, + SPI_OS_TIMER_T callback, int param, unsigned long duration); +extern int spi_os_start_timer(void *timer, SPI_OS_TIMER_T callback, + int param, unsigned long duration); +extern int spi_os_stop_timer(void *timer); +extern int spi_os_delete_timer(void *timer); + +/* Log */ +extern unsigned long spi_os_get_tick(void); +extern void spi_os_get_tick_by_log(char *name); +extern void spi_os_trace_dump(char *name, void *data, int length); +extern void spi_os_trace_dump_low(char *name, void *data, int length); +#endif diff --git a/drivers/phone_svn/ipc_spi/spi_test.c b/drivers/phone_svn/ipc_spi/spi_test.c new file mode 100644 index 0000000..31f717f --- /dev/null +++ b/drivers/phone_svn/ipc_spi/spi_test.c @@ -0,0 +1,270 @@ +#include "spi_main.h" +#include "spi_test.h" +#include "spi_os.h" +#include "spi_dev.h" +#include "spi_app.h" + +enum SPI_TEST_SCENARIO_T spi_test_scenario_mode = SPI_TEST_SCENARIO_NONE; + +static void _scenario_physical(void); +static void _scenario_sending(int param); + + +static void _scenario_physical(void) +{ + int count = 0; + int failcount = 0; + int i; + char txbuf[SPI_DEV_MAX_PACKET_SIZE]; + char rxbuf[SPI_DEV_MAX_PACKET_SIZE]; + + SPI_OS_TRACE_MID(("spi_scenario_physical\n")); + + spi_os_memset(txbuf, 0x00, sizeof(txbuf)); + for (i = 0 ; i < SPI_DEV_MAX_PACKET_SIZE ; i++) + txbuf[i] = i%100; + + while (count < 100) { + SPI_OS_TRACE_MID(("spi _scenario_physical %d\n", count)); + SPI_OS_TRACE_MID(("%s mrdy %d srdy %d submrdy %d subsrdy %d\n", + "spi_scenario_physical test start", + spi_dev_get_gpio(spi_dev_gpio_mrdy), + spi_dev_get_gpio(spi_dev_gpio_srdy), + spi_dev_get_gpio(spi_dev_gpio_submrdy), + spi_dev_get_gpio(spi_dev_gpio_subsrdy))); + + txbuf[0] = count; + spi_os_memset(rxbuf, 0x00, sizeof(rxbuf)); + +#ifdef SPI_FEATURE_MASTER + spi_dev_set_gpio(spi_dev_gpio_mrdy, SPI_DEV_GPIOLEVEL_LOW); + spi_os_sleep(SPI_FEATURE_TEST_DURATION); + spi_dev_set_gpio(spi_dev_gpio_submrdy, SPI_DEV_GPIOLEVEL_LOW); + spi_os_sleep(SPI_FEATURE_TEST_DURATION); + + SPI_OS_TRACE_MID(("%s, subsrdy high\n", + "spi_scenario_physical test wait srdy")); + spi_dev_set_gpio(spi_dev_gpio_mrdy, SPI_DEV_GPIOLEVEL_HIGH); + + while (spi_dev_get_gpio(spi_dev_gpio_srdy) == + SPI_DEV_GPIOLEVEL_LOW) + ; + + /* spreadtrum recommend. */ + /* master should send/receive after that slave is ready. */ + spi_os_sleep(20); + + if (count % 2 == 0) { + spi_dev_send(txbuf, SPI_DEV_MAX_PACKET_SIZE); + } else { + spi_dev_receive(rxbuf, SPI_DEV_MAX_PACKET_SIZE); + for (i = 1 ; i < SPI_DEV_MAX_PACKET_SIZE ; i++) { + if (rxbuf[i] != txbuf[i]) { + failcount++; + spi_os_trace_dump("spi_scenario_physical receiving fail", + &rxbuf[i-8], 16); + SPI_OS_TRACE_MID(("%s %d count %d/%d\n", + "spi_scenario_physical test receiving fail", + i, failcount, count)); + i = sizeof(rxbuf); + break; + } + } + } + + spi_os_sleep(20); + spi_dev_set_gpio(spi_dev_gpio_submrdy, SPI_DEV_GPIOLEVEL_HIGH); + + spi_os_sleep(SPI_FEATURE_TEST_DURATION); + +#elif defined SPI_FEATURE_SLAVE + spi_dev_set_gpio(spi_dev_gpio_srdy, SPI_DEV_GPIOLEVEL_LOW); + spi_os_sleep(SPI_FEATURE_TEST_DURATION); + spi_dev_set_gpio(spi_dev_gpio_subsrdy, SPI_DEV_GPIOLEVEL_LOW); + spi_os_sleep(SPI_FEATURE_TEST_DURATION); + + while (spi_dev_get_gpio(spi_dev_gpio_mrdy) == + SPI_DEV_GPIOLEVEL_LOW) + ; + + spi_dev_set_gpio(spi_dev_gpio_srdy, SPI_DEV_GPIOLEVEL_HIGH); + + if (count % 2 == 0) { + spi_dev_receive(rxbuf, SPI_DEV_MAX_PACKET_SIZE); + for (i = 1 ; i < SPI_DEV_MAX_PACKET_SIZE ; i++) { + if (rxbuf[i] != txbuf[i]) { + failcount++; + spi_os_trace_dump("spi_scenario_phy rx fail", + &rxbuf[i-8], 16); + SPI_OS_TRACE_MID(("%s %d count %d/%d\n", + "spi_scenario_physical test receiving fail", + i, failcount, count)); + i = sizeof(rxbuf); + break; + } + } + } else + spi_dev_send(txbuf, SPI_DEV_MAX_PACKET_SIZE); + + spi_os_sleep(SPI_FEATURE_TEST_DURATION); + +#endif + count++; + SPI_OS_TRACE_MID(("%s %d/%d\n", + "spi_scenario_physical test receiving result count", + failcount, count)); + } +} + + +SPI_OS_TIMER_CALLBACK(spi_test_timer_callback) +{ + SPI_OS_TRACE_MID(("spi_test_timer_callback\n")); + _scenario_sending(0); +} + +static char tempdata1[135]; /* temp code. becuase can not allocate */ +static char tempdata2[367]; /* temp code. becuase can not allocate */ +static char tempdata3[1057]; /* temp code. becuase can not allocate */ +static char tempdata4[35]; /* temp code. becuase can not allocate */ +static char tempdata5[2079]; /* temp code. becuase can not allocate */ +static char tempdata6[200]; /* temp code. becuase can not allocate */ +static char tempdata7[2052]; /* temp code. becuase can not allocate */ + +static void _scenario_sending(int param) +{ + #define NB_COUNT 50 + #define NB_STEP 7 + static int step; + static int duration; + static int count; + static void *timer_id; + + char *data = NULL; + int i, value; + struct DATABYSTEP_T { + SPI_MAIN_MSG_T type; + unsigned int size; + char *buf; + } databystep[NB_STEP] = { + {SPI_MAIN_MSG_IPC_SEND, 135, tempdata1}, + {SPI_MAIN_MSG_IPC_SEND, 187, tempdata2}, + {SPI_MAIN_MSG_RAW_SEND, 1057, tempdata3}, + {SPI_MAIN_MSG_IPC_SEND, 35, tempdata4}, + {SPI_MAIN_MSG_RAW_SEND, 2079, tempdata5}, + {SPI_MAIN_MSG_IPC_SEND, 100, tempdata6}, + {SPI_MAIN_MSG_RAW_SEND, 2052, tempdata7} + }; + + if (spi_test_scenario_mode == SPI_TEST_SCENARIO_SLAVE_SENDING) + data = databystep[step].buf; + + /* param is 0 to fix duration. */ + /* call this function with param to change timer duration. */ + if (param != 0) + duration = param; + + if (spi_test_scenario_mode != SPI_TEST_SCENARIO_SLAVE_SENDING) + data = (char *)spi_os_malloc(databystep[step].size); + + spi_os_memset(data, 0x00, databystep[step].size); + + SPI_OS_TRACE_MID(("spi _scenario_sending step %d\n", step)); + + /* generate data to send, It is serial number from 0 to 99 */ + for (i = 0 ; i < databystep[step].size ; i++) + data[i] = i%100; + + do { +#ifdef SPI_FEATURE_OMAP4430 + if (spi_is_ready() != 0) { + struct spi_os_msg *msg; + SPI_MAIN_MSG_T signal_code; + signal_code = databystep[step].type; + + msg = (struct spi_os_msg *) + spi_os_malloc(sizeof(struct spi_os_msg)); + msg->signal_code = signal_code; + msg->data_length = databystep[step].size; + msg->data = data; + + spi_receive_msg_from_app(msg); + spi_main_send_signal(signal_code); + break; + } else + spi_os_sleep(50); +#elif defined SPI_FEATURE_SC8800G + if (app_send_data_to_spi(databystep[step].type, + data, databystep[step].size) == 0) + /* send fail */ + spi_os_sleep(50); + else + /* send success */ + break; +#endif + } while (1); + + if (spi_test_scenario_mode == SPI_TEST_SCENARIO_MASTER_SENDING) + return; + + step++; + count++; + step %= NB_STEP; + + if (timer_id == 0) + timer_id = spi_os_create_timer("spi_test_timer", + spi_test_timer_callback, step, duration); + + if (timer_id == 0) { + SPI_OS_TRACE_MID(("spi _scenario_sending invalid timer id\n")); + return; + } + + SPI_OS_TRACE_MID(("spi _scenario_sending timer %x\n", timer_id)); + SPI_OS_TRACE_MID(("spi _scenario_sending start timer count %d\n", + count)); + + if (count == NB_COUNT) { + spi_os_stop_timer(timer_id); + return; + } + + value = spi_os_start_timer(timer_id, spi_test_timer_callback, + step, duration); + SPI_OS_TRACE_MID(("spi _scenario_sending start timer%d\n", value)); + + #undef NB_STEP +} + + +void spi_test_run(enum SPI_TEST_SCENARIO_T scenario, int param) +{ + SPI_OS_TRACE_MID(("spi_test_run %d\n", (int) scenario)); + + if (scenario == SPI_TEST_SCENARIO_NONE) + return; + + spi_test_scenario_mode = scenario; + + switch ((int)scenario) { + case SPI_TEST_SCENARIO_PHYSICAL: + _scenario_physical(); + break; + case SPI_TEST_SCENARIO_MASTER_SENDING: +#ifdef SPI_FEATURE_MASTER + _scenario_sending(param); +#endif + break; + case SPI_TEST_SCENARIO_SLAVE_SENDING: +#ifdef SPI_FEATURE_SLAVE + _scenario_sending(param); +#endif + break; + case SPI_TEST_SCENARIO_COMPLEX_SENDING: + _scenario_sending(param); + break; + case SPI_TEST_SCENARIO_NONE: + default: + break; + } +} diff --git a/drivers/phone_svn/ipc_spi/spi_test.h b/drivers/phone_svn/ipc_spi/spi_test.h new file mode 100644 index 0000000..251d057 --- /dev/null +++ b/drivers/phone_svn/ipc_spi/spi_test.h @@ -0,0 +1,14 @@ +#ifndef __SPI_TEST_H__ +#define __SPI_TEST_H__ + +enum SPI_TEST_SCENARIO_T { + SPI_TEST_SCENARIO_NONE, + SPI_TEST_SCENARIO_PHYSICAL, + SPI_TEST_SCENARIO_MASTER_SENDING, + SPI_TEST_SCENARIO_SLAVE_SENDING, + SPI_TEST_SCENARIO_COMPLEX_SENDING, +}; + +extern enum SPI_TEST_SCENARIO_T spi_test_scenario_mode; +extern void spi_test_run(enum SPI_TEST_SCENARIO_T scenario, int param); +#endif diff --git a/drivers/phone_svn/svnet/Makefile b/drivers/phone_svn/svnet/Makefile new file mode 100644 index 0000000..42fad44 --- /dev/null +++ b/drivers/phone_svn/svnet/Makefile @@ -0,0 +1,5 @@ +svnet-y := main.o pdp.o + +svnet-y += sipc4.o + +obj-$(CONFIG_PHONE_SVNET) += svnet.o diff --git a/drivers/phone_svn/svnet/main.c b/drivers/phone_svn/svnet/main.c new file mode 100644 index 0000000..2db9412 --- /dev/null +++ b/drivers/phone_svn/svnet/main.c @@ -0,0 +1,867 @@ +/** + * Samsung Virtual Network driver using OneDram device + * + * Copyright (C) 2010 Samsung Electronics. 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 + * version 2 as published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +/*#define DEBUG */ + +#if defined(DEBUG) +#define NOISY_DEBUG +#endif + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/workqueue.h> +#include <linux/list.h> +#include <linux/jiffies.h> + +#include <linux/netdevice.h> +#include <linux/skbuff.h> +#include <net/sock.h> +#include <linux/if.h> +#include <linux/if_arp.h> + +#include <linux/if_phonet.h> +#include <linux/phonet.h> +#include <net/phonet/phonet.h> + +#ifdef CONFIG_HAS_WAKELOCK +#include <linux/wakelock.h> + +#define DEFAULT_RAW_WAKE_TIME (6*HZ) +#define DEFAULT_FMT_WAKE_TIME (HZ/2) +#endif + +#if defined(NOISY_DEBUG) +# define _dbg(dev, format, arg...) dev_dbg(dev, format, ## arg) +#else +# define _dbg(dev, format, arg...) do { } while (0) +#endif + +#include "sipc.h" +#include "pdp.h" + +#define SVNET_DEV_ADDR 0xa0 + +enum { + SVNET_NORMAL = 0, + SVNET_RESET, + SVNET_EXIT, + SVNET_MAX, +}; + +struct svnet_stat { + unsigned int st_wq_state; + unsigned long st_recv_evt; + unsigned long st_recv_pkt_ph; + unsigned long st_recv_pkt_pdp; + unsigned long st_do_write; + unsigned long st_do_read; + unsigned long st_do_rx; +}; +static struct svnet_stat stat; + +struct svnet_evt { + struct list_head list; + u32 event; +}; + +struct svnet_evt_head { + struct list_head list; + u32 len; + spinlock_t lock; +}; + +struct svnet { + struct net_device *ndev; + const struct attribute_group *group; + + struct workqueue_struct *wq; + struct work_struct work_read; + struct delayed_work work_write; + struct delayed_work work_rx; + + struct work_struct work_exit; + int exit_flag; + + struct sk_buff_head txq; + struct svnet_evt_head rxq; + + struct sipc *si; +#ifdef CONFIG_HAS_WAKELOCK + struct wake_lock wlock; + long wake_time; /* jiffies */ /* wake time for not fmt packet */ + long wake_process_time; /* jiffies */ /* processing wake time */ +#endif +}; + +static struct svnet *svnet_dev; + +#ifdef CONFIG_HAS_WAKELOCK +static inline void _wake_lock_init(struct svnet *sn) +{ + wake_lock_init(&sn->wlock, WAKE_LOCK_SUSPEND, "svnet"); + sn->wake_time = DEFAULT_RAW_WAKE_TIME; + sn->wake_process_time = DEFAULT_FMT_WAKE_TIME; +} + +static inline void _wake_lock_destroy(struct svnet *sn) +{ + wake_lock_destroy(&sn->wlock); +} + +static inline void _wake_lock_timeout(struct svnet *sn) +{ + wake_lock_timeout(&sn->wlock, sn->wake_time); +} + +void _non_fmt_wakelock_timeout(void) +{ + if (svnet_dev) + _wake_lock_timeout(svnet_dev); +} + +static inline void _wake_process_lock_timeout(struct svnet *sn) +{ + wake_lock_timeout(&sn->wlock, sn->wake_process_time); +} + +void _fmt_wakelock_timeout(void) +{ + if (svnet_dev) + _wake_process_lock_timeout(svnet_dev); +} + +static inline void _wake_lock_settime(struct svnet *sn, long time) +{ + if (sn) + sn->wake_time = time; +} + +static inline long _wake_lock_gettime(struct svnet *sn) +{ + return sn ? sn->wake_time : DEFAULT_RAW_WAKE_TIME; +} +#else +#define _wake_lock_init(sn) do { } while (0) +#define _wake_lock_destroy(sn) do { } while (0) +#define _wake_lock_timeout(sn) do { } while (0) +#define _wake_process_lock_timeout(sn) do { } while (0) +#define _non_fmt_wakelock_timeout() do { } while (0) +#define _fmt_wakelock_timeout() do { } while (0) +#define _wake_lock_settime(sn, time) do { } while (0) +#define _wake_lock_gettime(sn) (0) +#endif + +static unsigned long long tmp_itor; +static unsigned long long tmp_xtow; +static unsigned long long time_max_itor; +static unsigned long long time_max_xtow; +static unsigned long long time_max_read; +static unsigned long long time_max_write; + +/* +extern unsigned long long time_max_semlat; +*/ +static int _queue_evt(struct svnet_evt_head *h, u32 event); + +static ssize_t show_version(struct device *d, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "Samsung IPC version %s\n", sipc_version); +} + +static ssize_t show_waketime(struct device *d, + struct device_attribute *attr, char *buf) +{ + char *p = buf; + unsigned int msec; + unsigned long j; + + if (!svnet_dev) + return 0; + + j = _wake_lock_gettime(svnet_dev); + msec = jiffies_to_msecs(j); + p += sprintf(p, "%u\n", msec); + + return p - buf; +} + +static ssize_t store_waketime(struct device *d, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long msec; + unsigned long j; + int r; + + if (!svnet_dev) + return count; + + r = strict_strtoul(buf, 10, &msec); + if (r) + return count; + + j = msecs_to_jiffies(msec); + _wake_lock_settime(svnet_dev, j); + + return count; +} + +static inline int _show_stat(char *buf) +{ + char *p = buf; + + p += sprintf(p, "Stat --------\n"); + p += sprintf(p, "\twork state: %d\n", stat.st_wq_state); + p += sprintf(p, "\trecv mailbox: %lu\n", stat.st_recv_evt); + p += sprintf(p, "\trecv phonet: %lu\n", stat.st_recv_pkt_ph); + p += sprintf(p, "\trecv packet: %lu\n", stat.st_recv_pkt_pdp); + p += sprintf(p, "\twrite count: %lu\n", stat.st_do_write); + p += sprintf(p, "\tread count: %lu\n", stat.st_do_read); + p += sprintf(p, "\trx count: %lu\n", stat.st_do_rx); + p += sprintf(p, "\n"); + + return p - buf; +} + +static ssize_t show_latency(struct device *d, + struct device_attribute *attr, char *buf) +{ + char *p = buf; + + p += sprintf(p, "Max read latency: %12llu ns\n", time_max_itor); + p += sprintf(p, "Max read time: %12llu ns\n", time_max_read); + p += sprintf(p, "Max write latency: %12llu ns\n", time_max_xtow); + p += sprintf(p, "Max write time: %12llu ns\n", time_max_write); + /*p += sprintf(p, "Max sem. latency: %12llu ns\n", time_max_semlat);*/ + + return p - buf; +} + +static ssize_t show_debug(struct device *d, + struct device_attribute *attr, char *buf) +{ + char *p = buf; + + if (!svnet_dev) + return 0; + + p += _show_stat(p); + + p += sprintf(p, "Event queue -----\n"); + p += sprintf(p, "\tTX queue\t%u\n", skb_queue_len(&svnet_dev->txq)); + p += sprintf(p, "\tRX queue\t%u\n", svnet_dev->rxq.len); + + p += sipc_debug_show(svnet_dev->si, p); + + return p - buf; +} + +static ssize_t store_debug(struct device *d, + struct device_attribute *attr, const char *buf, size_t count) +{ + if (!svnet_dev) + return count; + + switch (buf[0]) { + case 'R': + sipc_debug(svnet_dev->si, buf); + break; + default: + + break; + } + + return count; +} + +static ssize_t store_whitelist(struct device *d, + struct device_attribute *attr, const char *buf, size_t count) +{ + if (!svnet_dev) + return count; + + switch (buf[0]) { + case 0x7F: + return sipc_whitelist(svnet_dev->si, buf, count); + break; + default: + + break; + } + + return count; +} + +static DEVICE_ATTR(version, S_IRUGO, show_version, NULL); +static DEVICE_ATTR(latency, S_IRUGO, show_latency, NULL); +static DEVICE_ATTR(waketime, S_IRUGO | S_IWUSR | S_IWGRP, + show_waketime, store_waketime); +static DEVICE_ATTR(debug, S_IRUGO | S_IWUSR, show_debug, store_debug); +static DEVICE_ATTR(whitelist, S_IRUSR | S_IWUSR, NULL, store_whitelist); + +static struct attribute *svnet_attributes[] = { + &dev_attr_version.attr, + &dev_attr_waketime.attr, + &dev_attr_debug.attr, + &dev_attr_latency.attr, + &dev_attr_whitelist.attr, + NULL +}; + +static const struct attribute_group svnet_group = { + .attrs = svnet_attributes, +}; + + +int vnet_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct svnet *sn; + struct pdp_priv *priv; + + dev_dbg(&ndev->dev, "recv inet packet %p: %d bytes\n", skb, skb->len); + stat.st_recv_pkt_pdp++; + + priv = netdev_priv(ndev); + if (!priv) + goto drop; + + sn = netdev_priv(priv->parent); + if (!sn) + goto drop; + + if (!tmp_xtow) + tmp_xtow = cpu_clock(smp_processor_id()); + + skb_queue_tail(&sn->txq, skb); + + _wake_process_lock_timeout(sn); + queue_delayed_work(sn->wq, &sn->work_write, 0); + + return NETDEV_TX_OK; + +drop: + ndev->stats.tx_dropped++; + + return NETDEV_TX_OK; +} + +static int svnet_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct svnet *sn; + + if (skb->protocol != __constant_htons(ETH_P_PHONET)) { + dev_err(&ndev->dev, "recv not a phonet message\n"); + goto drop; + } + + stat.st_recv_pkt_ph++; + dev_dbg(&ndev->dev, "recv packet %p: %d bytes\n", skb, skb->len); + + sn = netdev_priv(ndev); + + if (sipc_check_skb(sn->si, skb)) { + sipc_do_cmd(sn->si, skb); + return NETDEV_TX_OK; + } + + if (!tmp_xtow) + tmp_xtow = cpu_clock(smp_processor_id()); + + skb_queue_tail(&sn->txq, skb); + + _wake_process_lock_timeout(sn); + queue_delayed_work(sn->wq, &sn->work_write, 0); + + return NETDEV_TX_OK; + +drop: + dev_kfree_skb(skb); + ndev->stats.tx_dropped++; + return NETDEV_TX_OK; +} + +static int _queue_evt(struct svnet_evt_head *h, u32 event) +{ + unsigned long flags; + struct svnet_evt *e; + + e = kmalloc(sizeof(struct svnet_evt), GFP_ATOMIC); + if (!e) + return -ENOMEM; + + e->event = event; + + spin_lock_irqsave(&h->lock, flags); + list_add_tail(&e->list, &h->list); + h->len++; + spin_unlock_irqrestore(&h->lock, flags); + + return 0; +} + +static void _queue_purge(struct svnet_evt_head *h) +{ + unsigned long flags; + struct svnet_evt *e, *next; + + spin_lock_irqsave(&h->lock, flags); + list_for_each_entry_safe(e, next, &h->list, list) { + list_del(&e->list); + h->len--; + kfree(e); + } + spin_unlock_irqrestore(&h->lock, flags); +} + +static u32 _dequeue_evt(struct svnet_evt_head *h) +{ + unsigned long flags; + struct list_head *p; + struct svnet_evt *e; + u32 event; + + spin_lock_irqsave(&h->lock, flags); + p = h->list.next; + if (p == &h->list) { + e = NULL; + event = 0; + } else { + e = list_entry(p, struct svnet_evt, list); + list_del(&e->list); + h->len--; + event = e->event; + } + spin_unlock_irqrestore(&h->lock, flags); + + if (e) + kfree(NULL); + + return event; +} + +static int _proc_private_event(struct svnet *sn, u32 evt) +{ + switch (evt) { + case SIPC_EXIT_MB: + dev_err(&sn->ndev->dev, "Modem crash message received\n"); + sn->exit_flag = SVNET_EXIT; + break; + case SIPC_RESET_MB: + dev_err(&sn->ndev->dev, "Modem reset message received\n"); + sn->exit_flag = SVNET_RESET; + break; + default: + return 0; + } + + /* //block for pdp_activate fail - jongmoon.suh + queue_work(sn->wq, &sn->work_exit); + */ + + return 1; +} + +static void svnet_queue_event(u32 evt, void *data) +{ + struct net_device *ndev = (struct net_device *)data; + struct svnet *sn; + int r; + + if (!tmp_itor) + tmp_itor = cpu_clock(smp_processor_id()); + + stat.st_recv_evt++; + + if (!ndev) + return; + + sn = netdev_priv(ndev); + if (!sn) + return; + + r = _proc_private_event(sn, evt); + if (r) + return; + + r = _queue_evt(&sn->rxq, evt); + if (r) { + dev_err(&sn->ndev->dev, "Not enough memory: event skipped\n"); + return; + } + + _wake_process_lock_timeout(sn); + queue_work(sn->wq, &sn->work_read); +} + +static int svnet_open(struct net_device *ndev) +{ + struct svnet *sn = netdev_priv(ndev); + + dev_dbg(&ndev->dev, "%s\n", __func__); + + /* TODO: check modem state */ + + if (!sn->si) { + sn->si = sipc_open(svnet_queue_event, ndev); + if (IS_ERR(sn->si)) { + dev_err(&ndev->dev, "IPC init error\n"); + return PTR_ERR(sn->si); + } + sn->exit_flag = SVNET_NORMAL; + } + + netif_wake_queue(ndev); + return 0; +} + +static int svnet_close(struct net_device *ndev) +{ + struct svnet *sn = netdev_priv(ndev); + + dev_dbg(&ndev->dev, "%s\n", __func__); + + if (sn->wq) + flush_workqueue(sn->wq); + skb_queue_purge(&sn->txq); + + if (sn->si) + sipc_close(&sn->si); + + netif_stop_queue(ndev); + + if (sn->wq) + flush_workqueue(sn->wq); + skb_queue_purge(&sn->txq); + + return 0; +} + +static int svnet_ioctl(struct net_device *ndev, struct ifreq *ifr, int cmd) +{ + struct if_phonet_req *req = (struct if_phonet_req *)ifr; + + switch (cmd) { + case SIOCPNGAUTOCONF: + req->ifr_phonet_autoconf.device = SVNET_DEV_ADDR; + return 0; + } + + return -ENOIOCTLCMD; +} + +static const struct net_device_ops svnet_ops = { + .ndo_open = svnet_open, + .ndo_stop = svnet_close, + .ndo_start_xmit = svnet_xmit, + .ndo_do_ioctl = svnet_ioctl, +}; + +static void svnet_setup(struct net_device *ndev) +{ + ndev->features = 0; + ndev->netdev_ops = &svnet_ops; + ndev->header_ops = &phonet_header_ops; + ndev->type = ARPHRD_PHONET; + ndev->flags = IFF_POINTOPOINT | IFF_NOARP; + ndev->mtu = PHONET_MAX_MTU; + ndev->hard_header_len = 1; + ndev->dev_addr[0] = SVNET_DEV_ADDR; + ndev->addr_len = 1; + ndev->tx_queue_len = 1000; + +/* ndev->destructor = free_netdev; */ +} + +static void svnet_read_wq(struct work_struct *work) +{ + struct svnet *sn = container_of(work, + struct svnet, work_read); + u32 event; + int r = 0; + int contd = 0; + unsigned long long t, d; + + t = cpu_clock(smp_processor_id()); + if (tmp_itor) { + d = t - tmp_itor; + _dbg(&sn->ndev->dev, "int_to_read %llu ns\n", d); + tmp_itor = 0; + if (time_max_itor < d) + time_max_itor = d; + } + + dev_dbg(&sn->ndev->dev, "%s\n", __func__); + stat.st_do_read++; + + stat.st_wq_state = 1; + event = _dequeue_evt(&sn->rxq); + while (event) { + /* isn't it possible that merge the events? */ + dev_dbg(&sn->ndev->dev, "event %x\n", event); + + if (sn->si) { + r = sipc_read(sn->si, event, &contd); + if (r < 0) { + dev_err(&sn->ndev->dev, "ret %d -> queue %x\n", + r, event); + _queue_evt(&sn->rxq, event); + break; + } + } else { + dev_err(&sn->ndev->dev, + "IPC not work, skip event %x\n", event); + } + event = _dequeue_evt(&sn->rxq); + } + + if (contd > 0) + queue_delayed_work(sn->wq, &sn->work_rx, 0); + + switch (r) { + case -EINVAL: + dev_err(&sn->ndev->dev, "Invalid argument\n"); + break; + case -EBADMSG: + dev_err(&sn->ndev->dev, "Bad message, purge the buffer\n"); + break; + case -ETIMEDOUT: + dev_err(&sn->ndev->dev, "Timed out\n"); + break; + default: + + break; + } + + stat.st_wq_state = 2; + + d = cpu_clock(smp_processor_id()) - t; + _dbg(&sn->ndev->dev, "read_time %llu ns\n", d); + if (d > time_max_read) + time_max_read = d; +} + +static void svnet_write_wq(struct work_struct *work) +{ + struct svnet *sn = container_of(work, + struct svnet, work_write.work); + int r; + unsigned long long t, d; + + t = cpu_clock(smp_processor_id()); + if (tmp_xtow) { + d = t - tmp_xtow; + _dbg(&sn->ndev->dev, "xmit_to_write %llu ns\n", d); + tmp_xtow = 0; + if (d > time_max_xtow) + time_max_xtow = d; + } + + dev_dbg(&sn->ndev->dev, "%s\n", __func__); + stat.st_do_write++; + + stat.st_wq_state = 3; + if (sn->si) + r = sipc_write(sn->si, &sn->txq); + else { + skb_queue_purge(&sn->txq); + dev_err(&sn->ndev->dev, "IPC not work, drop packet\n"); + r = 0; + } + + switch (r) { + case -ENOSPC: + dev_err(&sn->ndev->dev, "buffer is full, wait...\n"); + queue_delayed_work(sn->wq, &sn->work_write, HZ/10); + break; + case -EINVAL: + dev_err(&sn->ndev->dev, "Invalid arugment\n"); + break; + case -ENXIO: + dev_err(&sn->ndev->dev, "IPC not work, purge the queue\n"); + break; + case -ETIMEDOUT: + dev_err(&sn->ndev->dev, "Timed out\n"); + break; + default: + /* do nothing */ + break; + } + + stat.st_wq_state = 4; + d = cpu_clock(smp_processor_id()) - t; + _dbg(&sn->ndev->dev, "write_time %llu ns\n", d); + if (d > time_max_write) + time_max_write = d; +} + +static void svnet_rx_wq(struct work_struct *work) +{ + struct svnet *sn = container_of(work, + struct svnet, work_rx.work); + int r = 0; + + dev_dbg(&sn->ndev->dev, "%s\n", __func__); + stat.st_do_rx++; + + stat.st_wq_state = 5; + if (sn->si) + r = sipc_rx(sn->si); + + if (r > 0) + queue_delayed_work(sn->wq, &sn->work_rx, HZ/10); + + stat.st_wq_state = 6; +} + +static char *uevent_envs[SVNET_MAX] = { + "", + "MAILBOX=cp_reset", /* reset */ + "MAILBOX=cp_exit", /* exit */ +}; +static void svnet_exit_wq(struct work_struct *work) +{ + struct svnet *sn = container_of(work, + struct svnet, work_exit); + char *envs[2] = { NULL, NULL }; + + dev_dbg(&sn->ndev->dev, "%s: %d\n", __func__, sn->exit_flag); + + if (sn->exit_flag == SVNET_NORMAL || sn->exit_flag >= SVNET_MAX) + return; + + envs[0] = uevent_envs[sn->exit_flag]; + kobject_uevent_env(&sn->ndev->dev.kobj, KOBJ_OFFLINE, envs); + + _queue_purge(&sn->rxq); + skb_queue_purge(&sn->txq); + + if (sn->exit_flag == SVNET_EXIT) + sipc_ramdump(sn->si); + +#if 0 + rtnl_lock(); + if (netif_running(sn->ndev)) + dev_close(sn->ndev); + rtnl_unlock(); +#endif +} + +static inline void _init_data(struct svnet *sn) +{ + INIT_WORK(&sn->work_read, svnet_read_wq); + INIT_DELAYED_WORK(&sn->work_write, svnet_write_wq); + INIT_DELAYED_WORK(&sn->work_rx, svnet_rx_wq); + INIT_WORK(&sn->work_exit, svnet_exit_wq); + + INIT_LIST_HEAD(&sn->rxq.list); + spin_lock_init(&sn->rxq.lock); + sn->rxq.len = 0; + skb_queue_head_init(&sn->txq); +} + +static void _free(struct svnet *sn) +{ + if (!sn) + return; + + _wake_lock_destroy(sn); + + if (sn->group) + sysfs_remove_group(&sn->ndev->dev.kobj, &svnet_group); + + if (sn->wq) { + flush_workqueue(sn->wq); + destroy_workqueue(sn->wq); + } + + if (sn->si) { + sipc_close(&sn->si); + sipc_exit(); + } + + if (sn->ndev) + unregister_netdev(sn->ndev); + + /* sn is ndev's priv */ + free_netdev(sn->ndev); +} + +static int __init svnet_init(void) +{ + int r; + struct svnet *sn = NULL; + struct net_device *ndev; + + printk(KERN_ERR "[%s]\n", __func__); + ndev = alloc_netdev(sizeof(struct svnet), "svnet%d", svnet_setup); + if (!ndev) { + r = -ENOMEM; + goto err; + } + netif_stop_queue(ndev); + sn = netdev_priv(ndev); + + _wake_lock_init(sn); + + r = register_netdev(ndev); + if (r) { + dev_err(&ndev->dev, "failed to register netdev\n"); + goto err; + } + sn->ndev = ndev; + + _init_data(sn); + + sn->wq = create_workqueue("svnetd"); + if (!sn->wq) { + dev_err(&ndev->dev, "failed to create a workqueue\n"); + goto err; + } + + r = sysfs_create_group(&sn->ndev->dev.kobj, &svnet_group); + if (r) { + dev_err(&ndev->dev, "failed to create sysfs group\n"); + goto err; + } + sn->group = &svnet_group; + + dev_dbg(&ndev->dev, "Svnet dev: %p\n", sn); + svnet_dev = sn; + + return 0; + +err: + _free(sn); + return r; +} + +static void __exit svnet_exit(void) +{ + + _free(svnet_dev); + svnet_dev = NULL; +} + +module_init(svnet_init); +module_exit(svnet_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Suchang Woo <suchang.woo@samsung.com>"); +MODULE_DESCRIPTION("Samsung Virtual network interface"); diff --git a/drivers/phone_svn/svnet/main.h b/drivers/phone_svn/svnet/main.h new file mode 100644 index 0000000..74685e9 --- /dev/null +++ b/drivers/phone_svn/svnet/main.h @@ -0,0 +1,5 @@ +/*add interface for sipc files*/ + +void _non_fmt_wakelock_timeout(void); + +void _fmt_wakelock_timeout(void); diff --git a/drivers/phone_svn/svnet/pdp.c b/drivers/phone_svn/svnet/pdp.c new file mode 100644 index 0000000..c5a06dc --- /dev/null +++ b/drivers/phone_svn/svnet/pdp.c @@ -0,0 +1,100 @@ +/** + * Samsung Virtual Network driver using OneDram device + * + * Copyright (C) 2010 Samsung Electronics. 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 + * version 2 as published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +/* #define DEBUG */ + +#include <linux/netdevice.h> +#include <linux/skbuff.h> +#include <net/sock.h> +#include <linux/if.h> +#include <linux/if_arp.h> + +#include "pdp.h" + +static int vnet_open(struct net_device *ndev) +{ + netif_start_queue(ndev); + return 0; +} + +static int vnet_stop(struct net_device *ndev) +{ + netif_stop_queue(ndev); + return 0; +} + +static struct net_device_ops vnet_ops = { + .ndo_open = vnet_open, + .ndo_stop = vnet_stop, +/* .ndo_tx_timeout = vnet_tx_timeout, + .ndo_start_xmit = vnet_start_xmit,*/ +}; + +static void vnet_setup(struct net_device *ndev) +{ + vnet_ops.ndo_start_xmit = vnet_start_xmit; + + ndev->netdev_ops = &vnet_ops; + ndev->type = ARPHRD_PPP; + ndev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; + ndev->hard_header_len = 0; + ndev->addr_len = 0; + ndev->tx_queue_len = 1000; + ndev->mtu = ETH_DATA_LEN; + ndev->watchdog_timeo = 5 * HZ; +} + +struct net_device *create_pdp(int channel, struct net_device *parent) +{ + int r; + struct pdp_priv *priv; + struct net_device *ndev; + char devname[IFNAMSIZ]; + + if (!parent) + return ERR_PTR(-EINVAL); + + sprintf(devname, "pdp%d", channel - 1); + ndev = alloc_netdev(sizeof(struct pdp_priv), devname, vnet_setup); + if (!ndev) + return ERR_PTR(-ENOMEM); + + priv = netdev_priv(ndev); + priv->channel = channel; + priv->parent = parent; + + r = register_netdev(ndev); + if (r) { + free_netdev(ndev); + return ERR_PTR(r); + } + + return ndev; +} + +void destroy_pdp(struct net_device **ndev) +{ + if (!ndev || !*ndev) + return; + + unregister_netdev(*ndev); + free_netdev(*ndev); + *ndev = NULL; +} diff --git a/drivers/phone_svn/svnet/pdp.h b/drivers/phone_svn/svnet/pdp.h new file mode 100644 index 0000000..804b0f3 --- /dev/null +++ b/drivers/phone_svn/svnet/pdp.h @@ -0,0 +1,35 @@ +/** + * SAMSUNG MODEM IPC header + * + * Copyright (C) 2010 Samsung Electronics. 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 + * version 2 as published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef __PACKET_DATA_PROTOCOL_H__ +#define __PACKET_DATA_PROTOCOL_H__ + +#include <linux/netdevice.h> + +struct pdp_priv { + int channel; + struct net_device *parent; +}; + +extern struct net_device *create_pdp(int channel, struct net_device *parent); +extern void destroy_pdp(struct net_device **); +extern int vnet_start_xmit(struct sk_buff *skb, struct net_device *ndev); + +#endif /* __PACKET_DATA_PROTOCOL_H__ */ diff --git a/drivers/phone_svn/svnet/sipc.h b/drivers/phone_svn/svnet/sipc.h new file mode 100644 index 0000000..af9400b --- /dev/null +++ b/drivers/phone_svn/svnet/sipc.h @@ -0,0 +1,64 @@ +/** + * SAMSUNG MODEM IPC header + * + * Copyright (C) 2010 Samsung Electronics. 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 + * version 2 as published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef __SAMSUNG_IPC_H__ +#define __SAMSUNG_IPC_H__ + +#include <linux/types.h> +#include <linux/skbuff.h> +#include <linux/netdevice.h> + +extern const char *sipc_version; + +/* +#if 1 +# include "sipc4.h" +#else +# error "Unknown version" +#endif +*/ + +#define SIPC_RESET_MB 0xFFFFFF7E /* -2 & ~(INT_VALID) */ +#define SIPC_EXIT_MB 0xFFFFFF7F /* -1 & ~(INT_VALID) */ + +struct sipc; + +extern struct sipc *sipc_open(void (*queue)(u32 mailbox, void *data), + struct net_device *ndev); +extern void sipc_close(struct sipc **); + +extern void sipc_exit(void); + +extern int sipc_write(struct sipc *, struct sk_buff_head *); +extern int sipc_read(struct sipc *, u32 mailbox, int *cond); +extern int sipc_rx(struct sipc *); + + +/* TODO: use PN_CMD ?? */ +extern int sipc_check_skb(struct sipc *, struct sk_buff *skb); +extern int sipc_do_cmd(struct sipc *, struct sk_buff *skb); + +extern ssize_t sipc_debug_show(struct sipc *, char *); +extern int sipc_debug(struct sipc *, const char *); +extern int sipc_whitelist(struct sipc *si, const char *buf, size_t count); + +extern void sipc_ramdump(struct sipc *); + +#endif /* __SAMSUNG_IPC_H__ */ diff --git a/drivers/phone_svn/svnet/sipc4.c b/drivers/phone_svn/svnet/sipc4.c new file mode 100644 index 0000000..55aecf6 --- /dev/null +++ b/drivers/phone_svn/svnet/sipc4.c @@ -0,0 +1,2119 @@ +/** + * SAMSUNG MODEM IPC version 4 + * + * Copyright (C) 2010 Samsung Electronics. 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 + * version 2 as published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +/* #define DEBUG */ + +#if defined(DEBUG) +#define NOISY_DEBUG +#endif + +#include "pdp.h" +#include "sipc.h" +#include "sipc4.h" +#include "main.h" +#include <linux/vmalloc.h> +#include <linux/circ_buf.h> +#include <linux/workqueue.h> +#include <linux/errno.h> + +#include <net/sock.h> +#include <linux/if_ether.h> +#include <linux/phonet.h> +#include <net/phonet/phonet.h> + +#if defined(CONFIG_PHONE_IPC_SPI) +#include <linux/phone_svn/ipc_spi.h> +#else +#if defined(CONFIG_PHONE_IPC_HSI) +#include <linux/phone_svn/ipc_hsi.h> +#endif +#endif + +#if defined(CONFIG_KERNEL_DEBUG_SEC) +#include <linux/kernel_sec_common.h> +#define ERRMSG "Unknown CP Crash" +static char cp_errmsg[65]; +static void _go_dump(struct sipc *si); +#else +#define _go_dump(si) do { } while (0) +#endif + +#if defined(NOISY_DEBUG) +static struct device *_dev; +# define _dbg(format, arg...) \ + dev_dbg(_dev, format, ## arg) +#else +# define _dbg(format, arg...) \ + do { } while (0) +#endif + +const char *sipc_version = "4.1"; + +static const char hdlc_start[1] = { HDLC_START }; +static const char hdlc_end[1] = { HDLC_END }; + +struct mailbox_data { + u16 mask_send; + u16 mask_req_ack; + u16 mask_res_ack; +}; + +static struct mailbox_data mb_data[IPCIDX_MAX] = { + { + .mask_send = MBD_SEND_FMT, + .mask_req_ack = MBD_REQ_ACK_FMT, + .mask_res_ack = MBD_RES_ACK_FMT, + }, + { + .mask_send = MBD_SEND_RAW, + .mask_req_ack = MBD_REQ_ACK_RAW, + .mask_res_ack = MBD_RES_ACK_RAW, + }, + { + .mask_send = MBD_SEND_RFS, + .mask_req_ack = MBD_REQ_ACK_RFS, + .mask_res_ack = MBD_RES_ACK_RFS, + }, +}; + +/* semaphore latency */ +unsigned long long time_max_semlat; + +struct sipc; +struct ringbuf; + +struct ringbuf_info { + unsigned int out_off; + unsigned int in_off; + unsigned int size; + int (*read)(struct sipc *si, int inbuf, struct ringbuf *rb); +}; + +struct ringbuf { + unsigned char *out_base; + unsigned char *in_base; + struct ringbuf_cont *cont; + struct ringbuf_info *info; +}; +/* +#define rb_size info->size +#define rb_read info->read +#define rb_out_head cont->out_head +#define rb_out_tail cont->out_tail +#define rb_in_head cont->in_head +#define rb_in_tail cont->in_tail +*/ + +static int _read_fmt(struct sipc *si, int inbuf, struct ringbuf *rb); +static int _read_raw(struct sipc *si, int inbuf, struct ringbuf *rb); +static int _read_rfs(struct sipc *si, int inbuf, struct ringbuf *rb); + +static struct ringbuf_info rb_info[IPCIDX_MAX] = { + { + .out_off = FMT_OUT, + .in_off = FMT_IN, + .size = FMT_SZ, + .read = _read_fmt, + }, + { + .out_off = RAW_OUT, + .in_off = RAW_IN, + .size = RAW_SZ, + .read = _read_raw, + }, + { + .out_off = RFS_OUT, + .in_off = RFS_IN, + .size = RFS_SZ, + .read = _read_rfs, + }, +}; + +#define FRAG_BLOCK_MAX (PAGE_SIZE - sizeof(struct list_head) \ + - sizeof(u32) - sizeof(char *)) +struct frag_block { + struct list_head list; + u32 len; + char *ptr; + char buf[FRAG_BLOCK_MAX]; +}; + +struct frag_list { + struct list_head list; + u8 msg_id; + u32 len; + + struct list_head block_head; +}; + +struct frag_head { + struct list_head head; + unsigned long bitmap[FMT_ID_SIZE/BITS_PER_LONG]; +}; + +struct frag_info { + struct sk_buff *skb; + unsigned int offset; + u8 msg_id; +}; + +struct sipc { + struct sipc_mapped *map; + struct ringbuf rb[IPCIDX_MAX]; + + struct resource *res; + + void (*queue)(u32, void *); + void *queue_data; + + /* for fragmentation */ + u8 msg_id; + char *frag_buf; + struct frag_info frag; + + /* for merging */ + struct frag_head frag_map; + + int od_rel; /* onedram authority release */ + + struct net_device *svndev; + + const struct attribute_group *group; + + struct sk_buff_head rfs_rx; +}; + +/* sizeof(struct phonethdr) + NET_SKB_PAD > SMP_CACHE_BYTES */ + +/* SMP_CACHE_BYTES > sizeof(struct phonethdr) + NET_SKB_PAD */ +#define RFS_MTU (PAGE_SIZE - SMP_CACHE_BYTES) +#define RFS_TX_RATE 4 + +/* set at storage device */ +unsigned int factory_test_force_sleep; +EXPORT_SYMBOL(factory_test_force_sleep); + +/* TODO: move PDP related codes to other source file */ +static DEFINE_MUTEX(pdp_mutex); +static struct net_device *pdp_devs[PDP_MAX]; +static int pdp_cnt; +unsigned long pdp_bitmap[PDP_MAX/BITS_PER_LONG]; + +static void clear_pdp_wq(struct work_struct *work); +static DECLARE_WORK(pdp_work, clear_pdp_wq); + +static ssize_t show_act(struct device *d, + struct device_attribute *attr, char *buf); +static ssize_t show_deact(struct device *d, + struct device_attribute *attr, char *buf); +static ssize_t store_act(struct device *d, + struct device_attribute *attr, const char *buf, size_t count); +static ssize_t store_deact(struct device *d, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t show_suspend(struct device *d, + struct device_attribute *attr, char *buf); +static ssize_t store_suspend(struct device *d, + struct device_attribute *attr, const char *buf, size_t count); +static ssize_t store_resume(struct device *d, + struct device_attribute *attr, const char *buf, size_t count); + +static DEVICE_ATTR(activate, S_IRUGO | S_IWUSR | S_IWGRP, + show_act, store_act); +static DEVICE_ATTR(deactivate, S_IRUGO | S_IWUSR | S_IWGRP, + show_deact, store_deact); +static DEVICE_ATTR(suspend, S_IRUGO | S_IWUSR | S_IWGRP, + show_suspend, store_suspend); +static DEVICE_ATTR(resume, S_IRUGO | S_IWUSR | S_IWGRP, NULL, + store_resume); + +static struct attribute *pdp_attributes[] = { + &dev_attr_activate.attr, + &dev_attr_deactivate.attr, + &dev_attr_suspend.attr, + &dev_attr_resume.attr, + NULL +}; + +static const struct attribute_group pdp_group = { + .name = "pdp", + .attrs = pdp_attributes, +}; + + +#if defined(NOISY_DEBUG) +#define DUMP_LIMIT 32 +static char dump_buf[64]; +void _dbg_dump(u8 *buf, int size) +{ + int i; + int len = 0; + + if (!buf) + return; + + if (size > DUMP_LIMIT) + size = DUMP_LIMIT; + + for (i = 0; i < 32 && i < size; i++) { + len += sprintf(&dump_buf[len], "%02x ", buf[i]); + if ((i & 0xf) == 0xf) { + dump_buf[len] = '\0'; + _dbg("dump %04x [ %s]\n", (i>>4), dump_buf); + len = 0; + } + } + if (len) { + dump_buf[len] = '\0'; + _dbg("dump %04x [ %s]\n", i, dump_buf); + } +} +#else +# define _dbg_dump(buf, size) do { } while (0) +#endif + +static int cp_state = 1; +void modem_state_changed(int state) +{ + printk(KERN_ERR "cp state change. state : %d\n", state); + + cp_state = state; +} +EXPORT_SYMBOL(modem_state_changed); + +static int _get_auth(void) +{ + int r; + unsigned long long t, d; + + t = cpu_clock(smp_processor_id()); + + r = onedram_get_auth(MB_CMD(MBC_REQ_SEM)); + + d = cpu_clock(smp_processor_id()) - t; + if (d > time_max_semlat) + time_max_semlat = d; + + return r; +} + +static void _put_auth(struct sipc *si) +{ + if (!si) + return; + + onedram_put_auth(0); + + if (si->od_rel && !onedram_rel_sem()) { + onedram_write_mailbox(MB_CMD(MBC_RES_SEM)); + si->od_rel = 0; + } +} + +static inline void _req_rel_auth(struct sipc *si) +{ + si->od_rel = 1; +} + +static int _get_auth_try(void) +{ + return onedram_get_auth(0); +} + +static void _check_buffer(struct sipc *si) +{ + int i; + u32 mailbox; + +#if 0 + i = onedram_read_sem(); + if (i != 0x1) + return; +#endif + i = _get_auth_try(); + if (i) + return; + + mailbox = 0; + + for (i = 0 ; i < IPCIDX_MAX; i++) { + int inbuf; + struct ringbuf *rb; + + rb = &si->rb[i]; + inbuf = CIRC_CNT(rb->cont->in_head, + rb->cont->in_tail, rb->info->size); + if (!inbuf) + continue; + + mailbox |= mb_data[i].mask_send; + } + _put_auth(si); + + if (mailbox) + si->queue(MB_DATA(mailbox), si->queue_data); +} + +static void _do_command(struct sipc *si, u32 mailbox) +{ + int r; + u32 cmd = (mailbox & MBC_MASK) & ~(MB_CMD(0)); + + switch (cmd) { + case MBC_REQ_SEM: + r = onedram_rel_sem(); + if (r) { + dev_dbg(&si->svndev->dev, "onedram in use, " + "defer releasing semaphore\n"); + _req_rel_auth(si); + } else + onedram_write_mailbox(MB_CMD(MBC_RES_SEM)); + break; + case MBC_RES_SEM: + /* do nothing */ + break; + case MBC_PHONE_START: + onedram_write_mailbox(MB_CMD(MBC_INIT_END) | CP_BOOT_AIRPLANE + | AP_OS_ANDROID); + break; + case MBC_RESET: + printk(KERN_ERR "svnet reset mailbox msg : 0x%08x\n", mailbox); + si->queue(SIPC_RESET_MB, si->queue_data); + break; + case MBC_ERR_DISPLAY: + printk(KERN_ERR "svnet error display mailbox msg : 0x%08x\n", + mailbox); + si->queue(SIPC_EXIT_MB, si->queue_data); + break; + /* TODO : impletment other commands... */ + default: + /* do nothing */ + + break; + } +} + +void sipc_handler(u32 mailbox, void *data) +{ + struct sipc *si = (struct sipc *)data; + + if (!si || !si->queue) + return; + + dev_dbg(&si->svndev->dev, "recv mailbox %x\n", mailbox); + +#if defined(CONFIG_KERNEL_DEBUG_SEC) + if (mailbox == KERNEL_SEC_DUMP_AP_DEAD_ACK) + kernel_sec_set_cp_ack(); +#endif + + if ((mailbox & MB_VALID) == 0) { + dev_err(&si->svndev->dev, "Invalid mailbox message: %x\n", + mailbox); + return; + } + + if (mailbox & MB_COMMAND) { + _check_buffer(si); + _do_command(si, mailbox); + return; + } + + si->queue(mailbox, si->queue_data); +} + +static inline void _init_data(struct sipc *si, unsigned char *base) +{ + int i; + + si->map = (struct sipc_mapped *)base; + si->map->magic = 0x0; + si->map->access = 0x0; + + for (i = 0; i < IPCIDX_MAX; i++) { + struct ringbuf *r = &si->rb[i]; + struct ringbuf_info *info = &rb_info[i]; + struct ringbuf_cont *cont = &si->map->rbcont[i]; + + r->out_base = base + info->out_off; + r->in_base = base + info->in_off; + r->info = info; + r->cont = cont; + + cont->out_head = 0; + cont->out_tail = 0; + cont->in_head = 0; + cont->in_tail = 0; + } +} + +static void _init_proc(struct sipc *si) +{ + u32 mailbox; + int r; + + dev_dbg(&si->svndev->dev, "%s\n", __func__); + r = onedram_read_mailbox(&mailbox); + if (r) + return; + + mailbox = 0xc8; + sipc_handler(mailbox, si); +} + +struct sipc *sipc_open(void (*queue)(u32, void*), struct net_device *ndev) +{ + struct sipc *si; + struct resource *res; + int r; + void *onedram_vbase; + + if (!queue || !ndev) + return ERR_PTR(-EINVAL); + + si = kzalloc(sizeof(struct sipc), GFP_KERNEL); + if (!si) + return ERR_PTR(-ENOMEM); + + /* If FMT_SZ grown up, MUST be changed!! */ + si->frag_buf = vmalloc(FMT_SZ); + memset(si->frag_buf, 0x00, FMT_SZ); + if (!si->frag_buf) { + sipc_close(&si); + return ERR_PTR(-ENOMEM); + } + INIT_LIST_HEAD(&si->frag_map.head); + + res = onedram_request_region(0, SIPC_MAP_SIZE, SIPC_NAME); + if (!res) { + sipc_close(&si); + return ERR_PTR(-EBUSY); + } + si->res = res; + + r = onedram_register_handler(sipc_handler, si); + if (r) { + sipc_close(&si); + return ERR_PTR(r); + } + si->queue = queue; + si->queue_data = ndev; + si->svndev = ndev; + + /* TODO: need?? */ + if (work_pending(&pdp_work)) + flush_work(&pdp_work); + + r = sysfs_create_group(&si->svndev->dev.kobj, &pdp_group); + if (r) { + sipc_close(&si); + return ERR_PTR(r); + } + si->group = &pdp_group; + +#if defined(NOISY_DEBUG) + _dev = &si->svndev->dev; +#endif + + onedram_get_vbase(&onedram_vbase); + + if (onedram_vbase) + _init_data(si, (unsigned char *)onedram_vbase); + else + _init_data(si, (unsigned char *)res->start); + + skb_queue_head_init(&si->rfs_rx); + + /* process init message */ + _init_proc(si); + + dev_dbg(&si->svndev->dev, "sipc_open Done.\n"); + + return si; +} + +static void clear_pdp_wq(struct work_struct *work) +{ + int i; + + mutex_lock(&pdp_mutex); + + for (i = 0; i < sizeof(pdp_devs)/sizeof(pdp_devs[0]); i++) { + if (pdp_devs[i]) { + destroy_pdp(&pdp_devs[i]); + clear_bit(i, pdp_bitmap); + } + } + pdp_cnt = 0; + + mutex_unlock(&pdp_mutex); +} + +void sipc_exit(void) +{ + if (work_pending(&pdp_work)) + flush_work(&pdp_work); + else + clear_pdp_wq(NULL); +} + +void sipc_close(struct sipc **psi) +{ + struct sipc *si; + + if (!psi || !*psi) + return; + + si = *psi; + + if (si->group && si->svndev) { + int i; + sysfs_remove_group(&si->svndev->dev.kobj, si->group); + + mutex_lock(&pdp_mutex); + for (i = 0; i < sizeof(pdp_devs)/sizeof(pdp_devs[0]); i++) { + if (pdp_devs[i]) + netif_stop_queue(pdp_devs[i]); + } + mutex_unlock(&pdp_mutex); + schedule_work(&pdp_work); + } + + if (si->frag_buf) + vfree(si->frag_buf); + + if (si->queue) + onedram_unregister_handler(sipc_handler); + + if (si->res) + onedram_release_region(0, SIPC_MAP_SIZE); + + kfree(si); + *psi = NULL; +} + +static inline void _wake_queue(int idx) +{ + mutex_lock(&pdp_mutex); + + if (pdp_devs[idx] && !test_bit(idx, pdp_bitmap)) + netif_wake_queue(pdp_devs[idx]); + + mutex_unlock(&pdp_mutex); +} + +static int __write(struct ringbuf *rb, u8 *buf, unsigned int size) +{ + int c; + int len = 0; + + if (!cp_state) { + printk(KERN_ERR "__write : cp_state : %d\n", cp_state); + return -EPERM; + } + + _dbg("%s b: size %u head %u tail %u\n", __func__, + size, rb->cont->out_head, rb->cont->out_tail); + _dbg_dump(buf, size); + + while (1) { + c = CIRC_SPACE_TO_END(rb->cont->out_head, + rb->cont->out_tail, rb->info->size); + if (size < c) + c = size; + if (c <= 0) + break; + memcpy(rb->out_base + rb->cont->out_head, buf, c); + rb->cont->out_head = (rb->cont->out_head + c) + & (rb->info->size - 1); + buf += c; + size -= c; + len += c; + } + + _dbg("%s a: size %u head %u tail %u\n", __func__, + len, rb->cont->out_head, rb->cont->out_tail); + + return len; +} + +static inline void _set_raw_hdr(struct raw_hdr *h, int res, + unsigned int len, int control) +{ + h->len = len; + h->channel = CHID(res); + h->control = 0; +} + +static int _write_raw_buf(struct ringbuf *rb, int res, struct sk_buff *skb) +{ + int len; + struct raw_hdr h; + + _dbg("%s: packet %p res 0x%02x\n", __func__, skb, res); + + len = skb->len + sizeof(h); + + _set_raw_hdr(&h, res, len, 0); + + len = __write(rb, (u8 *)hdlc_start, sizeof(hdlc_start)); + len += __write(rb, (u8 *)&h, sizeof(h)); + len += __write(rb, skb->data, skb->len); + len += __write(rb, (u8 *)hdlc_end, sizeof(hdlc_end)); + + return len; +} + +static int _write_raw_skb(struct ringbuf *rb, int res, struct sk_buff *skb) +{ + char *b; + + if (!cp_state) { + printk(KERN_ERR "_write_raw_skb : cp_state : %d\n", cp_state); + return -EPERM; + } + + _dbg("%s: packet %p res 0x%02x\n", __func__, skb, res); + + b = skb_put(skb, sizeof(hdlc_end)); + memcpy(b, hdlc_end, sizeof(hdlc_end)); + + b = skb_push(skb, sizeof(struct raw_hdr) + sizeof(hdlc_start)); + memcpy(b, hdlc_start, sizeof(hdlc_start)); + + b += sizeof(hdlc_start); + + _set_raw_hdr((struct raw_hdr *)b, res, + skb->len - sizeof(hdlc_start) - sizeof(hdlc_end), 0); + + return __write(rb, skb->data, skb->len); +} + +static int _write_raw(struct ringbuf *rb, struct sk_buff *skb, int res) +{ + int len; + int space; + + space = CIRC_SPACE(rb->cont->out_head, + rb->cont->out_tail, rb->info->size); + if (space < skb->len + sizeof(struct raw_hdr) + + sizeof(hdlc_start) + sizeof(hdlc_end)) + return -ENOSPC; + mutex_lock(&pdp_mutex); + if (skb_headroom(skb) > (sizeof(struct raw_hdr) + sizeof(hdlc_start)) + && skb_tailroom(skb) > sizeof(hdlc_end)) { + len = _write_raw_skb(rb, res, skb); + } else { + len = _write_raw_buf(rb, res, skb); + } + mutex_unlock(&pdp_mutex); + if (res >= PN_PDP_START && res <= PN_PDP_END) + _wake_queue(PDP_ID(res)); + else + netif_wake_queue(skb->dev); + return len; +} + +static int _write_rfs_buf(struct ringbuf *rb, struct sk_buff *skb) +{ + int len; + + _dbg("%s: packet %p\n", __func__, skb); + len = __write(rb, (u8 *)hdlc_start, sizeof(hdlc_start)); + len += __write(rb, skb->data, skb->len); + len += __write(rb, (u8 *)hdlc_end, sizeof(hdlc_end)); + + return len; +} + +static int _write_rfs_skb(struct ringbuf *rb, struct sk_buff *skb) +{ + char *b; + + if (!cp_state) { + printk(KERN_ERR "_write_rfs_skb : cp_state : %d\n", cp_state); + return -EPERM; + } + + _dbg("%s: packet %p\n", __func__, skb); + b = skb_put(skb, sizeof(hdlc_end)); + memcpy(b, hdlc_end, sizeof(hdlc_end)); + + b = skb_push(skb, sizeof(hdlc_start)); + memcpy(b, hdlc_start, sizeof(hdlc_start)); + + return __write(rb, skb->data, skb->len); +} + +static int _write_rfs(struct ringbuf *rb, struct sk_buff *skb) +{ + int len; + int space; + + space = CIRC_SPACE(rb->cont->out_head, + rb->cont->out_tail, rb->info->size); + if (space < skb->len + sizeof(hdlc_start) + sizeof(hdlc_end)) + return -ENOSPC; + + if (skb_headroom(skb) > sizeof(hdlc_start) + && skb_tailroom(skb) > sizeof(hdlc_end)) { + len = _write_rfs_skb(rb, skb); + } else { + len = _write_rfs_buf(rb, skb); + } + + netif_wake_queue(skb->dev); + return len; +} + +static int _write_fmt_buf(char *frag_buf, struct ringbuf *rb, + struct sk_buff *skb, struct frag_info *fi, int wlen, + u8 control) +{ + char *buf = frag_buf; + struct fmt_hdr *h; + + if (!cp_state) { + printk(KERN_ERR "_write_fmt_buf : cp_state : %d\n", cp_state); + return -EPERM; + } + + memcpy(buf, hdlc_start, sizeof(hdlc_start)); + buf += sizeof(hdlc_start); + + h = (struct fmt_hdr *)buf; + h->len = sizeof(struct fmt_hdr) + wlen; + h->control = control; + buf += sizeof(struct fmt_hdr); + + memcpy(buf, skb->data + fi->offset, wlen); + buf += wlen; + + memcpy(buf, hdlc_end, sizeof(hdlc_end)); + buf += sizeof(hdlc_end); + + return __write(rb, frag_buf, buf - frag_buf); +} + +static int _write_fmt(struct sipc *si, struct ringbuf *rb, struct sk_buff *skb) +{ + int len; + int space; + int remain; + struct frag_info *fi = &si->frag; + + if (skb != fi->skb) { + /* new packet */ + fi->skb = skb; + fi->offset = 0; + } + + len = 0; + remain = skb->len - fi->offset; + + _dbg("%s: packet %p length %d sent %d\n", + __func__, skb, skb->len, fi->offset); + + while (remain > 0) { + int wlen; + u8 control; + + space = CIRC_SPACE(rb->cont->out_head, + rb->cont->out_tail, rb->info->size); + space -= sizeof(struct fmt_hdr) + + sizeof(hdlc_start) + sizeof(hdlc_end); + if (space < FMT_TX_MIN) + return -ENOSPC; + + if (remain > space) { + /* multiple frame */ + wlen = space; + control = 0x1 | FMT_MB_MASK; + } else { + wlen = remain; + if (fi->offset == 0) { + /* single frame */ + control = 0x0; + } else { + /* last frmae */ + control = 0x1; + } + } + + wlen = _write_fmt_buf(si->frag_buf, rb, skb, fi, wlen, control); + if (wlen < 0) + return wlen; + + len += wlen; + + wlen -= sizeof(hdlc_start) + sizeof(struct fmt_hdr) + + sizeof(hdlc_end); + + fi->offset += wlen; + remain -= wlen; + } + + if (len > 0) { + fi->skb = NULL; + fi->offset = 0; + } + + netif_wake_queue(skb->dev); + return len; /* total write bytes */ +} + +static int _write(struct sipc *si, int res, struct sk_buff *skb, u32 *mailbox) +{ + int r; + int rid; + + rid = res_to_ridx(res); + if (rid < 0 || rid >= IPCIDX_MAX) + return -EINVAL; + + switch (rid) { + case IPCIDX_FMT: + r = _write_fmt(si, &si->rb[rid], skb); + break; + case IPCIDX_RAW: + r = _write_raw(&si->rb[rid], skb, res); + break; + case IPCIDX_RFS: + r = _write_rfs(&si->rb[rid], skb); + break; + default: + /* do nothing */ + r = 0; + break; + } + + if (r > 0) + *mailbox |= mb_data[rid].mask_send; + + _dbg("%s: return %d\n", __func__, r); + return r; +} + +static inline void _update_stat(struct net_device *ndev, unsigned int len) +{ + if (!ndev) + return; + + ndev->stats.tx_bytes += len; + ndev->stats.tx_packets++; +} + +static inline int _write_pn(struct sipc *si, struct sk_buff *skb, u32 *mb) +{ + int r; + struct phonethdr *ph; + + ph = pn_hdr(skb); + skb_pull(skb, sizeof(struct phonethdr) + 1); + + r = _write(si, ph->pn_res, skb, mb); + if (r < 0) + skb_push(skb, sizeof(struct phonethdr) + 1); + + return r; +} + +int sipc_write(struct sipc *si, struct sk_buff_head *sbh) +{ + int r; + u32 mailbox; + struct sk_buff *skb; + + if (!sbh) + return -EINVAL; + + if (!si) { + skb_queue_purge(sbh); + return -ENXIO; + } + + r = _get_auth(); + if (r) { + if (factory_test_force_sleep) { + printk(KERN_ERR "tx ignored for factory force sleep\n"); + skb_queue_purge(sbh); + return 0; + } else { + return r; + } + } + + r = mailbox = 0; + skb = skb_dequeue(sbh); + while (skb) { + struct net_device *ndev = skb->dev; + int len = skb->len; + + dev_dbg(&si->svndev->dev, "write packet %p\n", skb); + + if (skb->protocol != __constant_htons(ETH_P_PHONET)) { + struct pdp_priv *priv; + priv = netdev_priv(ndev); + r = _write(si, PN_PDP(priv->channel), skb, &mailbox); + } else + r = _write_pn(si, skb, &mailbox); + + if (r < 0) + break; + + _update_stat(ndev, len); + dev_kfree_skb_any(skb); + + skb = skb_dequeue(sbh); + } + + _req_rel_auth(si); + _put_auth(si); + + if (mailbox) + onedram_write_mailbox(MB_DATA(mailbox)); + + if (r < 0) { + if (r == -ENOSPC) { + dev_err(&si->svndev->dev, + "write nospc queue %p\n", skb); + skb_queue_head(sbh, skb); + netif_stop_queue(skb->dev); + } else { + dev_err(&si->svndev->dev, + "write err %d, drop %p\n", r, skb); + dev_kfree_skb_any(skb); + } + } + + return r; +} + +extern int __read(struct ringbuf *rb, unsigned char *buf, unsigned int size) +{ + int c; + int len = 0; + unsigned char *p = buf; + + if (!cp_state) { + printk(KERN_ERR "__read : cp_state : %d\n", cp_state); + return -EPERM; + } + + _dbg("%s b: size %u head %u tail %u\n", __func__, + size, rb->cont->in_head, rb->cont->in_tail); + + while (1) { + c = CIRC_CNT_TO_END(rb->cont->in_head, + rb->cont->in_tail, rb->info->size); + if (size < c) + c = size; + if (c <= 0) + break; + if (p) { + memcpy(p, rb->in_base + rb->cont->in_tail, c); + p += c; + } + rb->cont->in_tail = (rb->cont->in_tail + c) + & (rb->info->size - 1); + size -= c; + len += c; + } + + _dbg("%s a: size %u head %u tail %u\n", __func__, + len, rb->cont->in_head, rb->cont->in_tail); + _dbg_dump(buf, len); + + return len; +} + +static inline void _get_raw_hdr(struct raw_hdr *h, int *res, + unsigned int *len, int *control) +{ + if (res) + *res = PN_RAW(h->channel); + if (len) + *len = h->len; + if (control) + *control = h->control; +} + +static inline void _phonet_rx(struct net_device *ndev, + struct sk_buff *skb, int res) +{ + int r; + struct phonethdr *ph; + + skb->protocol = __constant_htons(ETH_P_PHONET); + + ph = (struct phonethdr *)skb_push(skb, sizeof(struct phonethdr)); + ph->pn_rdev = ndev->dev_addr[0]; + ph->pn_sdev = 0; + ph->pn_res = res; + ph->pn_length = __cpu_to_be16(skb->len + 2 - sizeof(*ph)); + ph->pn_robj = 0; + ph->pn_sobj = 0; + + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += skb->len; + + skb_reset_mac_header(skb); + + r = netif_rx_ni(skb); + if (r != NET_RX_SUCCESS) + dev_err(&ndev->dev, "phonet rx error: %d\n", r); + + _dbg("%s: res 0x%02x packet %p len %d\n", __func__, res, skb, skb->len); +} + +static int _read_pn(struct net_device *ndev, struct ringbuf *rb, int len, + int res) +{ + int r; + struct sk_buff *skb; + char *p; + int read_len = len + sizeof(hdlc_end); + + _dbg("%s: res 0x%02x data %d\n", __func__, res, len); + + skb = netdev_alloc_skb(ndev, read_len + sizeof(struct phonethdr)); + if (unlikely(!skb)) + return -ENOMEM; + + skb_reserve(skb, sizeof(struct phonethdr)); + + p = skb_put(skb, len); + r = __read(rb, p, read_len); + if (r != read_len) { + kfree_skb(skb); + return -EBADMSG; + } + + _phonet_rx(ndev, skb, res); + + return r; +} + +static inline struct sk_buff *_alloc_phskb(struct net_device *ndev, int len) +{ + struct sk_buff *skb; + + skb = netdev_alloc_skb(ndev, len + sizeof(struct phonethdr)); + if (likely(skb)) + skb_reserve(skb, sizeof(struct phonethdr)); + + return skb; +} + +static inline int _alloc_rfs(struct net_device *ndev, + struct sk_buff_head *list, int len) +{ + int r = 0; + struct sk_buff *skb; + + __skb_queue_head_init(list); + + while (len > 0) { + skb = _alloc_phskb(ndev, RFS_MTU); + if (unlikely(!skb)) { + r = -ENOMEM; + break; + } + __skb_queue_tail(list, skb); + len -= RFS_MTU; + } + + return r; +} +static void _free_rfs(struct sk_buff_head *list) +{ + struct sk_buff *skb; + + skb = __skb_dequeue(list); + while (skb) { + __kfree_skb(skb); + skb = __skb_dequeue(list); + } +} + +static inline int _read_rfs_rb(struct ringbuf *rb, int len, + struct sk_buff_head *list) +{ + int r; + int read_len; + struct sk_buff *skb; + char *p; + + read_len = 0; + skb = list->next; + while (skb != (struct sk_buff *)list) { + int rd = RFS_MTU; + + if (skb == list->next) /* first sk has header */ + rd -= sizeof(struct rfs_hdr); + + if (len < rd) + rd = len; + + p = skb_put(skb, rd); + r = __read(rb, p, rd); + if (r != rd) + return -EBADMSG; + + len -= r; + read_len += r; + skb = skb->next; + } + + return read_len; +} + +static int _read_rfs_data(struct sipc *si, struct ringbuf *rb, int len, + struct rfs_hdr *h) +{ + int r; + struct sk_buff_head list; + struct sk_buff *skb; + char *p; + int read_len; + struct net_device *ndev = si->svndev; + + if (!cp_state) { + printk(KERN_ERR "_read_rfs_data : cp_state : %d\n", cp_state); + return -EPERM; + } + + _dbg("%s: %d bytes\n", __func__, len); + + /* alloc sk_buffs */ + r = _alloc_rfs(ndev, &list, len + sizeof(struct rfs_hdr)); + if (r) + goto free_skb; + + skb = list.next; + p = skb_put(skb, sizeof(struct rfs_hdr)); + memcpy(p, h, sizeof(struct rfs_hdr)); + + /* read data all */ + r = _read_rfs_rb(rb, len, &list); + if (r < 0) + goto free_skb; + + read_len = r; + + /* move to rfs_rx queue */ + skb = __skb_dequeue(&list); + while (skb) { + skb_queue_tail(&si->rfs_rx, skb); + skb = __skb_dequeue(&list); + } + + /* remove hdlc_end */ + read_len += __read(rb, NULL, sizeof(hdlc_end)); + + return read_len; + +free_skb: + _free_rfs(&list); + return r; +} + +static int _read_pdp(struct ringbuf *rb, int len, + int res) +{ + int r; + struct sk_buff *skb; + char *p; + int read_len = len + sizeof(hdlc_end); + struct net_device *ndev; + + _dbg("%s: res 0x%02x data %d\n", __func__, res, len); + + mutex_lock(&pdp_mutex); + + ndev = pdp_devs[PDP_ID(res)]; + if (!ndev) { + /* drop data */ + r = __read(rb, NULL, read_len); + mutex_unlock(&pdp_mutex); + return r; + } + + skb = netdev_alloc_skb(ndev, read_len); + if (unlikely(!skb)) { + mutex_unlock(&pdp_mutex); + return -ENOMEM; + } + + p = skb_put(skb, len); + r = __read(rb, p, read_len); + if (r != read_len) { + mutex_unlock(&pdp_mutex); + kfree_skb(skb); + return -EBADMSG; + } + ndev->stats.rx_packets++; + ndev->stats.rx_bytes += skb->len; + + mutex_unlock(&pdp_mutex); + + read_len = r; + + skb->protocol = __constant_htons(ETH_P_IP); + + skb_reset_mac_header(skb); + + _dbg("%s: pdp packet %p len %d\n", __func__, skb, skb->len); + + r = netif_rx_ni(skb); + if (r != NET_RX_SUCCESS) + dev_err(&ndev->dev, "pdp rx error: %d\n", r); + + return read_len; +} + +static int _read_raw(struct sipc *si, int inbuf, struct ringbuf *rb) +{ + int r; + char buf[sizeof(struct raw_hdr) + sizeof(hdlc_start)]; + int res, data_len; + u32 tail; + + while (inbuf > 0) { + tail = rb->cont->in_tail; + + r = __read(rb, buf, sizeof(buf)); + if (r < sizeof(buf) || + strncmp(buf, hdlc_start, sizeof(hdlc_start))) { + dev_err(&si->svndev->dev, "Bad message: %c %d\n", + buf[0], r); + return -EBADMSG; + } + inbuf -= r; + + _get_raw_hdr((struct raw_hdr *)&buf[sizeof(hdlc_start)], + &res, &data_len, NULL); + + data_len -= sizeof(struct raw_hdr); + + if (res >= PN_PDP_START && res <= PN_PDP_END) + r = _read_pdp(rb, data_len, res); + else + r = _read_pn(si->svndev, rb, data_len, res); + + if (r < 0) { + if (r == -ENOMEM) + rb->cont->in_tail = tail; + + return r; + } + + inbuf -= r; + } + + return 0; +} + +static int _read_rfs(struct sipc *si, int inbuf, struct ringbuf *rb) +{ + int r; + char buf[sizeof(struct rfs_hdr) + sizeof(hdlc_start)]; + int data_len; + u32 tail; + struct rfs_hdr *h; + + h = (struct rfs_hdr *)&buf[sizeof(hdlc_start)]; + while (inbuf > 0) { + tail = rb->cont->in_tail; + + r = __read(rb, buf, sizeof(buf)); + if (r < sizeof(buf) || + strncmp(buf, hdlc_start, sizeof(hdlc_start))) { + dev_err(&si->svndev->dev, "Bad message: %c %d\n", + buf[0], r); + return -EBADMSG; + } + inbuf -= r; + + data_len = h->len - sizeof(struct rfs_hdr); + + r = _read_rfs_data(si, rb, data_len, h); + if (r < 0) { + if (r == -ENOMEM) + rb->cont->in_tail = tail; + + return r; + } + + inbuf -= r; + } + + return 0; +} + + +static struct frag_list *_find_frag_list(u8 control, struct frag_head *fh) +{ + struct frag_list *fl; + u8 msg_id = control & FMT_ID_MASK; + + if (!test_bit(msg_id, fh->bitmap)) + return NULL; + + list_for_each_entry(fl, &fh->head, list) { + if (fl->msg_id == msg_id) + break; + } + + return fl; +} + +static int _fill_skb(struct sk_buff *skb, struct frag_list *fl) +{ + struct frag_block *fb, *n; + int offset = 0; + char *p; + + if (!cp_state) { + printk(KERN_ERR "_fill_skb : cp_state : %d\n", cp_state); + return -EPERM; + } + + list_for_each_entry_safe(fb, n, &fl->block_head, list) { + p = skb_put(skb, fb->len); + memcpy(p, fb->buf, fb->len); + offset += fb->len; + list_del(&fb->list); + kfree(fb); + } + + return offset; +} + +static void _destroy_frag_list(struct frag_list *fl, struct frag_head *fh) +{ + struct frag_block *fb, *n; + + if (!fl || !fh) + return; + + list_for_each_entry_safe(fb, n, &fl->block_head, list) { + kfree(fb); + } + + clear_bit(fl->msg_id, fh->bitmap); + list_del(&fl->list); + kfree(fl); +} + +static struct frag_list *_create_frag_list(u8 control, struct frag_head *fh) +{ + struct frag_list *fl; + u8 msg_id = control & FMT_ID_MASK; + + if (test_bit(msg_id, fh->bitmap)) { + fl = _find_frag_list(control, fh); + _destroy_frag_list(fl, fh); + } + + fl = kmalloc(sizeof(struct frag_list), GFP_KERNEL); + if (!fl) + return NULL; + + INIT_LIST_HEAD(&fl->block_head); + fl->msg_id = msg_id; + fl->len = 0; + list_add(&fl->list, &fh->head); + set_bit(msg_id, fh->bitmap); + + return fl; +} + +static inline struct frag_block *_create_frag_block(struct frag_list *fl) +{ + struct frag_block *fb; + + fb = kmalloc(sizeof(struct frag_block), GFP_KERNEL); + if (!fb) + return NULL; + + fb->len = 0; + fb->ptr = fb->buf; + list_add_tail(&fb->list, &fl->block_head); + + return fb; +} + +static struct frag_block *_prepare_frag_block(struct frag_list *fl, int size) +{ + struct frag_block *fb; + + if (size > FRAG_BLOCK_MAX) + BUG(); + + if (list_empty(&fl->block_head)) { + fb = _create_frag_block(fl); + } else { + fb = list_entry(fl->block_head.prev, struct frag_block, list); + if (size > FRAG_BLOCK_MAX - fb->len) + fb = _create_frag_block(fl); + } + + return fb; +} + +static int _read_fmt_frag(struct frag_head *fh, struct fmt_hdr *h, + struct ringbuf *rb) +{ + int r; + int data_len; + int read_len; + struct frag_list *fl; + struct frag_block *fb; + + data_len = h->len - sizeof(struct fmt_hdr); + read_len = data_len + sizeof(hdlc_end); + + _dbg("%s: data %d\n", __func__, data_len); + + fl = _find_frag_list(h->control, fh); + if (!fl) + fl = _create_frag_list(h->control, fh); + + if (!fl) + return -ENOMEM; + + fb = _prepare_frag_block(fl, read_len); + if (!fb) { + if (fl->len == 0) + _destroy_frag_list(fl, fh); + + return -ENOMEM; + } + + r = __read(rb, fb->ptr, read_len); + if (r != read_len) { + _destroy_frag_list(fl, fh); + return -EBADMSG; + } + + fb->ptr += data_len; + fb->len += data_len; + fl->len += data_len; + + _dbg("%s: fl %p len %d fb %p ptr %p len %d\n", __func__, + fl, fl->len, fb, fb->ptr, fb->len); + + return r; +} + +static int _read_fmt_last(struct frag_head *fh, struct fmt_hdr *h, + struct ringbuf *rb, struct net_device *ndev) +{ + int r; + int data_len; + int read_len; + int total_len; + struct sk_buff *skb; + struct frag_list *fl; + char *p; + + total_len = data_len = h->len - sizeof(struct fmt_hdr); + read_len = data_len + sizeof(hdlc_end); + + fl = _find_frag_list(h->control & FMT_ID_MASK, fh); + if (fl) + total_len += fl->len; + + _dbg("%s: total %d data %d\n", __func__, total_len, data_len); + + skb = netdev_alloc_skb(ndev, total_len + + sizeof(struct phonethdr) + sizeof(hdlc_end)); + if (unlikely(!skb)) + return -ENOMEM; + + skb_reserve(skb, sizeof(struct phonethdr)); + + if (fl) + _fill_skb(skb, fl); + + _destroy_frag_list(fl, fh); + + p = skb_put(skb, data_len); + r = __read(rb, p, read_len); + if (r != read_len) { + kfree_skb(skb); + return -EBADMSG; + } + + _phonet_rx(ndev, skb, PN_FMT); + + return r; +} + +static int _read_fmt(struct sipc *si, int inbuf, struct ringbuf *rb) +{ + int r; + char buf[sizeof(struct fmt_hdr) + sizeof(hdlc_start)]; + struct fmt_hdr *h; + u32 tail; + struct net_device *ndev = si->svndev; + + h = (struct fmt_hdr *)&buf[sizeof(hdlc_start)]; + while (inbuf > 0) { + tail = rb->cont->in_tail; + + r = __read(rb, buf, sizeof(buf)); + if (r < sizeof(buf) || + strncmp(buf, hdlc_start, sizeof(hdlc_start))) { + dev_err(&ndev->dev, "Bad message: %c %d\n", buf[0], r); + return -EBADMSG; + } + inbuf -= r; + + if (is_fmt_last(h->control)) + r = _read_fmt_last(&si->frag_map, h, rb, ndev); + else + r = _read_fmt_frag(&si->frag_map, h, rb); + + if (r < 0) { + if (r == -ENOMEM) + rb->cont->in_tail = tail; + + return r; + } + + inbuf -= r; + } + + return 0; +} + +static inline int check_mailbox(u32 mailbox, int idx) +{ + return mailbox & mb_data[idx].mask_send; +} + +static inline void purge_buffer(struct ringbuf *rb) +{ + rb->cont->in_tail = rb->cont->in_head; +} + +int sipc_read(struct sipc *si, u32 mailbox, int *cond) +{ + int r = 0; + int i; + u32 res = 0; + + if (!si) + return -EINVAL; + + r = _get_auth(); + if (r) + return r; + + for (i = 0; i < IPCIDX_MAX; i++) { + int inbuf; + struct ringbuf *rb; + +/* if (!check_mailbox(mailbox, i)) + continue; */ + + rb = &si->rb[i]; + inbuf = CIRC_CNT(rb->cont->in_head, + rb->cont->in_tail, rb->info->size); + if (!inbuf) + continue; + + if (i == IPCIDX_FMT) + _fmt_wakelock_timeout(); + else + _non_fmt_wakelock_timeout(); + + _dbg("%s: %d bytes in %d\n", __func__, inbuf, i); + + r = rb->info->read(si, inbuf, rb); + if (r < 0) { + if (r == -EBADMSG) + purge_buffer(rb); + + dev_err(&si->svndev->dev, "read err %d\n", r); + break; + } + + if (mailbox & mb_data[i].mask_req_ack) + res = mb_data[i].mask_res_ack; + } + + _req_rel_auth(si); + _put_auth(si); + + if (res) + onedram_write_mailbox(MB_DATA(res)); + + *cond = skb_queue_len(&si->rfs_rx); + + return r; +} + +int sipc_rx(struct sipc *si) +{ + int tx_cnt; + struct sk_buff *skb; + + if (!si) + return -EINVAL; + + if (skb_queue_len(&si->rfs_rx) == 0) + return 0; + + tx_cnt = 0; + skb = skb_dequeue(&si->rfs_rx); + while (skb) { + _phonet_rx(si->svndev, skb, PN_RFS); + tx_cnt++; + if (tx_cnt > RFS_TX_RATE) + break; + skb = skb_dequeue(&si->rfs_rx); + } + + return skb_queue_len(&si->rfs_rx); +} + +static inline ssize_t _debug_show_buf(struct sipc *si, char *buf) +{ + int i; + int r; + int inbuf, outbuf; + char *p = buf; + + r = _get_auth(); + if (r) { + p += sprintf(p, "\nGet authority: timed out!\n"); + return p - buf; + } + + p += sprintf(p, "\nHeader info ---------\n"); + + for (i = 0; i < IPCIDX_MAX; i++) { + struct ringbuf *rb = &si->rb[i]; + inbuf = CIRC_CNT(rb->cont->in_head, + rb->cont->in_tail, rb->info->size); + outbuf = CIRC_CNT(rb->cont->out_head, + rb->cont->out_tail, rb->info->size); + p += sprintf(p, "%d\tSize\t%8u\n\tIn\t%8u\t%8u\t%8u\n\tOut\t%8u\t%8u\t%8u\n", + i, rb->info->size, + rb->cont->in_head, rb->cont->in_tail, inbuf, + rb->cont->out_head, rb->cont->out_tail, outbuf); + } + _put_auth(si); + + return p - buf; +} + +static inline ssize_t _debug_show_pdp(struct sipc *si, char *buf) +{ + int i; + char *p = buf; + + p += sprintf(p, "\nPDP count: %d\n", pdp_cnt); + + mutex_lock(&pdp_mutex); + for (i = 0; i < sizeof(pdp_devs)/sizeof(pdp_devs[0]); i++) { + if (pdp_devs[i]) + p += sprintf(p, "pdp%d: %d", i, + netif_queue_stopped(pdp_devs[i])); + } + mutex_unlock(&pdp_mutex); + + return p - buf; +} + +ssize_t sipc_debug_show(struct sipc *si, char *buf) +{ + char *p = buf; + + if (!si || !buf) + return 0; + + p += _debug_show_buf(si, p); + + p += _debug_show_pdp(si, p); + + p += sprintf(p, "\nDebug command -----------\n"); + p += sprintf(p, "R0\tcopy FMT out to in\n"); + p += sprintf(p, "R1\tcopy RAW out to in\n"); + p += sprintf(p, "R2\tcopy RFS out to in\n"); + + return p - buf; +} + +static void test_copy_buf(struct sipc *si, int idx) +{ + struct ringbuf *rb; + + if (idx >= IPCIDX_MAX) + return; + + if (!cp_state) { + printk(KERN_ERR "test_copy_buf : cp_state : %d\n", cp_state); + return; + } + + rb = (struct ringbuf *)&si->rb[idx]; + + memcpy(rb->in_base, rb->out_base, rb->info->size); + rb->cont->in_head = rb->cont->out_head; + rb->cont->in_tail = rb->cont->out_tail; + + rb->cont->out_tail = rb->cont->out_head; + + if (si->queue) + si->queue(MB_DATA(mb_data[idx].mask_send), si->queue_data); +} + +int sipc_debug(struct sipc *si, const char *buf) +{ + int r; + + if (!si || !buf) + return -EINVAL; + + r = _get_auth(); + if (r) + return r; + + switch (buf[0]) { + case 'R': + test_copy_buf(si, buf[1]-'0'); + break; + default: + /* do nothing */ + break; + } + _put_auth(si); + + return 0; +} + +int sipc_whitelist(struct sipc *si, const char *buf, size_t count) +{ + int r; + struct ringbuf *rb; + + printk(KERN_ERR "[%s]\n", __func__); + + if (factory_test_force_sleep) { + printk(KERN_ERR "[%s]factory test\n", __func__); + return count; + } + + if (!si || !buf) + return -EINVAL; + + r = _get_auth(); + if (r) + return r; + + rb = (struct ringbuf *)&si->rb[IPCIDX_FMT]; + + /* write direct full-established-packet to buf */ + r = __write(rb, (u8 *) buf, (unsigned int)count); + + _req_rel_auth(si); + _put_auth(si); + + onedram_write_mailbox(MB_DATA(mb_data[IPCIDX_FMT].mask_send)); + return r; +} + +int sipc_check_skb(struct sipc *si, struct sk_buff *skb) +{ + struct phonethdr *ph; + + ph = pn_hdr(skb); + + if (ph->pn_res == PN_CMD) + return 1; + + return 0; +} + +int sipc_do_cmd(struct sipc *si, struct sk_buff *skb) +{ + if (!si) + return -EINVAL; + + skb_pull(skb, sizeof(struct phonethdr) + 1); + + if (!strncmp("PHONE_ON", skb->data, sizeof("PHONE_ON"))) + return 0; + + return 0; +} + +static int pdp_activate(struct net_device *svndev, int channel) +{ + int idx; + struct net_device *ndev; + + if (!svndev || channel < 1 || channel > PDP_MAX) + return -EINVAL; + + idx = channel - 1; /* start from 0 */ + + mutex_lock(&pdp_mutex); + + if (pdp_devs[idx]) { + mutex_unlock(&pdp_mutex); + return -EBUSY; + } + + ndev = create_pdp(channel, svndev); + if (IS_ERR(ndev)) { + mutex_unlock(&pdp_mutex); + return PTR_ERR(ndev); + } + + pdp_devs[idx] = ndev; + pdp_cnt++; + + mutex_unlock(&pdp_mutex); + + return 0; +} + +static int pdp_deactivate(int channel) +{ + int idx; + + if (channel < 1 || channel > PDP_MAX) + return -EINVAL; + + idx = channel - 1; /* start from 0 */ + + mutex_lock(&pdp_mutex); + + if (!pdp_devs[idx]) { + mutex_unlock(&pdp_mutex); + return -EBUSY; + } + + destroy_pdp(&pdp_devs[idx]); + clear_bit(idx, pdp_bitmap); + pdp_cnt--; + + mutex_unlock(&pdp_mutex); + + return 0; +} + +static ssize_t show_act(struct device *d, + struct device_attribute *attr, char *buf) +{ + int i; + char *p = buf; + + mutex_lock(&pdp_mutex); + + for (i = 0; i < sizeof(pdp_devs)/sizeof(pdp_devs[0]); i++) { + if (pdp_devs[i]) + p += sprintf(p, "%d\n", (i+1)); + } + + mutex_unlock(&pdp_mutex); + + return p - buf; +} + +static ssize_t show_deact(struct device *d, + struct device_attribute *attr, char *buf) +{ + int i; + char *p = buf; + + mutex_lock(&pdp_mutex); + + for (i = 0; i < sizeof(pdp_devs)/sizeof(pdp_devs[0]); i++) { + if (!pdp_devs[i]) + p += sprintf(p, "%d\n", (i+1)); + } + + mutex_unlock(&pdp_mutex); + + return p - buf; +} + +static ssize_t store_act(struct device *d, + struct device_attribute *attr, const char *buf, size_t count) +{ + int r; + unsigned long chan; + struct net_device *ndev = to_net_dev(d); + + if (!ndev) + return count; + + r = strict_strtoul(buf, 10, &chan); + if (!r) + r = pdp_activate(ndev, chan); + + if (r) { + dev_err(&ndev->dev, "Failed to activate pdp " + " channel %lu: %d\n", chan, r); + + /* lock for pdp_activate fail - jongmoon.suh */ + return r; + } + + return count; +} + +static ssize_t store_deact(struct device *d, + struct device_attribute *attr, const char *buf, size_t count) +{ + int r; + unsigned long chan; + struct net_device *ndev = to_net_dev(d); + + if (!ndev) + return count; + + r = strict_strtoul(buf, 10, &chan); + if (!r) + r = pdp_deactivate(chan); + + if (r) + dev_err(&ndev->dev, "Failed to deactivate pdp" + " channel %lu: %d\n", chan, r); + + return count; +} + +static ssize_t show_suspend(struct device *d, + struct device_attribute *attr, char *buf) +{ + int i; + char *p = buf; + + mutex_lock(&pdp_mutex); + + for (i = 0; i < sizeof(pdp_devs)/sizeof(pdp_devs[0]); i++) { + if (test_bit(i, pdp_bitmap)) + p += sprintf(p, "%d\n", (i+1)); + } + + mutex_unlock(&pdp_mutex); + + return p - buf; +} + +static ssize_t store_suspend(struct device *d, + struct device_attribute *attr, const char *buf, size_t count) +{ + int r; + unsigned long chan; + int id; + + r = strict_strtoul(buf, 10, &chan); + if (r) + return count; + + if (chan < 1 || chan > PDP_MAX) + return count; + + id = chan - 1; + + mutex_lock(&pdp_mutex); + + set_bit(id, pdp_bitmap); + + if (pdp_devs[id]) + netif_stop_queue(pdp_devs[id]); + + mutex_unlock(&pdp_mutex); + + return count; +} + +static ssize_t store_resume(struct device *d, + struct device_attribute *attr, const char *buf, size_t count) +{ + int r; + unsigned long chan; + int id; + + r = strict_strtoul(buf, 10, &chan); + if (r) + return count; + + if (chan < 1 || chan > PDP_MAX) + return count; + + id = chan - 1; + + mutex_lock(&pdp_mutex); + + clear_bit(id, pdp_bitmap); + + if (pdp_devs[id]) + netif_wake_queue(pdp_devs[id]); + + mutex_unlock(&pdp_mutex); + + return count; +} + +void sipc_ramdump(struct sipc *si) +{ +#if defined(CONFIG_KERNEL_DEBUG_SEC) + /* silent reset at debug level low */ + if (kernel_sec_get_debug_level() == KERNEL_SEC_DEBUG_LEVEL_LOW) + return; +#endif + _go_dump(si); +} + +#if defined(CONFIG_KERNEL_DEBUG_SEC) +static void _go_dump(struct sipc *si) +{ + int r; + t_kernel_sec_mmu_info mmu_info; + + memset(cp_errmsg, 0, sizeof(cp_errmsg)); + + r = _get_auth(); + if (r) + strcpy(cp_errmsg, ERRMSG); + else { + char *p; + p = (char *)si->map + FATAL_DISP; + memcpy(cp_errmsg, p, sizeof(cp_errmsg)); + } + + printk(KERN_ERR "CP Dump Cause - %s\n", cp_errmsg); + + kernel_sec_set_upload_magic_number(); + kernel_sec_get_mmu_reg_dump(&mmu_info); + kernel_sec_set_upload_cause(UPLOAD_CAUSE_CP_ERROR_FATAL); + kernel_sec_hw_reset(false); + +} +#endif diff --git a/drivers/phone_svn/svnet/sipc4.h b/drivers/phone_svn/svnet/sipc4.h new file mode 100644 index 0000000..40e6714 --- /dev/null +++ b/drivers/phone_svn/svnet/sipc4.h @@ -0,0 +1,267 @@ +/** + * SAMSUNG MODEM IPC header version 4 + * + * Copyright (C) 2010 Samsung Electronics. 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 + * version 2 as published by the Free Software Foundation. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef __SAMSUNG_IPC_V4_H__ +#define __SAMSUNG_IPC_V4_H__ + +/* IPC4.1 NEW PARTITION MAP + * This map is seen by AP side + + 0x00_0000 =========================================== + MAGIC(4)| ACCESS(4) | RESERVED(8) + 0x00_0010 ------------------------------------------- + FMT_OUT_PTR | FMT_IN_PTR + HEAD(4) | TAIL(4) | HEAD(4) | TAIL(4) + 0x00_0020 ------------------------------------------- + RAW_OUT_PTR | RAW_IN_PTR + HEAD(4) | TAIL(4) | HEAD(4) | TAIL(4) + 0x00_0030 ------------------------------------------- + RFS_OUT_PTR | RFS_IN_PTR + HEAD(4) | TAIL(4) | HEAD(4) | TAIL(4) + 0x00_0040 ------------------------------------------- + RESERVED (4KB - 64B) + 0x00_1000 ------------------------------------------- + CP Fatal Display (160B) + 0x00_10A0 ------------------------------------------- + RESERVED (1MB - 4kb-4kb-4kb - 160B) + 0x0F_E000 ------------------------------------------- + Formatted Out (64KB) + 0x10_E000 ------------------------------------------- + Formatted In (64KB) + 0x11_E000 =========================================== + Raw Out (1MB) + 0x21_E000 =========================================== + Raw In (1MB) + 0x31_E000 =========================================== + RemoteFS Out (1MB) + 0x41_E000 =========================================== + RemoteFS In (1MB) + 0x51_E000 =========================================== + + 0xFF_FFFF =========================================== +*/ + +#if defined(CONFIG_PHONE_IPC_SPI) +#define FMT_OUT 0x0FE000 +#define FMT_IN 0x10E000 +#define FMT_SZ 0x10000 /* 65536 bytes */ + +#define RAW_OUT 0x11E000 +#define RAW_IN 0x21E000 +#define RAW_SZ 0x100000 /* 1 MB */ + +#define RFS_OUT 0x31E000 +#define RFS_IN 0x41E000 +#define RFS_SZ 0x100000 /* 1 MB */ +#else +#if defined(CONFIG_PHONE_IPC_HSI) +#define FMT_OUT 0x0FE000 +#define FMT_IN 0x10E000 +#define FMT_SZ 0x10000 /* 65536 bytes */ + +#define RAW_OUT 0x11E000 +#define RAW_IN 0x21E000 +#define RAW_SZ 0x100000 /* 1 MB */ + +#define RFS_OUT 0x31E000 +#define RFS_IN 0x41E000 +#define RFS_SZ 0x100000 /* 1 MB */ +#endif +#endif + +#define FATAL_DISP 0x001000 +#define FATAL_DISP_SZ 0xA0 /* 160 bytes */ + +#define SIPC_MAP_SIZE (RFS_IN + RFS_SZ) +#define SIPC_NAME "IPCv4.1" + +enum { + IPCIDX_FMT = 0, + IPCIDX_RAW, + IPCIDX_RFS, + IPCIDX_MAX +}; + +struct ringbuf_cont { + u32 out_head; + u32 out_tail; + u32 in_head; + u32 in_tail; +}; + +struct sipc_mapped { /* map to the onedram start addr */ + u32 magic; + u32 access; + u32 reserved[2]; + + struct ringbuf_cont rbcont[IPCIDX_MAX]; +}; + + +#define PN_CMD 0x00 +#define PN_FMT 0x01 +#define PN_RFS 0x41 +#define PN_RAW(chid) (0x20 | (chid)) +#define CHID(x) ((x) & 0x1F) + +#define res_to_ridx(x) ((x) >> 5) + +/* + * IPC Frame Format + */ +#define HDLC_START 0x7F +#define HDLC_END 0x7E + +/* Formatted IPC Frame */ +struct fmt_hdr { + u16 len; + u8 control; +} __packed; + +#define FMT_ID_MASK 0x7F /* Information ID mask */ +#define FMT_ID_SIZE 0x80 /* = 128 ( 0 ~ 127 ) */ +#define FMT_MB_MASK 0x80 /* More bit mask */ + +#define FMT_TX_MIN 5 /* ??? */ + +#define is_fmt_last(x) (!((x) & FMT_MB_MASK)) + +/* RAW IPC Frame */ +struct raw_hdr { + u32 len; + u8 channel; + u8 control; +} __packed; + + +/* RFS IPC Frame */ +struct rfs_hdr { + u32 len; + u8 cmd; + u8 id; +} __packed; + +/* + * RAW frame channel ID + */ +enum { + CHID_0 = 0, + CHID_CSD_VT_DATA, + CHID_PDS_PVT_CONTROL, + CHID_PDS_VT_AUDIO, + CHID_PDS_VT_VIDEO, + CHID_5, /* 5 */ + CHID_6, + CHID_CDMA_DATA, + CHID_PCM_DATA, + CHID_TRANSFER_SCREEN, + CHID_PSD_DATA1, /* 10 */ + CHID_PSD_DATA2, + CHID_PSD_DATA3, + CHID_PSD_DATA4, + CHID_PSD_DATA5, + CHID_PSD_DATA6, /* 15 */ + CHID_PSD_DATA7, + CHID_PSD_DATA8, + CHID_PSD_DATA9, + CHID_PSD_DATA10, + CHID_PSD_DATA11, /* 20 */ + CHID_PSD_DATA12, + CHID_PSD_DATA13, + CHID_PSD_DATA14, + CHID_PSD_DATA15, + CHID_BT_DUN, /* 25 */ + CHID_CIQ_BRIDGE_DATA, + CHID_27, + CHID_CP_LOG1, + CHID_CP_LOG2, + CHID_30, /* 30 */ + CHID_31, + CHID_MAX +}; + +#define PDP_MAX 15 +#define PN_PDP_START PN_RAW(CHID_PSD_DATA1) +#define PN_PDP_END PN_RAW(CHID_PSD_DATA15) + +#define PN_PDP(chid) (0x20 | ((chid) + CHID_PSD_DATA1 - 1)) +#define PDP_ID(res) ((res) - PN_PDP_START) + + +/* + * IPC 4.0 Mailbox message definition + */ +#define MB_VALID 0x0080 +#define MB_COMMAND 0x0040 + +#define MB_CMD(x) (MB_VALID | MB_COMMAND | x) +#define MB_DATA(x) (MB_VALID | x) + +/* + * If not command + */ +#define MBD_SEND_FMT 0x0002 +#define MBD_SEND_RAW 0x0001 +#define MBD_SEND_RFS 0x0100 +#define MBD_REQ_ACK_FMT 0x0020 +#define MBD_REQ_ACK_RAW 0x0010 +#define MBD_REQ_ACK_RFS 0x0400 +#define MBD_RES_ACK_FMT 0x0008 +#define MBD_RES_ACK_RAW 0x0004 +#define MBD_RES_ACK_RFS 0x0200 + +/* + * If command + */ +enum { + MBC_NONE = 0, + MBC_INIT_START, + MBC_INIT_END, + MBC_REQ_ACTIVE, + MBC_RES_ACTIVE, + MBC_TIME_SYNC, + MBC_POWER_OFF, + MBC_RESET, + MBC_PHONE_START, + MBC_ERR_DISPLAY, + MBC_POWER_SAVE, + MBC_NV_REBUILD, + MBC_EMER_DOWN, + MBC_REQ_SEM, + MBC_RES_SEM, + MBC_MAX +}; +#define MBC_MASK 0xFF + +/* CMD_INIT_END extended bit */ +#define CP_BOOT_ONLINE 0x0000 +#define CP_BOOT_AIRPLANE 0x1000 +#define AP_OS_ANDROID 0x0100 +#define AP_OS_WINMOBILE 0x0200 +#define AP_OS_LINUX 0x0300 +#define AP_OS_SYMBIAN 0x0400 + +/* CMD_PHONE_START extended bit */ +#define CP_QUALCOMM 0x0100 +#define CP_INFINEON 0x0200 +#define CP_BROADCOM 0x0300 + + +#endif /* __SAMSUNG_IPC_V4_H__ */ |