aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/modem_if/modem_link_device_spi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/modem_if/modem_link_device_spi.c')
-rw-r--r--drivers/misc/modem_if/modem_link_device_spi.c1795
1 files changed, 1795 insertions, 0 deletions
diff --git a/drivers/misc/modem_if/modem_link_device_spi.c b/drivers/misc/modem_if/modem_link_device_spi.c
new file mode 100644
index 0000000..1ea5a2c
--- /dev/null
+++ b/drivers/misc/modem_if/modem_link_device_spi.c
@@ -0,0 +1,1795 @@
+/*
+ * Copyright (C) 2011 Google, Inc.
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/time.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/wakelock.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+#include <linux/if_arp.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/kthread.h>
+
+#include <linux/platform_data/modem.h>
+#include "modem_prj.h"
+#include "modem_link_device_spi.h"
+#include "modem_utils.h"
+
+/* For function which has void parmeter */
+static struct spi_link_device *p_spild;
+static struct spi_device *p_spi;
+
+static void spi_send_work(int spi_sigs)
+{
+ struct spi_work_type *spi_wq = NULL;
+ spi_wq = kmalloc(sizeof(struct spi_work_type), GFP_ATOMIC);
+ spi_wq->signal_code = spi_sigs;
+ INIT_WORK(&spi_wq->work, spi_work);
+ queue_work(p_spild->spi_wq, (struct work_struct *)spi_wq);
+}
+
+static void spi_send_work_front(int spi_sigs)
+{
+ struct spi_work_type *spi_wq = NULL;
+ spi_wq = kmalloc(sizeof(struct spi_work_type), GFP_ATOMIC);
+ spi_wq->signal_code = spi_sigs;
+ INIT_WORK(&spi_wq->work, spi_work);
+ queue_work_front(p_spild->spi_wq, (struct work_struct *)spi_wq);
+}
+
+static irqreturn_t spi_srdy_irq_handler(int irq, void *p_ld)
+{
+ struct link_device *ld = (struct link_device *)p_ld;
+ struct spi_link_device *spild = to_spi_link_device(ld);
+
+ irqreturn_t result = IRQ_HANDLED;
+
+ if (!spild->boot_done)
+ return result;
+
+ if (!wake_lock_active(&spild->spi_wake_lock)
+ && spild->send_modem_spi != 1) {
+ wake_lock(&spild->spi_wake_lock);
+ pr_debug("[SPI] [%s](%d) spi_wakelock locked . spild->spi_state[%d]\n",
+ __func__, __LINE__, (int)spild->spi_state);
+ }
+
+ if (spild->send_modem_spi == 1)
+ up(&spild->srdy_sem);
+
+ /* SRDY interrupt work on SPI_STATE_IDLE state for receive data */
+ if (spild->spi_state == SPI_STATE_IDLE
+ || spild->spi_state == SPI_STATE_RX_TERMINATE
+ || spild->spi_state == SPI_STATE_TX_TERMINATE) {
+ spi_send_work_front(SPI_WORK_RECEIVE);
+
+ return result;
+ }
+
+ return result;
+}
+
+static irqreturn_t spi_subsrdy_irq_handler(int irq, void *p_ld)
+{
+ struct link_device *ld = (struct link_device *)p_ld;
+ struct spi_link_device *spild = to_spi_link_device(ld);
+
+ irqreturn_t result = IRQ_HANDLED;
+
+ /* SRDY interrupt work on SPI_STATE_TX_WAIT state for send data */
+ if (spild->spi_state == SPI_STATE_TX_WAIT)
+ return result;
+
+ pr_debug("%s spild->spi_state[%d]\n",
+ "[SPI] spi_main_subsrdy_rising_handler :",
+ (int)spild->spi_state);
+
+
+ return result;
+}
+
+static int spi_send
+(
+ struct link_device *ld,
+ struct io_device *iod,
+ struct sk_buff *skb
+)
+{
+ struct sk_buff_head *txq;
+ enum dev_format fmt = iod->format;
+
+ u32 data;
+ u32 cmd_ready = 0x12341234;
+ u32 cmd_start = 0x45674567;
+ int ret;
+
+ switch (fmt) {
+ case IPC_FMT:
+ case IPC_RAW:
+ case IPC_RFS:
+ txq = ld->skb_txq[fmt];
+ skb_queue_tail(txq, skb);
+
+ ret = skb->len;
+
+ break;
+
+ case IPC_BOOT:
+ if (get_user(data, (u32 __user *)skb->data))
+ return -EFAULT;
+
+ if (data == cmd_ready) {
+ p_spild->ril_send_modem_img = 1;
+ p_spild->ril_send_cnt = 0;
+ } else if (data == cmd_start) {
+ p_spild->ril_send_modem_img = 0;
+ if (!queue_work(p_spild->ipc_spi_wq,
+ &p_spild->send_modem_w))
+ pr_err("(%d) already exist w-q\n",
+ __LINE__);
+ } else {
+ if (p_spild->ril_send_modem_img) {
+ memcpy((void *)(p_spild->p_virtual_buff
+ + p_spild->ril_send_cnt),
+ skb->data, skb->len);
+ p_spild->ril_send_cnt += skb->len;
+ }
+ }
+ ret = skb->len;
+ dev_kfree_skb_any(skb);
+
+ return ret;
+
+ default:
+ pr_err("[LNK/E] <%s:%s> No TXQ for %s\n",
+ __func__, ld->name, iod->name);
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+
+ spi_send_work(SPI_WORK_SEND);
+
+ return ret;
+}
+
+
+static int spi_register_isr
+(
+ unsigned irq,
+ irqreturn_t (*isr)(int, void*),
+ unsigned long flag,
+ const char *name,
+ struct link_device *ld
+)
+{
+ int ret = 0;
+
+ ret = request_irq(irq, isr, flag, name, ld);
+ if (ret) {
+ pr_err("[LNK/E] <%s> request_irq fail (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = enable_irq_wake(irq);
+ if (ret)
+ pr_err("[LNK/E] <%s> enable_irq_wake fail (%d)\n",
+ __func__, ret);
+
+ pr_debug("[LNK] <%s> IRQ#%d handler is registered.\n", __func__, irq);
+
+ return 0;
+}
+
+void spi_unregister_isr(unsigned irq, void *data)
+{
+ free_irq(irq, data);
+}
+
+int 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_spi, &msg);
+}
+
+static int spi_buff_write
+(
+ struct spi_link_device *spild,
+ int dev_id,
+ const char *buff,
+ unsigned size
+)
+{
+ u32 templength, buf_length;
+ u32 spi_data_mux;
+ u32 spi_packet_free_length;
+
+ u8 *spi_packet;
+ struct spi_data_packet_header *spi_packet_header;
+
+ spi_packet_header = (struct spi_data_packet_header *)spild->buff;
+ spi_packet = (char *)spild->buff;
+
+ spi_packet_free_length = SPI_DATA_PACKET_MAX_PACKET_BODY_SIZE -
+ spi_packet_header->current_data_size;
+
+ buf_length = size + SPI_DATA_MUX_SIZE + SPI_DATA_LENGTH_SIZE;
+
+ /* not enough space in spi packet */
+ if (spi_packet_free_length < buf_length) {
+ spi_packet_header->more = 1;
+ return 0;
+ }
+
+ /* check spi mux type */
+ switch (dev_id) {
+ case IPC_FMT:
+ spi_data_mux = SPI_DATA_MUX_IPC;
+ break;
+
+ case IPC_RAW:
+ spi_data_mux = SPI_DATA_MUX_RAW;
+ break;
+
+ case IPC_RFS:
+ spi_data_mux = SPI_DATA_MUX_RFS;
+ break;
+
+ default:
+ pr_err("%s %s\n",
+ "[SPI] ERROR : spi_buff_write:",
+ "invalid dev_id");
+ return 0;
+ }
+
+ /* copy spi mux field */
+ memcpy(spi_packet + SPI_DATA_PACKET_HEADER_SIZE +
+ spi_packet_header->current_data_size,
+ &spi_data_mux, SPI_DATA_MUX_SIZE);
+ spi_packet_header->current_data_size += SPI_DATA_MUX_SIZE;
+
+ /* copy spi data length field */
+ templength = size-SPI_DATA_BOF_SIZE-SPI_DATA_EOF_SIZE;
+ memcpy(spi_packet + SPI_DATA_PACKET_HEADER_SIZE +
+ spi_packet_header->current_data_size,
+ &templength, SPI_DATA_LENGTH_SIZE);
+ spi_packet_header->current_data_size += SPI_DATA_LENGTH_SIZE;
+
+ /* copy data field */
+ memcpy(spi_packet + SPI_DATA_PACKET_HEADER_SIZE +
+ spi_packet_header->current_data_size,
+ buff, size);
+ spi_packet_header->current_data_size += size;
+
+ return buf_length;
+}
+
+
+int spi_prepare_tx_packet(void)
+{
+ struct link_device *ld;
+ struct spi_link_device *spild;
+ struct sk_buff *skb;
+ int ret;
+ int i;
+
+ spild = p_spild;
+ ld = &spild->ld;
+
+ for (i = 0; i < spild->max_ipc_dev; i++) {
+ while ((skb = skb_dequeue(ld->skb_txq[i]))) {
+ if (ld->mode == LINK_MODE_IPC) {
+ ret = spi_buff_write(spild, i,
+ skb->data, skb->len);
+ if (!ret) {
+ skb_queue_head(ld->skb_txq[i], skb);
+ break;
+ }
+ } else {
+ pr_err("[LNK/E] <%s:%s> "
+ "ld->mode != LINK_MODE_IPC\n",
+ __func__, ld->name);
+ }
+ dev_kfree_skb_any(skb);
+ }
+ }
+
+ return 1;
+}
+
+
+static int spi_start_data_send(void)
+{
+ struct link_device *ld;
+ struct spi_link_device *spild;
+ int i;
+
+ spild = p_spild;
+ ld = &spild->ld;
+
+ for (i = 0; i < spild->max_ipc_dev; i++) {
+ if (skb_queue_len(ld->skb_txq[i]) > 0) {
+ spi_send_work(SPI_WORK_SEND);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void spi_tx_work(void)
+{
+ struct spi_link_device *spild;
+ struct io_device *iod;
+ char *spi_packet_buf;
+ char *spi_sync_buf;
+
+ spild = p_spild;
+
+ /* check SUB SRDY state */
+ if (gpio_get_value(spild->gpio_ipc_sub_srdy) ==
+ SPI_GPIOLEVEL_HIGH) {
+ spi_start_data_send();
+ return;
+ }
+
+ /* check SRDY state */
+ if (gpio_get_value(spild->gpio_ipc_srdy) ==
+ SPI_GPIOLEVEL_HIGH) {
+ spi_start_data_send();
+ return;
+ }
+
+ if (get_console_suspended())
+ return;
+
+ if (spild->spi_state == SPI_STATE_END)
+ return;
+
+ /* change state SPI_STATE_IDLE to SPI_STATE_TX_WAIT */
+ spild->spi_state = SPI_STATE_TX_WAIT;
+ spild->spi_timer_tx_state = SPI_STATE_TIME_START;
+
+ gpio_set_value(spild->gpio_ipc_mrdy, SPI_GPIOLEVEL_HIGH);
+
+ /* Start TX timer */
+ spild->spi_tx_timer.expires =
+ jiffies + ((SPI_TIMER_TX_WAIT_TIME * HZ) / 1000);
+ add_timer(&spild->spi_tx_timer);
+ /* check SUBSRDY state */
+ while (gpio_get_value(spild->gpio_ipc_sub_srdy) ==
+ SPI_GPIOLEVEL_LOW) {
+ if (spild->spi_timer_tx_state == SPI_STATE_TIME_OVER) {
+ pr_err("%s spild->spi_state=[%d]\n",
+ "[spi_tx_work] == spi Fail to receiving SUBSRDY CONF :"
+ , (int)spild->spi_state);
+
+ spild->spi_timer_tx_state = SPI_STATE_TIME_START;
+
+ gpio_set_value(spild->gpio_ipc_mrdy,
+ SPI_GPIOLEVEL_LOW);
+
+ /* change state SPI_STATE_TX_WAIT */
+ /* to SPI_STATE_IDLE */
+ spild->spi_state = SPI_STATE_IDLE;
+ spi_send_work(SPI_WORK_SEND);
+
+ return;
+ }
+ }
+ /* Stop TX timer */
+ del_timer(&spild->spi_tx_timer);
+
+ if (spild->spi_state != SPI_STATE_START
+ && spild->spi_state != SPI_STATE_END
+ && spild->spi_state != SPI_STATE_INIT) {
+ spi_packet_buf = spild->buff;
+ spi_sync_buf = spild->sync_buff;
+
+ gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_HIGH);
+ gpio_set_value(spild->gpio_ipc_mrdy, SPI_GPIOLEVEL_LOW);
+
+ /* change state SPI_STATE_TX_WAIT to
+ SPI_MAIN_STATE_TX_SENDING */
+ spild->spi_state = SPI_STATE_TX_SENDING;
+
+ memset(spi_packet_buf, 0, SPI_MAX_PACKET_SIZE);
+ memset(spi_sync_buf, 0, SPI_MAX_PACKET_SIZE);
+
+ spi_prepare_tx_packet();
+
+ if (spi_tx_rx_sync((void *)spi_packet_buf,
+ (void *)spi_sync_buf,
+ SPI_MAX_PACKET_SIZE) != 0) {
+ /* TODO: save failed packet */
+ /* back data to each queue */
+ pr_err("[SPI] spi_dev_send fail\n");
+
+ /* add cp reset when spi sync fail */
+ iod = link_get_iod_with_format(&spild->ld, IPC_FMT);
+
+ if (iod)
+ iod->modem_state_changed(iod,
+ STATE_CRASH_RESET);
+ }
+
+ spild->spi_state = SPI_STATE_TX_TERMINATE;
+
+ gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_LOW);
+
+ /* change state SPI_MAIN_STATE_TX_SENDING to SPI_STATE_IDLE */
+ spild->spi_state = SPI_STATE_IDLE;
+ spi_start_data_send();
+ } else
+ pr_err("[SPI] ERR : _start_packet_tx:spild->spi_state[%d]",
+ (int)spild->spi_state);
+
+ return;
+}
+
+int spi_buff_read(struct spi_link_device *spild)
+{
+ struct link_device *ld;
+ struct spi_data_packet_header *spi_packet_header;
+ struct sk_buff *skb;
+ char *spi_packet;
+ int dev_id;
+ unsigned int spi_packet_length;
+ unsigned int spi_packet_cur_pos = SPI_DATA_PACKET_HEADER_SIZE;
+
+ unsigned int spi_data_mux;
+ unsigned int spi_data_length;
+ unsigned int data_length;
+ u8 *spi_cur_data;
+ u8 *dst;
+
+ spi_packet = spild->buff;
+ ld = &spild->ld;
+
+ /* check spi packet header */
+ if (*(unsigned int *)spi_packet == 0x00000000
+ || *(unsigned int *)spi_packet == 0xFFFFFFFF) {
+ /* if spi header is invalid, */
+ /* read spi header again with next 4 byte */
+ spi_packet += SPI_DATA_PACKET_HEADER_SIZE;
+ }
+
+ /* read spi packet header */
+ spi_packet_header = (struct spi_data_packet_header *)spi_packet;
+ spi_packet_length = SPI_DATA_PACKET_HEADER_SIZE +
+ spi_packet_header->current_data_size;
+
+
+ do {
+ /* read spi data mux and set current queue */
+ 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:
+ dev_id = IPC_FMT;
+ break;
+
+ case SPI_DATA_MUX_RAW:
+ dev_id = IPC_RAW;
+ break;
+
+ case SPI_DATA_MUX_RFS:
+ dev_id = IPC_RFS;
+ break;
+
+ default:
+ pr_err("%s len[%u], pos[%u]\n",
+ "[SPI] ERROR : spi_buff_read : MUX error",
+ spi_packet_length, spi_packet_cur_pos);
+
+ return spi_packet_cur_pos -
+ SPI_DATA_PACKET_HEADER_SIZE;
+ }
+
+ /* read spi data length */
+ memcpy(&spi_data_length, spi_packet +
+ spi_packet_cur_pos + SPI_DATA_LENGTH_OFFSET,
+ SPI_DATA_LENGTH_SIZE);
+
+ data_length = spi_data_length + SPI_DATA_BOF_SIZE +
+ SPI_DATA_EOF_SIZE;
+
+ spi_data_length += SPI_DATA_HEADER_SIZE;
+
+ /* read data and make spi data */
+ spi_cur_data = spi_packet + spi_packet_cur_pos;
+
+ /* enqueue spi data */
+ skb = alloc_skb(data_length, GFP_ATOMIC);
+ if (!skb) {
+ pr_err("%s %s\n",
+ "[SPI] ERROR : spi_buff_read:",
+ "Can't allocate memory for SPI");
+ return 0;
+ }
+
+ dst = skb_put(skb, data_length);
+
+ memcpy(dst, spi_packet +
+ spi_packet_cur_pos + SPI_DATA_BOF_OFFSET,
+ data_length);
+
+ if (skb)
+ skb_queue_tail(&spild->skb_rxq[dev_id], skb);
+ else
+ pr_err("[LNK/E] <%s:%s> read[%d] fail\n",
+ __func__, ld->name, dev_id);
+
+ /* move spi packet current posision */
+ spi_packet_cur_pos += spi_data_length;
+ } while ((spi_packet_length - 1) > spi_packet_cur_pos);
+
+ return 1;
+}
+
+static void spi_rx_work(void)
+{
+ struct link_device *ld;
+ struct spi_link_device *spild;
+ struct sk_buff *skb;
+ struct io_device *iod;
+ char *spi_packet_buf;
+ char *spi_sync_buf;
+ int i;
+
+ spild = p_spild;
+ ld = &spild->ld;
+ if (!spild)
+ pr_err("[LNK/E] <%s> dpld == NULL\n", __func__);
+
+ if (!wake_lock_active(&spild->spi_wake_lock))
+ return;
+
+ if (gpio_get_value(spild->gpio_ipc_srdy) == SPI_GPIOLEVEL_LOW)
+ return;
+
+ if (get_console_suspended())
+ return;
+
+ if (spild->spi_state == SPI_STATE_END)
+ return;
+
+ spild->spi_state = SPI_STATE_RX_WAIT;
+ spild->spi_timer_rx_state = SPI_STATE_TIME_START;
+
+ gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_HIGH);
+ /* Start TX timer */
+ spild->spi_rx_timer.expires =
+ jiffies + ((SPI_TIMER_RX_WAIT_TIME * HZ) / 1000);
+ add_timer(&spild->spi_rx_timer);
+
+ /* check SUBSRDY state */
+ while (gpio_get_value(spild->gpio_ipc_sub_srdy) ==
+ SPI_GPIOLEVEL_LOW) {
+ if (spild->spi_timer_rx_state == SPI_STATE_TIME_OVER) {
+ pr_err("[SPI] ERROR(Failed MASTER RX:%d ms)",
+ SPI_TIMER_RX_WAIT_TIME);
+
+ spild->spi_timer_rx_state = SPI_STATE_TIME_START;
+
+ gpio_set_value(spild->gpio_ipc_sub_mrdy,
+ SPI_GPIOLEVEL_LOW);
+
+ /* change state SPI_MAIN_STATE_RX_WAIT */
+ /* to SPI_STATE_IDLE */
+ spild->spi_state = SPI_STATE_IDLE;
+
+ return;
+ }
+ }
+ /* Stop TX timer */
+ del_timer(&spild->spi_rx_timer);
+
+ if (spild->spi_state == SPI_STATE_START
+ || spild->spi_state == SPI_STATE_END
+ || spild->spi_state == SPI_STATE_INIT)
+ return;
+
+ spi_packet_buf = spild->buff;
+ spi_sync_buf = spild->sync_buff;
+
+ memset(spi_packet_buf, 0, SPI_MAX_PACKET_SIZE);
+ memset(spi_sync_buf, 0, SPI_MAX_PACKET_SIZE);
+
+ if (spi_tx_rx_sync((void *)spi_sync_buf, (void *)spi_packet_buf,
+ SPI_MAX_PACKET_SIZE) == 0) {
+ /* parsing SPI packet */
+ if (spi_buff_read(spild) > 0) {
+ /* call function for send data to IPC, RAW, RFS */
+ for (i = 0; i < spild->max_ipc_dev; i++) {
+ iod = spild->iod[i];
+ while ((skb = skb_dequeue(&spild->skb_rxq[i]))
+ != NULL) {
+ if (iod->recv(iod, ld, skb->data,
+ skb->len) < 0)
+ pr_err("[LNK/E] <%s:%s> recv fail\n",
+ __func__, ld->name);
+ dev_kfree_skb_any(skb);
+ }
+ }
+ }
+ } else {
+ pr_err("%s %s\n", "[SPI] ERROR : spi_rx_work :",
+ "spi sync failed");
+
+ /* add cp reset when spi sync fail */
+ iod = link_get_iod_with_format(&spild->ld, IPC_FMT);
+
+ if (iod)
+ iod->modem_state_changed(iod,
+ STATE_CRASH_RESET);
+ }
+
+ spild->spi_state = SPI_STATE_RX_TERMINATE;
+
+ gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_LOW);
+
+ /* change state SPI_MAIN_STATE_RX_WAIT to SPI_STATE_IDLE */
+ spild->spi_state = SPI_STATE_IDLE;
+ spi_start_data_send();
+}
+
+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 = 0;
+ unsigned short lowSourceValue, hiSourceValue = 0;
+
+ /* 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 = 0;
+ 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 -ENOMEM;
+
+ *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;
+
+ kfree(dest_ptr);
+ return 0;
+}
+
+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 -EINVAL;
+
+ /* 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 = kmalloc(dest_len, GFP_ATOMIC);
+ /* Memory allocate fail.*/
+ if (dest_ptr == NULL)
+ return -ENOMEM;
+
+ 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) {
+ pr_err("CRC error : 0x%X", crc);
+ kfree(dest_ptr);
+ return -EPERM;
+ }
+
+ memcpy(img->decoded_rx_b, dest_ptr, dest_len - CRC_CHECK_SIZE);
+ img->decoded_rx_size = dest_len - CRC_CHECK_SIZE ;
+
+ kfree(dest_ptr);
+ return 0;
+}
+
+static int spi_send_modem_bin_execute_cmd
+ (u8 *spi_ptr, u32 spi_size, u16 spi_type,
+ u16 spi_crc, struct sprd_image_buf *sprd_img)
+{
+ int i, retval;
+ u16 send_packet_size;
+ u8 *send_packet_data;
+ u16 d1_crc;
+ u16 d2_crc = spi_crc;
+ u16 type = spi_type;
+
+ /* D1 */
+ send_packet_size = spi_size; /* u32 -> u16 */
+ 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;
+
+ if (down_timeout(&p_spild->srdy_sem, 2 * HZ)) {
+ pr_err("(%d) SRDY TimeOUT!!! SRDY : %d, SEM : %d\n",
+ __LINE__,
+ gpio_get_value(p_spild->gpio_modem_bin_srdy),
+ p_spild->srdy_sem.count);
+ goto err;
+ }
+
+ retval = spi_tx_rx_sync
+ (sprd_img->tx_b, sprd_img->rx_b, sprd_img->tx_size);
+ if (retval != 0) {
+ pr_err("(%d) spi sync error : %d\n",
+ __LINE__, retval);
+ goto err;
+ }
+
+ if ((type == 0x0003) || (type == 0x0004)) {
+ pr_err("D2 Skip!!\n");
+ goto ACK;
+ }
+
+ /* D2 */
+ send_packet_data = spi_ptr;
+
+ if (down_timeout(&p_spild->srdy_sem, 2 * HZ)) {
+ pr_err("(%d) SRDY TimeOUT!!! SRDY : %d, SEM : %d\n", __LINE__,
+ gpio_get_value(p_spild->gpio_modem_bin_srdy),
+ p_spild->srdy_sem.count);
+ goto err;
+ }
+
+ retval = spi_tx_rx_sync
+ (send_packet_data, sprd_img->rx_b, send_packet_size);
+ if (retval != 0) {
+ pr_err("(%d) spi sync error : %d\n", __LINE__, retval);
+ goto err;
+ }
+
+ACK:
+
+ if (p_spild->is_cp_reset) {
+ while (!gpio_get_value(p_spild->gpio_modem_bin_srdy))
+ ;
+ } else {
+ if (down_timeout(&p_spild->srdy_sem, 2 * HZ)) {
+ pr_err("(%d) SRDY TimeOUT!!! SRDY : %d, SEM : %d\n",
+ __LINE__,
+ gpio_get_value(p_spild->gpio_modem_bin_srdy),
+ p_spild->srdy_sem.count);
+
+ pr_err("[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]);
+ pr_err("[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(p_spild->gpio_modem_bin_srdy))
+ ;
+ else
+ goto err;
+
+ }
+ }
+
+ memset(sprd_img->tx_b, 0, SPRD_BLOCK_SIZE+10);
+ retval = spi_tx_rx_sync(sprd_img->tx_b, sprd_img->rx_b, 8);
+ if (retval != 0) {
+ pr_err("(%d) spi sync error : %d\n",
+ __LINE__, retval);
+ goto err;
+ }
+
+ memcpy(sprd_img->decoded_rx_b, sprd_img->rx_b, 4);
+
+ 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)) {
+ pr_debug("[SPRD] CP sent ACK");
+ } else {
+ pr_err("Transfer ACK error! srdy_sem = %d\n",
+ p_spild->srdy_sem.count);
+ pr_err("[SPI DUMP] RX(%d) : [ ", sprd_img->rx_size);
+ for (i = 0; i < 15; i++)
+ pr_err("%02x ", *((u8 *)(sprd_img->rx_b + i)));
+ pr_err("]");
+
+ goto err;
+ }
+
+ return retval;
+
+err:
+ return -EINVAL;
+}
+
+static int spi_send_modem_bin_xmit_img
+ (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 = 0;
+ unsigned int fdl1_size = 0;
+ /* No Translate */
+ u16 crc = 0;
+ u16 spi_type = 0;
+
+ unsigned char *spi_ptr;
+ unsigned char *ptr;
+ int i, j = 0;
+
+ u16 sprd_packet_size = SPRD_BLOCK_SIZE;
+
+ sprd_img.tx_b = kmalloc(SPRD_BLOCK_SIZE*2, GFP_ATOMIC);
+ if (!sprd_img.tx_b) {
+ pr_err("(%d) tx_b kmalloc fail.",
+ __LINE__);
+ return -ENOMEM;
+ }
+ 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) {
+ pr_err("(%d) rx_b kmalloc fail.",
+ __LINE__);
+ retval = -ENOMEM;
+ goto err3;
+ }
+ 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) {
+ pr_err("(%d) encoded_tx_b kmalloc fail.",
+ __LINE__);
+ retval = -ENOMEM;
+ goto err2;
+ }
+ 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) {
+ pr_err("(%d) encoded_rx_b kmalloc fail.",
+ __LINE__);
+ retval = -ENOMEM;
+ goto err1;
+ }
+ memset(sprd_img.decoded_rx_b, 0, SPRD_BLOCK_SIZE*2);
+
+ pr_debug("(%d) spi_send_modem_bin_xmit_img type : %d.\n",
+ __LINE__, type);
+ memcpy(&fdl1_size, (void *)(p_spild->p_virtual_buff + 4), 4);
+
+ switch (type) {
+ case MODEM_MAIN:
+ memcpy(&img->address,
+ (void *)(p_spild->p_virtual_buff + 8), 4);
+ memcpy(&img->length,
+ (void *)(p_spild->p_virtual_buff + 12), 4);
+ img->buf = (unsigned char *)
+ (p_spild->p_virtual_buff + 0x30 + fdl1_size);
+ img->offset = img->length + fdl1_size + 0x30;
+ pr_debug("(%d) spi_send_modem_bin_xmit_img save MAIN to img.\n",
+ __LINE__);
+
+ break;
+
+ case MODEM_DSP:
+ memcpy(&img->address,
+ (void *)(p_spild->p_virtual_buff + 16), 4);
+ memcpy(&img->length,
+ (void *)(p_spild->p_virtual_buff + 20), 4);
+ img->buf = (unsigned char *)
+ (p_spild->p_virtual_buff + img->offset);
+ img->offset += img->length;
+ pr_debug("(%d) spi_send_modem_bin_xmit_img save DSP to img.\n",
+ __LINE__);
+
+ break;
+
+ case MODEM_NV:
+ memcpy(&img->address,
+ (void *)(p_spild->p_virtual_buff + 24), 4);
+ memcpy(&img->length,
+ (void *)(p_spild->p_virtual_buff + 28), 4);
+ img->buf = (unsigned char *)
+ (p_spild->p_virtual_buff + img->offset);
+ img->offset += img->length;
+ pr_debug("(%d) spi_send_modem_bin_xmit_img save NV to img.\n",
+ __LINE__);
+
+ break;
+
+ case MODEM_EFS:
+ memcpy(&img->address,
+ (void *)(p_spild->p_virtual_buff + 32), 4);
+ memcpy(&img->length,
+ (void *)(p_spild->p_virtual_buff + 36), 4);
+ img->buf = (unsigned char *)
+ (p_spild->p_virtual_buff + img->offset);
+ img->offset += img->length;
+ pr_debug("(%d) spi_send_modem_bin_xmit_img save EFS to img.\n",
+ __LINE__);
+
+ break;
+ case MODEM_RUN:
+ memset(sprd_img.encoded_tx_b, 0, SPRD_BLOCK_SIZE*2);
+ sprd_img.encoded_tx_size = 0;
+ spi_type = 0x0004;
+ crc = 0;
+
+ spi_ptr = sprd_img.encoded_tx_b;
+ spi_size = sprd_img.encoded_tx_size;
+
+ retval = spi_send_modem_bin_execute_cmd
+ (spi_ptr, spi_size, spi_type, crc, &sprd_img);
+ if (retval < 0) {
+ pr_err("(%d) spi_send_modem_bin_execute_cmd fail : %d",
+ __LINE__, retval);
+ goto err0;
+ }
+ return retval;
+
+ default:
+ pr_err("(%d) spi_send_modem_bin_xmit_img wrong : %d.",
+ __LINE__, type);
+ goto err0;
+ }
+
+ pr_debug("(%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 */
+ 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;
+
+ spi_ptr = sprd_img.encoded_tx_b;
+ spi_size = sprd_img.encoded_tx_size;
+
+ pr_debug("(%d) [Transfer Start, Type = %d, Packet = %d]\n",
+ __LINE__, type, sprd_packet_size);
+ retval = spi_send_modem_bin_execute_cmd
+ (spi_ptr, spi_size, spi_type, crc, &sprd_img);
+ if (retval < 0) {
+ pr_err("(%d) spi_send_modem_bin_execute_cmd fail : %d",
+ __LINE__, retval);
+ goto err0;
+ }
+ 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;
+
+ 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);
+
+ spi_ptr = sprd_img.encoded_tx_b;
+ spi_size = sprd_img.encoded_tx_size;
+
+ retval = spi_send_modem_bin_execute_cmd
+ (spi_ptr, spi_size, spi_type, crc, &sprd_img);
+ if (retval < 0) {
+ pr_err("(%d) spi_send_modem_bin_execute_cmd fail : %d, %d",
+ __LINE__, retval, i);
+ goto err0;
+ }
+
+ send_size += data_size;
+ rest_size -= data_size;
+ ptr += data_size;
+
+ if (!(i % 100))
+ pr_debug("(%d) [%d] 0x%x size done, rest size: 0x%x\n",
+ __LINE__, i, send_size, rest_size);
+ }
+
+ /* Send Transfer End */
+ memset(sprd_img.encoded_tx_b, 0, SPRD_BLOCK_SIZE * 2);
+ sprd_img.encoded_tx_size = 0;
+
+ spi_type = 0x0003;
+ crc = 0;
+
+ spi_ptr = sprd_img.encoded_tx_b;
+ spi_size = sprd_img.encoded_tx_size;
+
+ pr_debug("(%d) [Transfer END]\n", __LINE__);
+ retval = spi_send_modem_bin_execute_cmd
+ (spi_ptr, spi_size, spi_type, crc, &sprd_img);
+ if (retval < 0) {
+ pr_err("(%d) spi_send_modem_bin_execute_cmd fail : %d",
+ __LINE__, retval);
+ goto err0;
+ }
+
+err0:
+ kfree(sprd_img.decoded_rx_b);
+err1:
+ kfree(sprd_img.encoded_tx_b);
+err2:
+ kfree(sprd_img.rx_b);
+err3:
+ kfree(sprd_img.tx_b);
+
+ return retval;
+
+}
+
+static void spi_send_modem_bin(struct work_struct *send_modem_w)
+{
+ int retval;
+ struct image_buf img;
+ unsigned long tick1, tick2 = 0;
+
+ tick1 = jiffies_to_msecs(jiffies);
+
+ retval = spi_send_modem_bin_xmit_img(MODEM_MAIN, &img);
+ if (retval < 0) {
+ pr_err("(%d) spi_send_modem_bin_xmit_img fail : %d",
+ __LINE__, retval);
+ goto err;
+ }
+
+ retval = spi_send_modem_bin_xmit_img(MODEM_DSP, &img);
+ if (retval < 0) {
+ pr_err("(%d) spi_send_modem_bin_xmit_img fail : %d",
+ __LINE__, retval);
+ goto err;
+ }
+
+ retval = spi_send_modem_bin_xmit_img(MODEM_NV, &img);
+ if (retval < 0) {
+ pr_err("(%d) spi_send_modem_bin_xmit_img fail : %d",
+ __LINE__, retval);
+ goto err;
+ }
+
+ retval = spi_send_modem_bin_xmit_img(MODEM_EFS, &img);
+ if (retval < 0) {
+ pr_err("(%d) spi_send_modem_bin_xmit_img fail : %d",
+ __LINE__, retval);
+ goto err;
+ }
+
+ retval = spi_send_modem_bin_xmit_img(MODEM_RUN, &img);
+ if (retval < 0) {
+ pr_err("(%d) spi_send_modem_bin_xmit_img fail : %d",
+ __LINE__, retval);
+ goto err;
+ }
+
+ p_spild->send_modem_spi = 0;
+ p_spild->is_cp_reset = 0;
+ tick2 = jiffies_to_msecs(jiffies);
+ pr_info("Downloading takes %lu msec\n", (tick2-tick1));
+
+ complete_all(&p_spild->ril_init);
+ sprd_boot_done = 1;
+ p_spild->ril_send_cnt = 0;
+
+err:
+ return;
+
+}
+
+static inline int _request_mem(struct ipc_spi *od)
+{
+ if (!p_spild->p_virtual_buff) {
+ pr_err("what\n");
+ od->mmio = vmalloc(od->size);
+ if (!od->mmio) {
+ pr_err("(%d) Failed to vmalloc size : %lu\n", __LINE__,
+ od->size);
+
+ return -EBUSY;
+ } else {
+ pr_err("(%d) vmalloc Done. mmio : 0x%08x\n", __LINE__,
+ (u32)od->mmio);
+ }
+ }
+
+ memset((void *)od->mmio, 0, od->size);
+
+ p_spild->p_virtual_buff = od->mmio;
+
+ return 0;
+}
+
+void spi_tx_timer_callback(unsigned long param)
+{
+ if (p_spild->spi_state == SPI_STATE_TX_WAIT) {
+ p_spild->spi_timer_tx_state = SPI_STATE_TIME_OVER;
+ pr_err("[SPI] spi_tx_timer_callback -timer expires\n");
+ }
+}
+
+void spi_rx_timer_callback(unsigned long param)
+{
+ if (p_spild->spi_state == SPI_STATE_RX_WAIT) {
+ p_spild->spi_timer_rx_state = SPI_STATE_TIME_OVER;
+ pr_err("[SPI] spi_rx_timer_callback -timer expires\n");
+ }
+}
+
+int spi_sema_init(void)
+{
+ pr_info("[SPI] Srdy sema init\n");
+ sema_init(&p_spild->srdy_sem, 0);
+ p_spild->send_modem_spi = 1;
+ return 0;
+}
+
+static void spi_work(struct work_struct *work)
+{
+ int signal_code;
+
+ struct spi_work_type *spi_wq =
+ container_of(work, struct spi_work_type, work);
+
+ signal_code = spi_wq->signal_code;
+
+ if (p_spild->spi_state == SPI_STATE_END
+ || p_spild->spi_state == SPI_STATE_START) {
+ kfree(spi_wq);
+ return;
+ }
+
+ switch (signal_code) {
+ case SPI_WORK_SEND:
+ if (p_spild->spi_state == SPI_STATE_IDLE)
+ spi_tx_work();
+ break;
+ case SPI_WORK_RECEIVE:
+ if (p_spild->spi_state == SPI_STATE_IDLE
+ || p_spild->spi_state == SPI_STATE_TX_TERMINATE
+ || p_spild->spi_state == SPI_STATE_RX_TERMINATE)
+ spi_rx_work();
+ break;
+
+ default:
+ pr_err("[SPI] ERROR(No signal_code in spi_work[%d])\n",
+ signal_code);
+ break;
+ }
+
+ kfree(spi_wq);
+ if (wake_lock_active(&p_spild->spi_wake_lock)) {
+ wake_unlock(&p_spild->spi_wake_lock);
+ pr_err("[SPI] [%s](%d) spi_wakelock unlocked .\n",
+ __func__, __LINE__);
+ }
+}
+
+static int spi_init_ipc(struct spi_link_device *spild)
+{
+ struct link_device *ld = &spild->ld;
+
+ int i;
+
+ /* Make aliases to each IO device */
+ for (i = 0; i < MAX_DEV_FORMAT; i++)
+ spild->iod[i] = link_get_iod_with_format(ld, i);
+
+ spild->iod[IPC_RAW] = spild->iod[IPC_MULTI_RAW];
+
+ /* List up the IO devices connected to each IPC channel */
+ for (i = 0; i < MAX_DEV_FORMAT; i++) {
+ if (spild->iod[i])
+ pr_err("[LNK] <%s:%s> spild->iod[%d]->name = %s\n",
+ __func__, ld->name, i, spild->iod[i]->name);
+ else
+ pr_err("[LNK] <%s:%s> No spild->iod[%d]\n",
+ __func__, ld->name, i);
+ }
+
+ ld->mode = LINK_MODE_IPC;
+
+ return 0;
+}
+
+static int spi_thread(void *data)
+{
+ struct spi_link_device *spild = (struct spi_link_device *)data;
+
+ if (lpcharge == 1) {
+ pr_err("[LPM MODE] spi_thread_exit!\n");
+ return 0;
+ }
+
+ daemonize("spi_thread");
+
+ pr_info("[%s] spi_thread start.\n", __func__);
+ p_spild->boot_done = 1;
+
+ wait_for_completion(&p_spild->ril_init);
+
+ pr_info("[%s] ril_init completed.\n", __func__);
+
+ pr_info("<%s> wait 2 sec... srdy : %d\n",
+ __func__, gpio_get_value(spild->gpio_ipc_srdy));
+ msleep_interruptible(1700);
+
+ while (gpio_get_value(spild->gpio_ipc_srdy))
+ ;
+ pr_info("(%s) cp booting... Done.\n", __func__);
+
+ spi_init_ipc(spild);
+
+ pr_info("[spi_thread] Start IPC Communication. SRDY : %d\n",
+ gpio_get_value(spild->gpio_ipc_srdy));
+
+ /* CP booting is already completed, just set submrdy to high */
+ if (gpio_get_value(spild->gpio_ipc_sub_srdy) == SPI_GPIOLEVEL_HIGH) {
+ gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_HIGH);
+ pr_err("[spi_thread] CP booting is already completed\n");
+ }
+ /* CP booting is not completed.
+ set submrdy to high and wait until subsrdy is high */
+ else {
+ pr_err("[spi_thread] CP booting is not completed. wait...\n");
+
+ gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_HIGH);
+ do {
+ msleep_interruptible(5);
+ } while (gpio_get_value(spild->gpio_ipc_sub_srdy) ==
+ SPI_GPIOLEVEL_LOW);
+
+ pr_err("[spi_thread] CP booting is done...\n");
+ }
+
+ if (p_spild->spi_is_restart)
+ msleep_interruptible(100);
+ else
+ msleep_interruptible(30);
+
+ gpio_set_value(spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_LOW);
+
+ pr_info("(%s) spi sync done.\n", __func__);
+
+ spild->spi_state = SPI_STATE_IDLE;
+ p_spild->spi_is_restart = 0;
+
+ return 0;
+}
+
+static int spi_probe(struct spi_device *spi)
+{
+ int ret;
+
+ p_spi = spi;
+ p_spi->mode = SPI_MODE_1;
+ p_spi->bits_per_word = 32;
+
+ ret = spi_setup(p_spi);
+ if (ret != 0) {
+
+ pr_err("[%s] spi setup error\n", __func__);
+
+ return ret;
+ }
+
+ pr_info("[%s] spi probe done\n", __func__);
+
+ return ret;
+}
+
+static int spi_remove(struct spi_device *spi)
+{
+ return 0;
+}
+
+static struct spi_driver spi_driver = {
+ .probe = spi_probe,
+ .remove = __devexit_p(spi_remove),
+ .driver = {
+ .name = "modem_if_spi",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int spi_link_init(void)
+{
+ int ret;
+ struct ipc_spi *od;
+ struct task_struct *th;
+ struct link_device *ld = &p_spild->ld;
+
+ p_spild->gpio_modem_bin_srdy = p_spild->gpio_ipc_srdy;
+
+ pr_info("(%d) gpio_mrdy : %d, gpio_srdy : %d(%d)\n", __LINE__,
+ p_spild->gpio_ipc_mrdy, p_spild->gpio_modem_bin_srdy,
+ gpio_get_value(p_spild->gpio_ipc_srdy));
+
+ od = kzalloc(sizeof(struct ipc_spi), GFP_KERNEL);
+ if (!od) {
+ pr_err("(%d) failed to allocate device\n", __LINE__);
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ od->base = 0;
+ od->size = SZ_16M; /* 16M */
+ if (p_spild->p_virtual_buff)
+ od->mmio = p_spild->p_virtual_buff;
+ ret = _request_mem(od);
+ if (ret)
+ goto err;
+
+ init_completion(&p_spild->ril_init);
+ sema_init(&p_spild->srdy_sem, 0);
+
+ INIT_WORK(&p_spild->send_modem_w,
+ spi_send_modem_bin);
+
+ wake_lock_init(&p_spild->spi_wake_lock,
+ WAKE_LOCK_SUSPEND,
+ "samsung-spiwakelock");
+
+ /* Register SPI Srdy interrupt handler */
+ ret = spi_register_isr(gpio_to_irq(p_spild->gpio_ipc_srdy),
+ spi_srdy_irq_handler, IRQF_TRIGGER_RISING,
+ "spi_srdy_rising", ld);
+
+ if (ret)
+ goto err;
+
+ /* Register SPI SubSrdy interrupt handler */
+ ret = spi_register_isr(gpio_to_irq(p_spild->gpio_ipc_sub_srdy),
+ spi_subsrdy_irq_handler, IRQF_TRIGGER_RISING,
+ "spi_subsrdy_rising", ld);
+
+ if (ret)
+ goto err;
+
+ th = kthread_create(spi_thread, (void *)p_spild, "spi_thread");
+ if (IS_ERR(th)) {
+ pr_err("kernel_thread() failed\n");
+ goto err;
+ }
+ wake_up_process(th);
+
+ pr_info("[%s] Done\n", __func__);
+ return 0;
+
+err:
+ kfree(od);
+ return ret;
+}
+
+/*=====================================
+* Description spi restart for CP slient reset
+=====================================*/
+void spi_set_restart(void)
+{
+ struct link_device *ld = &p_spild->ld;
+
+ pr_info("[SPI] spi_set_restart(spi_main_state=[%d])\n",
+ (int)p_spild->spi_state);
+
+ gpio_set_value(p_spild->gpio_ipc_sub_mrdy, SPI_GPIOLEVEL_LOW);
+
+ p_spild->spi_state = SPI_STATE_END;
+ p_spild->spi_is_restart = 1;
+
+ /* Flush SPI work queue */
+ flush_workqueue(p_spild->spi_wq);
+
+ /* Unregister SPI Srdy interrupt handler */
+ spi_unregister_isr(gpio_to_irq(p_spild->gpio_ipc_srdy), ld);
+
+ /* Unregister SPI SubSrdy interrupt handler */
+ spi_unregister_isr(gpio_to_irq(p_spild->gpio_ipc_sub_srdy), ld);
+
+ if (wake_lock_active(&p_spild->spi_wake_lock)) {
+ wake_unlock(&p_spild->spi_wake_lock);
+ pr_err("[SPI] [%s](%d) spi_wakelock unlocked.\n",
+ __func__, __LINE__);
+ }
+ wake_lock_destroy(&p_spild->spi_wake_lock);
+}
+
+
+int spi_thread_restart(void)
+{
+ int retval;
+
+ p_spild->send_modem_spi = 1;
+ p_spild->is_cp_reset = 1;
+ sprd_boot_done = 0;
+
+ pr_info("[modem_if_spi] spi_thread_restart\n");
+
+ spi_set_restart();
+
+ /* Create SPI device */
+ retval = spi_link_init();
+ if (retval)
+ goto exit;
+
+ return 0;
+
+exit:
+ return retval;
+}
+EXPORT_SYMBOL(spi_thread_restart);
+
+struct link_device *spi_create_link_device(struct platform_device *pdev)
+{
+ struct spi_link_device *spild = NULL;
+ struct link_device *ld;
+ struct modem_data *pdata;
+
+ int ret;
+ int i;
+
+ /* Get the platform data */
+ pdata = (struct modem_data *)pdev->dev.platform_data;
+ if (!pdata) {
+ pr_err("[LNK/E] <%s> pdata == NULL\n", __func__);
+ goto err;
+ }
+
+ pr_info("[LNK] <%s> link device = %s\n", __func__, pdata->link_name);
+ pr_info("[LNK] <%s> modem = %s\n", __func__, pdata->name);
+
+ /* Alloc SPI link device structure */
+ p_spild = spild = kzalloc(sizeof(struct spi_link_device), GFP_KERNEL);
+ if (!spild) {
+ pr_err("[LNK/E] <%s> Failed to kzalloc()\n", __func__);
+ goto err;
+ }
+ ld = &spild->ld;
+
+ /* Extract modem data and SPI control data from the platform data */
+ ld->mdm_data = pdata;
+ ld->name = "spi";
+
+ if (ld->aligned)
+ pr_err("[LNK] <%s> Aligned access is required!!!\n", __func__);
+
+
+ ld->send = spi_send;
+
+ INIT_LIST_HEAD(&ld->list);
+
+ skb_queue_head_init(&ld->sk_fmt_tx_q);
+ skb_queue_head_init(&ld->sk_raw_tx_q);
+ skb_queue_head_init(&ld->sk_rfs_tx_q);
+ ld->skb_txq[IPC_FMT] = &ld->sk_fmt_tx_q;
+ ld->skb_txq[IPC_RAW] = &ld->sk_raw_tx_q;
+ ld->skb_txq[IPC_RFS] = &ld->sk_rfs_tx_q;
+
+ spild->spi_wq = create_singlethread_workqueue("spi_wq");
+ if (!spild->spi_wq) {
+ pr_err("[LNK/E] <%s> Fail to create workqueue for spi_wq\n",
+ __func__);
+ goto err;
+ }
+
+ spild->spi_state = SPI_STATE_END;
+ spild->max_ipc_dev = IPC_RFS+1; /* FMT, RAW, RFS */
+
+ for (i = 0; i < spild->max_ipc_dev; i++)
+ skb_queue_head_init(&spild->skb_rxq[i]);
+
+ /* Prepare a clean buffer for SPI access */
+ spild->buff = kzalloc(SPI_MAX_PACKET_SIZE, GFP_KERNEL);
+ spild->sync_buff = kzalloc(SPI_MAX_PACKET_SIZE, GFP_KERNEL);
+
+ memset(spild->buff , 0, SPI_MAX_PACKET_SIZE);
+ memset(spild->sync_buff , 0, SPI_MAX_PACKET_SIZE);
+
+ if (!spild->buff) {
+ pr_err("[LNK/E] <%s> Failed to alloc spild->buff\n", __func__);
+ goto err;
+ }
+
+ /* Initialize SPI pin value */
+ spild->gpio_ipc_mrdy = pdata->gpio_ipc_mrdy;
+ spild->gpio_ipc_srdy = pdata->gpio_ipc_srdy;
+ spild->gpio_ipc_sub_mrdy = pdata->gpio_ipc_sub_mrdy;
+ spild->gpio_ipc_sub_srdy = pdata->gpio_ipc_sub_srdy;
+
+ spild->gpio_ap_cp_int1 = pdata->gpio_ap_cp_int1;
+ spild->gpio_ap_cp_int2 = pdata->gpio_ap_cp_int2;
+
+ /* Create SPI Timer */
+ init_timer(&spild->spi_tx_timer);
+
+ spild->spi_tx_timer.expires =
+ jiffies + ((SPI_TIMER_TX_WAIT_TIME * HZ) / 1000);
+
+ spild->spi_tx_timer.data = 0;
+ spild->spi_tx_timer.function = spi_tx_timer_callback;
+
+ init_timer(&spild->spi_rx_timer);
+
+ spild->spi_rx_timer.expires =
+ jiffies + ((SPI_TIMER_RX_WAIT_TIME * HZ) / 1000);
+
+ spild->spi_rx_timer.data = 0;
+ spild->spi_rx_timer.function = spi_rx_timer_callback;
+
+ ret = spi_register_driver(&spi_driver);
+ if (ret < 0) {
+ pr_err("spi_register_driver() fail : %d\n", ret);
+ goto err;
+ }
+
+ /* Create SPI device */
+ ret = spi_link_init();
+ if (ret)
+ goto err;
+
+ /* creat work queue thread */
+ p_spild->ipc_spi_wq = create_singlethread_workqueue("ipc_spi_wq");
+
+ if (!p_spild->ipc_spi_wq) {
+ pr_err("[%s] get workqueue thread fail\n",
+ __func__);
+
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ return ld;
+
+err:
+ kfree(spild->buff);
+ kfree(spild);
+ return NULL;
+}